From 51538b3af3a5a3dcecec00928dca562e10bc7a0c Mon Sep 17 00:00:00 2001 From: OrsellGaming <34631691+OrsellGaming@users.noreply.github.com> Date: Sat, 9 Nov 2024 23:44:15 -0800 Subject: [PATCH] Initial WorkshopStopper9000 upload. It's pretty much complete with this one commit, hopefully. --- .github/workflows/ms-build.yml | 34 + .gitignore | 2 + README.md | 39 +- common/ConfigManager.h | 116 + common/GameUI/IGameConsole.h | 41 + common/GameUI/IGameUI.h | 103 + common/IRunGameEngine.h | 81 + common/IVGuiModule.h | 64 + common/IVGuiModuleLoader.h | 33 + common/ServerBrowser/IServerBrowser.h | 45 + common/blackbox_helper.cpp | 48 + common/blackbox_helper.h | 11 + common/compiledcaptionswap.cpp | 103 + common/debug_dll_check.cpp | 16 + common/ifilesystemopendialog.h | 51 + common/language.cpp | 116 + common/language.h | 43 + common/matchmaking/mm_helpers.h | 92 + common/protocol.h | 176 + common/qlimits.h | 35 + common/randoverride.cpp | 27 + common/studiobyteswap.cpp | 2636 ++++++++++ common/studiobyteswap.h | 40 + common/vgui_surfacelib/ifontsurface.h | 107 + common/xbox/xboxstubs.h | 409 ++ engine/host.h | 221 + engine/iblackbox.h | 19 + main.cpp | 185 + main.hpp | 70 + minhook/include/MinHook.h | 186 + public/.editorconfig | 69 + public/ATI_Compress.h | 144 + public/BitmapFontFile.h | 54 + public/Color.h | 124 + public/Friends/AddOns/AddOnMessages.h | 56 + public/Friends/AddOns/AddOnTypes.h | 18 + public/Friends/AddOns/ISteamAddOn.h | 69 + public/Friends/IFriendsNET.h | 41 + public/Friends/IFriendsUser.h | 62 + public/IGameUIFuncs.h | 30 + public/IHammer.h | 69 + public/OfflineMode.h | 29 + public/PlayerState.h | 63 + public/ScratchPadUtils.cpp | 467 ++ public/ScratchPadUtils.h | 163 + .../isoundemittersystembase.h | 283 ++ public/SoundParametersInternal.cpp | 589 +++ public/UnicodeFileHelpers.cpp | 240 + public/UnicodeFileHelpers.h | 28 + public/UtlCachedFileData.h | 1084 ++++ public/VGuiMatSurface/IMatSystemSurface.h | 113 + public/VGuiMatSurface/IMatSystemSurfaceV5.h | 88 + public/XUnzip.cpp | 4494 +++++++++++++++++ public/XZip.cpp | 3013 +++++++++++ public/appframework/AppFramework.h | 228 + public/appframework/IAppSystem.h | 121 + public/appframework/IAppSystemGroup.h | 299 ++ public/appframework/VguiMatSysApp.h | 51 + public/appframework/tier2app.h | 89 + public/appframework/tier3app.h | 121 + public/arraystack.h | 69 + public/avi/iavi.h | 124 + public/avi/ibik.h | 139 + public/basehandle.h | 191 + public/bitmap/bitmap.h | 107 + public/bitmap/colorformat.h | 146 + public/bitmap/cubemap.h | 68 + public/bitmap/imageformat.h | 783 +++ public/bitmap/psd.h | 105 + public/bitmap/psheet.h | 136 + public/bitmap/tgaloader.h | 45 + public/bitmap/tgawriter.h | 35 + public/bittools.h | 46 + public/bitvec.h | 1417 ++++++ public/blockingudpsocket.cpp | 153 + public/blockingudpsocket.h | 39 + public/bone_accessor.cpp | 37 + public/bone_accessor.h | 131 + public/bone_setup.h | 525 ++ public/branchingsingleton.h | 48 + public/bspfile.h | 1285 +++++ public/bspflags.h | 156 + public/bsptreedata.cpp | 352 ++ public/bsptreedata.h | 141 + public/builddisp.cpp | 3153 ++++++++++++ public/builddisp.h | 1557 ++++++ public/captioncompiler.h | 77 + public/cdll_int.h | 893 ++++ public/chunkfile.cpp | 985 ++++ public/chunkfile.h | 197 + public/client_class.cpp | 16 + public/client_class.h | 180 + public/client_render_handle.h | 31 + public/client_textmessage.h | 33 + public/clientstats.h | 198 + public/closedcaptions.cpp | 58 + public/closedcaptions.h | 70 + public/cmodel.h | 133 + public/collisionutils.cpp | 3406 +++++++++++++ public/collisionutils.h | 491 ++ public/con_nprint.h | 31 + public/const.h | 429 ++ public/coordsize.h | 99 + public/crtmemdebug.cpp | 44 + public/crtmemdebug.h | 33 + public/datacache/idatacache.h | 567 +++ public/datacache/imdlcache.h | 258 + public/datacache/iprecachesystem.h | 51 + public/datacache/iresourceaccesscontrol.h | 50 + public/datamap.h | 500 ++ public/datamodel/attributeflags.h | 32 + public/datamodel/dmattribute.h | 440 ++ public/datamodel/dmattributetypes.h | 351 ++ public/datamodel/dmattributevar.h | 1518 ++++++ public/datamodel/dmehandle.h | 263 + public/datamodel/dmelement.h | 1210 +++++ public/datamodel/dmelementfactoryhelper.h | 248 + public/datamodel/dmelementhandle.h | 47 + public/datamodel/dmvar.h | 95 + public/datamodel/idatamodel.h | 942 ++++ public/disp_common.cpp | 1630 ++++++ public/disp_common.h | 263 + public/disp_powerinfo.cpp | 580 +++ public/disp_powerinfo.h | 215 + public/disp_tesselate.h | 206 + public/disp_vertindex.h | 150 + public/dispcoll.cpp | 1994 ++++++++ public/dispcoll.h | 249 + public/dispcoll_common.cpp | 1540 ++++++ public/dispcoll_common.h | 456 ++ public/dlight.h | 94 + public/dmserializers/idmserializers.h | 47 + public/dmxloader/dmxattribute.h | 252 + public/dmxloader/dmxelement.h | 518 ++ public/dmxloader/dmxloader.h | 72 + public/dt_common.h | 213 + public/dt_recv.cpp | 530 ++ public/dt_recv.h | 584 +++ public/dt_send.cpp | 910 ++++ public/dt_send.h | 843 ++++ public/dt_shared.cpp | 113 + public/dt_shared.h | 103 + public/dt_utlvector_common.cpp | 69 + public/dt_utlvector_common.h | 85 + public/dt_utlvector_recv.cpp | 157 + public/dt_utlvector_recv.h | 60 + public/dt_utlvector_send.cpp | 220 + public/dt_utlvector_send.h | 57 + public/edict.h | 437 ++ public/editor_sendcommand.cpp | 228 + public/editor_sendcommand.h | 52 + public/eiface.h | 764 +++ public/engine/IClientLeafSystem.h | 54 + public/engine/ICollideable.h | 82 + public/engine/IEngineSound.h | 128 + public/engine/IEngineTrace.h | 233 + public/engine/IStaticPropMgr.h | 103 + public/engine/SndInfo.h | 66 + public/engine/iserverplugin.h | 166 + public/engine/ishadowmgr.h | 227 + public/engine/ivdebugoverlay.h | 68 + public/engine/ivmodelinfo.h | 149 + public/engine/ivmodelrender.h | 216 + public/engine/thinktracecounter.h | 49 + public/engine/view_sharedv1.h | 94 + public/engine_hlds_api.h | 57 + public/entitydefs.h | 21 + public/event_flags.h | 28 + public/filesystem.h | 871 ++++ public/filesystem_helpers.cpp | 138 + public/filesystem_helpers.h | 22 + public/filesystem_init.cpp | 1564 ++++++ public/filesystem_init.h | 223 + public/filesystem_passthru.h | 280 + public/game/client/IGameClientExports.h | 38 + public/game/client/iclientrendertargets.h | 41 + public/game/client/iviewport.h | 68 + public/game/server/ientityinfo.h | 150 + public/game/server/igameinfo.h | 45 + public/game/server/iplayerinfo.h | 198 + public/game/server/pluginvariant.h | 165 + public/gamebspfile.h | 292 ++ public/gametrace.h | 111 + public/globalvars_base.h | 109 + public/iachievementmgr.h | 84 + public/ibsppack.h | 45 + public/iclient.h | 105 + public/iclientalphaproperty.h | 89 + public/icliententity.h | 50 + public/icliententitylist.h | 67 + public/iclientnetworkable.h | 105 + public/iclientrenderable.h | 329 ++ public/iclientthinkable.h | 41 + public/iclientunknown.h | 45 + public/icvar.h | 213 + public/idedicatedexports.h | 29 + public/iefx.h | 68 + public/ienginevgui.h | 67 + public/ifilelist.h | 28 + public/igameevents.h | 199 + public/igameresources.h | 44 + public/ihandleentity.h | 35 + public/ihltv.h | 48 + public/ihltvdirector.h | 37 + public/ilaunchabledll.h | 28 + public/imapoverview.h | 58 + public/inetchannel.h | 113 + public/inetchannelinfo.h | 80 + public/inetmessage.h | 48 + public/inetmsghandler.h | 186 + public/inetwork.h | 52 + public/inputsystem/AnalogCode.h | 44 + public/inputsystem/ButtonCode.h | 345 ++ public/inputsystem/InputEnums.h | 109 + public/inputsystem/iinputstacksystem.h | 76 + public/inputsystem/iinputsystem.h | 178 + public/interfaces/interfaces.h | 287 ++ public/interpolatortypes.cpp | 506 ++ public/interpolatortypes.h | 102 + public/iprediction.h | 68 + public/irecipientfilter.h | 29 + public/iregistry.h | 48 + public/ireplay.h | 41 + public/ireplaydirector.h | 37 + public/isaverestore.h | 392 ++ public/iscratchpad3d.h | 326 ++ public/iserver.h | 70 + public/iserverentity.h | 42 + public/iservernetworkable.h | 114 + public/iserverunknown.h | 35 + public/ishadercompiledll.h | 26 + public/isoundcombiner.h | 40 + public/ispatialpartition.h | 211 + public/ispsharedmemory.h | 27 + public/isqlwrapper.h | 116 + public/istudiorender.h | 425 ++ public/ivoiceserver.h | 34 + public/ivoicetweak.h | 42 + public/ivraddll.h | 99 + public/ivrenderview.h | 334 ++ public/ivtex.h | 31 + public/ixboxsystem.h | 444 ++ public/jigglebones.cpp | 643 +++ public/jigglebones.h | 68 + public/jpeglib/jconfig.h | 45 + public/jpeglib/jmorecfg.h | 374 ++ public/jpeglib/jpeglib.h | 1113 ++++ public/kevvaluescompiler.cpp | 435 ++ public/keyframe/keyframe.cpp | 501 ++ public/keyframe/keyframe.h | 42 + public/keyvaluescompiler.h | 188 + public/list.h | 352 ++ public/loadcmdline.cpp | 123 + public/loadcmdline.h | 27 + public/localflexcontroller.h | 30 + public/localize/ilocalize.h | 130 + public/lumpfiles.cpp | 27 + public/lumpfiles.h | 20 + public/map_utils.cpp | 48 + public/map_utils.h | 25 + public/matchmaking/idatacenter.h | 19 + public/matchmaking/imatchasync.h | 62 + public/matchmaking/imatchevents.h | 259 + public/matchmaking/imatchextensions.h | 28 + public/matchmaking/imatchframework.h | 103 + public/matchmaking/imatchnetworkmsg.h | 49 + public/matchmaking/imatchsystem.h | 28 + public/matchmaking/imatchtitle.h | 162 + public/matchmaking/imatchvoice.h | 38 + public/matchmaking/iplayer.h | 77 + public/matchmaking/iplayermanager.h | 46 + public/matchmaking/isearchmanager.h | 62 + public/matchmaking/iservermanager.h | 56 + public/matchmaking/swarm/imatchext_swarm.h | 83 + public/materialsystem/IColorCorrection.h | 75 + public/materialsystem/IShader.h | 218 + public/materialsystem/MaterialSystemUtil.cpp | 253 + public/materialsystem/MaterialSystemUtil.h | 103 + public/materialsystem/deformations.h | 58 + public/materialsystem/hardwareverts.h | 86 + public/materialsystem/idebugtextureinfo.h | 68 + public/materialsystem/imaterial.h | 663 +++ public/materialsystem/imaterialproxy.h | 32 + public/materialsystem/imaterialproxyfactory.h | 26 + public/materialsystem/imaterialsystem.h | 1792 +++++++ .../imaterialsystemhardwareconfig.h | 180 + public/materialsystem/imaterialsystemstub.h | 31 + public/materialsystem/imaterialvar.h | 245 + public/materialsystem/imesh.h | 3945 +++++++++++++++ public/materialsystem/imorph.h | 251 + public/materialsystem/ishaderapi.h | 43 + public/materialsystem/itexture.h | 141 + public/materialsystem/ivballoctracker.h | 34 + public/materialsystem/materialsystem_config.h | 200 + public/materialsystem/meshreader.h | 268 + public/materialsystem/shader_vcs_version.h | 75 + public/mathlib/IceKey.H | 66 + public/mathlib/anorms.h | 25 + public/mathlib/bumpvects.h | 37 + public/mathlib/camera.h | 302 ++ public/mathlib/compressed_3d_unitvec.h | 284 ++ public/mathlib/compressed_light_cube.h | 24 + public/mathlib/compressed_vector.h | 608 +++ public/mathlib/halton.h | 70 + public/mathlib/lightdesc.h | 185 + public/mathlib/math_pfns.h | 110 + public/mathlib/mathlib.h | 2246 ++++++++ public/mathlib/noise.h | 35 + public/mathlib/polyhedron.h | 73 + public/mathlib/quantize.h | 141 + public/mathlib/simdvectormatrix.h | 142 + public/mathlib/spherical_geometry.h | 73 + public/mathlib/ssemath.h | 4123 +++++++++++++++ public/mathlib/ssequaternion.h | 1096 ++++ public/mathlib/vector.h | 2501 +++++++++ public/mathlib/vector2d.h | 670 +++ public/mathlib/vector4d.h | 747 +++ public/mathlib/vertexcolor.h | 121 + public/mathlib/vmatrix.h | 941 ++++ public/mathlib/vplane.h | 182 + public/matsys_controls/QCGenerator.h | 155 + public/matsys_controls/assetpicker.h | 51 + public/matsys_controls/baseassetpicker.h | 243 + public/matsys_controls/colorpickerpanel.h | 162 + public/matsys_controls/curveeditorpanel.h | 68 + public/matsys_controls/gamefiletreeview.h | 81 + public/matsys_controls/manipulator.h | 128 + public/matsys_controls/matsyscontrols.h | 72 + public/matsys_controls/mdlpanel.h | 121 + public/matsys_controls/mdlpicker.h | 139 + public/matsys_controls/mdlsequencepicker.h | 116 + public/matsys_controls/picker.h | 133 + public/matsys_controls/potterywheelpanel.h | 160 + .../matsys_controls/proceduraltexturepanel.h | 92 + public/matsys_controls/sequencepicker.h | 178 + public/matsys_controls/tgapreviewpanel.h | 41 + public/matsys_controls/vmtpanel.h | 91 + public/matsys_controls/vmtpicker.h | 116 + public/matsys_controls/vmtpreviewpanel.h | 101 + public/matsys_controls/vtfpicker.h | 59 + public/matsys_controls/vtfpreviewpanel.h | 68 + public/maya/IMayaVGui.h | 67 + public/maya/VsMayaDmx.h | 30 + public/maya/VsMayaMPxFactory.h | 1120 ++++ public/maya/VsVGuiWindow.h | 188 + public/maya/valveMaya.h | 544 ++ public/maya/valveMaya/HyperShadeUtil.h | 46 + public/maya/valveMaya/Undo.h | 327 ++ public/mdllib/mdllib.h | 149 + public/measure_section.cpp | 271 + public/measure_section.h | 127 + public/minmax.h | 19 + public/missionchooser/iasw_map_builder.h | 67 + public/missionchooser/iasw_mission_chooser.h | 135 + .../iasw_mission_chooser_source.h | 78 + public/missionchooser/iasw_random_missions.h | 94 + public/missionchooser/iasw_spawn_selection.h | 81 + public/model_types.h | 48 + public/modes.h | 21 + public/mouthinfo.h | 201 + public/networkstringtabledefs.h | 87 + public/networkvar.cpp | 15 + public/networkvar.h | 821 +++ public/nmatrix.h | 332 ++ public/ntree.h | 316 ++ public/nvector.h | 203 + public/nvtc.h | 92 + public/optimize.h | 264 + public/overlaytext.h | 58 + public/p4lib/ip4.h | 191 + public/particles/particles.h | 3280 ++++++++++++ public/phonemeconverter.cpp | 229 + public/phonemeconverter.h | 28 + public/phonemeextractor/phonemeextractor.h | 50 + public/phyfile.h | 23 + public/pixelwriter.h | 875 ++++ public/posedebugger.cpp | 666 +++ public/posedebugger.h | 38 + public/r_efx.h | 38 + public/raytrace.h | 420 ++ public/registry.cpp | 420 ++ public/renderparm.h | 77 + .../responserules/response_host_interface.h | 61 + public/responserules/response_types.h | 418 ++ public/responserules/rr_speechconcept.h | 57 + public/rope_physics.cpp | 152 + public/rope_physics.h | 117 + public/rope_shared.h | 70 + public/savegame_version.h | 15 + public/saverestoretypes.h | 550 ++ public/scenefilecache/ISceneFileCache.h | 44 + public/scenefilecache/SceneImageFile.h | 69 + public/scratchpad3d.cpp | 640 +++ public/scratchpad3d.h | 224 + public/sentence.cpp | 1830 +++++++ public/sentence.h | 303 ++ public/server_class.cpp | 16 + public/server_class.h | 148 + public/shaderapi/IShaderDevice.h | 386 ++ public/shaderapi/commandbuffer.h | 105 + public/shaderapi/ishaderapi.h | 767 +++ public/shaderapi/ishaderdynamic.h | 291 ++ public/shaderapi/ishadershadow.h | 207 + public/shaderapi/ishaderutil.h | 136 + public/shaderapi/shareddefs.h | 106 + public/shaderlib/BaseShader.h | 285 ++ public/shaderlib/ShaderDLL.h | 49 + public/shaderlib/cshader.h | 439 ++ public/shake.h | 91 + public/shattersurfacetypes.h | 21 + public/simple_physics.cpp | 72 + public/simple_physics.h | 81 + public/smooth_average.h | 221 + public/soundchars.h | 71 + public/soundcombiner.cpp | 823 +++ public/soundflags.h | 162 + public/soundinfo.h | 388 ++ public/soundsystem/isoundsystem.h | 70 + public/soundsystem/snd_audio_source.h | 101 + public/soundsystem/snd_device.h | 105 + public/stdstring.h | 86 + public/steam/isteamapps.h | 49 + public/steam/isteamclient.h | 159 + public/steam/isteamfriends.h | 274 + public/steam/isteamgameserver.h | 226 + public/steam/isteamgameserverstats.h | 93 + public/steam/isteammasterserverupdater.h | 103 + public/steam/isteammatchmaking.h | 634 +++ public/steam/isteamnetworking.h | 278 + public/steam/isteamremotestorage.h | 45 + public/steam/isteamuser.h | 233 + public/steam/isteamuserstats.h | 331 ++ public/steam/isteamutils.h | 182 + public/steam/matchmakingtypes.h | 230 + public/steam/steam_api.h | 467 ++ public/steam/steam_gameserver.h | 143 + public/steam/steamclientpublic.h | 917 ++++ public/steam/steamtypes.h | 123 + public/string_t.h | 113 + public/stringregistry.cpp | 145 + public/stringregistry.h | 57 + public/studio.cpp | 2160 ++++++++ public/studio.h | 3363 ++++++++++++ public/studio_generic_io.cpp | 156 + public/studio_virtualmodel.cpp | 567 +++ public/surfinfo.h | 31 + public/texture_group_names.h | 43 + public/tier0/EventMasks.h | 480 ++ public/tier0/EventModes.h | 1787 +++++++ public/tier0/IOCTLCodes.h | 29 + public/tier0/K8PerformanceCounters.h | 2040 ++++++++ public/tier0/P4PerformanceCounters.h | 322 ++ public/tier0/P5P6PerformanceCounters.h | 225 + public/tier0/PMELib.h | 212 + public/tier0/afxmem_override.cpp | 460 ++ public/tier0/annotations.h | 116 + public/tier0/basetypes.h | 557 ++ public/tier0/cache_hints.h | 26 + public/tier0/commonmacros.h | 149 + public/tier0/dbg.h | 643 +++ public/tier0/dbgflag.h | 64 + public/tier0/fasttimer.h | 600 +++ public/tier0/ia32detect.h | 383 ++ public/tier0/icommandline.h | 65 + public/tier0/l2cache.h | 47 + public/tier0/logging.h | 746 +++ public/tier0/mem.h | 53 + public/tier0/memalloc.h | 574 +++ public/tier0/memdbgoff.h | 25 + public/tier0/memdbgon.h | 263 + public/tier0/memoverride.cpp | 1385 +++++ public/tier0/minidump.h | 82 + public/tier0/miniprofiler.h | 256 + public/tier0/platform.h | 1604 ++++++ public/tier0/platwindow.h | 82 + public/tier0/pmc360.h | 73 + public/tier0/progressbar.h | 23 + public/tier0/protected_things.h | 266 + public/tier0/systeminformation.h | 58 + public/tier0/testthread.h | 60 + public/tier0/threadtools.h | 1770 +++++++ public/tier0/tslist.h | 808 +++ public/tier0/validator.h | 73 + public/tier0/valobject.h | 72 + public/tier0/valve_off.h | 33 + public/tier0/valve_on.h | 37 + public/tier0/vprof.h | 1398 +++++ public/tier0/wchartypes.h | 101 + public/tier0/win32consoleio.h | 32 + public/tier0/xbox_codeline_defines.h | 16 + public/tier1/CommandBuffer.h | 160 + public/tier1/KeyValues.h | 474 ++ public/tier1/UtlSortVector.h | 311 ++ public/tier1/UtlStringMap.h | 110 + public/tier1/bitbuf.h | 1501 ++++++ public/tier1/byteswap.h | 268 + public/tier1/callqueue.h | 233 + public/tier1/characterset.h | 43 + public/tier1/checksum_crc.h | 31 + public/tier1/checksum_md5.h | 33 + public/tier1/convar.h | 1011 ++++ public/tier1/convar_serverbounded.h | 53 + public/tier1/datamanager.h | 296 ++ public/tier1/delegates.h | 99 + public/tier1/diff.h | 29 + public/tier1/exprevaluator.h | 74 + public/tier1/fmtstr.h | 128 + public/tier1/functors.h | 903 ++++ public/tier1/generichash.h | 116 + public/tier1/iconvar.h | 121 + public/tier1/interface.h | 220 + public/tier1/interpolatedvar.cpp | 28 + public/tier1/interpolatedvar.h | 1562 ++++++ public/tier1/lerp_functions.h | 170 + public/tier1/lzmaDecoder.h | 44 + public/tier1/lzss.h | 71 + public/tier1/mempool.h | 628 +++ public/tier1/memstack.h | 345 ++ public/tier1/netadr.h | 73 + public/tier1/processor_detect.h | 12 + public/tier1/rangecheckedvar.h | 113 + public/tier1/refcount.h | 385 ++ public/tier1/smartptr.h | 279 + public/tier1/stringpool.h | 102 + public/tier1/strtools.h | 565 +++ public/tier1/tier1.h | 85 + public/tier1/timeutils.h | 170 + public/tier1/tokenset.h | 122 + public/tier1/uniqueid.h | 56 + public/tier1/utlbidirectionalset.h | 394 ++ public/tier1/utlblockmemory.h | 349 ++ public/tier1/utlbuffer.h | 1359 +++++ public/tier1/utlbufferutil.h | 197 + public/tier1/utldict.h | 324 ++ public/tier1/utlenvelope.h | 241 + public/tier1/utlfixedmemory.h | 354 ++ public/tier1/utlflags.h | 124 + public/tier1/utlhandletable.h | 586 +++ public/tier1/utlhash.h | 1299 +++++ public/tier1/utlhashdict.h | 342 ++ public/tier1/utlintrusivelist.h | 1064 ++++ public/tier1/utllinkedlist.h | 1070 ++++ public/tier1/utlmap.h | 203 + public/tier1/utlmemory.h | 1084 ++++ public/tier1/utlmultilist.h | 769 +++ public/tier1/utlntree.h | 624 +++ public/tier1/utlobjectreference.h | 284 ++ public/tier1/utlpriorityqueue.h | 212 + public/tier1/utlqueue.h | 176 + public/tier1/utlrbtree.h | 1568 ++++++ public/tier1/utlsoacontainer.h | 895 ++++ public/tier1/utlstack.h | 331 ++ public/tier1/utlstring.h | 222 + public/tier1/utlsymbol.h | 321 ++ public/tier1/utlsymbollarge.h | 494 ++ public/tier1/utltshash.h | 625 +++ public/tier1/utlvector.h | 1209 +++++ public/tier2/beamsegdraw.h | 198 + public/tier2/fileutils.h | 312 ++ public/tier2/interval.h | 20 + public/tier2/keybindings.h | 42 + public/tier2/meshutils.h | 26 + public/tier2/p4helpers.h | 138 + public/tier2/renderutils.h | 72 + public/tier2/resourceprecacher.h | 195 + public/tier2/riff.h | 202 + public/tier2/soundutils.h | 31 + public/tier2/tier2.h | 121 + public/tier2/tier2_logging.h | 72 + public/tier2/tier2dm.h | 72 + public/tier2/tokenreader.h | 83 + public/tier2/utlstreambuffer.h | 72 + public/tier2/vconfig.h | 27 + public/tier3/choreoutils.h | 39 + public/tier3/mdlutils.h | 114 + public/tier3/scenetokenprocessor.h | 18 + public/tier3/tier3.h | 58 + public/tier3/tier3dm.h | 45 + public/toolframework/iclientenginetools.h | 59 + public/toolframework/ienginetool.h | 246 + public/toolframework/iserverenginetools.h | 56 + public/toolframework/itooldictionary.h | 39 + public/toolframework/itoolentity.h | 236 + public/toolframework/itoolframework.h | 321 ++ public/toolframework/itoolsystem.h | 159 + public/toolframework/toolframework.cpp | 40 + public/tools/bonelist.cpp | 72 + public/tools/bonelist.h | 61 + public/trace.h | 69 + public/unitlib/unitlib.h | 269 + public/vallocator.cpp | 36 + public/vallocator.h | 84 + public/vaudio/ivaudio.h | 63 + public/vcollide.h | 27 + public/vcollide_parse.h | 91 + public/vgui/Cursor.h | 45 + public/vgui/Dar.h | 134 + public/vgui/IBorder.h | 63 + public/vgui/IClientPanel.h | 98 + public/vgui/IHTML.h | 86 + public/vgui/IImage.h | 75 + public/vgui/IInput.h | 193 + public/vgui/IInputInternal.h | 90 + public/vgui/ILocalize.h | 30 + public/vgui/IPanel.h | 148 + public/vgui/IScheme.h | 140 + public/vgui/ISurface.h | 330 ++ public/vgui/ISurfaceV30.h | 375 ++ public/vgui/ISystem.h | 132 + public/vgui/IVGui.h | 113 + public/vgui/IVguiMatInfo.h | 25 + public/vgui/IVguiMatInfoVar.h | 22 + public/vgui/KeyCode.h | 24 + public/vgui/MouseCode.h | 23 + public/vgui/Point.h | 61 + public/vgui/VGUI.h | 68 + public/vgui/ischemesurface.h | 84 + public/vgui_controls/AnalogBar.h | 109 + public/vgui_controls/AnimatingImagePanel.h | 66 + public/vgui_controls/AnimationController.h | 267 + public/vgui_controls/BitmapImagePanel.h | 57 + public/vgui_controls/BuildGroup.h | 185 + public/vgui_controls/BuildModeDialog.h | 136 + public/vgui_controls/Button.h | 229 + public/vgui_controls/CheckButton.h | 74 + public/vgui_controls/CheckButtonList.h | 74 + public/vgui_controls/CircularProgressBar.h | 64 + public/vgui_controls/ComboBox.h | 148 + public/vgui_controls/ControllerMap.h | 48 + public/vgui_controls/Controls.h | 160 + public/vgui_controls/CvarToggleCheckButton.h | 191 + public/vgui_controls/DialogManager.h | 196 + public/vgui_controls/DirectorySelectDialog.h | 96 + public/vgui_controls/Divider.h | 38 + public/vgui_controls/EditablePanel.h | 164 + public/vgui_controls/ExpandButton.h | 61 + public/vgui_controls/FileOpenDialog.h | 197 + public/vgui_controls/FileOpenStateMachine.h | 172 + public/vgui_controls/FocusNavGroup.h | 61 + public/vgui_controls/Frame.h | 265 + public/vgui_controls/GraphPanel.h | 81 + public/vgui_controls/HTML.h | 138 + public/vgui_controls/Image.h | 84 + public/vgui_controls/ImageList.h | 56 + public/vgui_controls/ImagePanel.h | 88 + public/vgui_controls/InputDialog.h | 152 + public/vgui_controls/KeyBindingHelpDialog.h | 63 + public/vgui_controls/KeyBindingMap.h | 224 + public/vgui_controls/KeyBoardEditorDialog.h | 138 + public/vgui_controls/Label.h | 237 + public/vgui_controls/ListPanel.h | 374 ++ public/vgui_controls/ListViewPanel.h | 121 + public/vgui_controls/Menu.h | 351 ++ public/vgui_controls/MenuBar.h | 54 + public/vgui_controls/MenuButton.h | 82 + public/vgui_controls/MenuItem.h | 138 + public/vgui_controls/MessageBox.h | 98 + public/vgui_controls/MessageDialog.h | 154 + public/vgui_controls/MessageMap.h | 393 ++ public/vgui_controls/PHandle.h | 91 + public/vgui_controls/Panel.h | 1080 ++++ public/vgui_controls/PanelAnimationVar.h | 161 + public/vgui_controls/PanelListPanel.h | 143 + public/vgui_controls/PerforceFileExplorer.h | 67 + public/vgui_controls/PerforceFileList.h | 114 + public/vgui_controls/ProgressBar.h | 119 + public/vgui_controls/ProgressBox.h | 99 + public/vgui_controls/PropertyDialog.h | 84 + public/vgui_controls/PropertyPage.h | 58 + public/vgui_controls/PropertySheet.h | 208 + public/vgui_controls/QueryBox.h | 61 + public/vgui_controls/RadioButton.h | 75 + public/vgui_controls/RichText.h | 290 ++ public/vgui_controls/RotatingProgressBar.h | 67 + public/vgui_controls/ScalableImagePanel.h | 54 + public/vgui_controls/ScrollBar.h | 131 + public/vgui_controls/ScrollBarSlider.h | 113 + .../vgui_controls/ScrollableEditablePanel.h | 54 + public/vgui_controls/SectionedListPanel.h | 245 + public/vgui_controls/Slider.h | 121 + public/vgui_controls/Splitter.h | 98 + public/vgui_controls/TextEntry.h | 405 ++ public/vgui_controls/TextImage.h | 119 + public/vgui_controls/ToggleButton.h | 54 + public/vgui_controls/ToolWindow.h | 108 + public/vgui_controls/Tooltip.h | 59 + public/vgui_controls/TreeView.h | 231 + public/vgui_controls/TreeViewListControl.h | 130 + public/vgui_controls/URLLabel.h | 49 + public/vgui_controls/WizardPanel.h | 127 + public/vgui_controls/WizardSubPanel.h | 91 + public/vgui_controls/consoledialog.h | 169 + public/vgui_controls/pch_vgui_controls.h | 115 + public/vgui_controls/perforcefilelistframe.h | 151 + public/vgui_controls/savedocumentquery.h | 37 + public/vgui_controls/subrectimage.h | 51 + public/vgui_controls/tgaimagepanel.h | 59 + public/vgui_controls/vgui_controls.cpp | 57 + public/videocfg/videocfg.h | 103 + public/view_shared.h | 164 + public/vphysics/collision_set.h | 20 + public/vphysics/constraints.h | 353 ++ public/vphysics/friction.h | 51 + public/vphysics/object_hash.h | 29 + public/vphysics/performance.h | 40 + public/vphysics/player_controller.h | 47 + public/vphysics/stats.h | 40 + public/vphysics/vehicles.h | 250 + public/vphysics/virtualmesh.h | 46 + public/vphysics_interface.h | 1191 +++++ public/vpklib/packedstore.h | 203 + public/vscript/ivscript.h | 1412 ++++++ public/vscript/vscript_templates.h | 414 ++ public/vstdlib/IKeyValuesSystem.h | 56 + public/vstdlib/cvar.h | 25 + public/vstdlib/iprocessutils.h | 131 + public/vstdlib/jobthread.h | 1439 ++++++ public/vstdlib/pch_vstdlib.h | 51 + public/vstdlib/random.h | 111 + public/vstdlib/vcover.h | 125 + public/vstdlib/vstdlib.h | 33 + public/vtf/vtf.h | 618 +++ public/wadtypes.h | 99 + public/winlite.h | 31 + public/worldsize.h | 43 + public/xwvfile.h | 85 + public/zip/XUnzip.h | 424 ++ public/zip/XZip.h | 342 ++ public/zip_uncompressed.h | 113 + public/zip_utils.cpp | 1741 +++++++ public/zip_utils.h | 87 + scanner.cpp | 431 ++ scanner.hpp | 60 + workshopstopper9000.sln | 22 + workshopstopper9000.vcxproj | 96 + workshopstopper9000.vcxproj.user | 9 + workshopstopper9000.vdf | 4 + 737 files changed, 207641 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ms-build.yml create mode 100644 common/ConfigManager.h create mode 100644 common/GameUI/IGameConsole.h create mode 100644 common/GameUI/IGameUI.h create mode 100644 common/IRunGameEngine.h create mode 100644 common/IVGuiModule.h create mode 100644 common/IVGuiModuleLoader.h create mode 100644 common/ServerBrowser/IServerBrowser.h create mode 100644 common/blackbox_helper.cpp create mode 100644 common/blackbox_helper.h create mode 100644 common/compiledcaptionswap.cpp create mode 100644 common/debug_dll_check.cpp create mode 100644 common/ifilesystemopendialog.h create mode 100644 common/language.cpp create mode 100644 common/language.h create mode 100644 common/matchmaking/mm_helpers.h create mode 100644 common/protocol.h create mode 100644 common/qlimits.h create mode 100644 common/randoverride.cpp create mode 100644 common/studiobyteswap.cpp create mode 100644 common/studiobyteswap.h create mode 100644 common/vgui_surfacelib/ifontsurface.h create mode 100644 common/xbox/xboxstubs.h create mode 100644 engine/host.h create mode 100644 engine/iblackbox.h create mode 100644 main.cpp create mode 100644 main.hpp create mode 100644 minhook/include/MinHook.h create mode 100644 public/.editorconfig create mode 100644 public/ATI_Compress.h create mode 100644 public/BitmapFontFile.h create mode 100644 public/Color.h create mode 100644 public/Friends/AddOns/AddOnMessages.h create mode 100644 public/Friends/AddOns/AddOnTypes.h create mode 100644 public/Friends/AddOns/ISteamAddOn.h create mode 100644 public/Friends/IFriendsNET.h create mode 100644 public/Friends/IFriendsUser.h create mode 100644 public/IGameUIFuncs.h create mode 100644 public/IHammer.h create mode 100644 public/OfflineMode.h create mode 100644 public/PlayerState.h create mode 100644 public/ScratchPadUtils.cpp create mode 100644 public/ScratchPadUtils.h create mode 100644 public/SoundEmitterSystem/isoundemittersystembase.h create mode 100644 public/SoundParametersInternal.cpp create mode 100644 public/UnicodeFileHelpers.cpp create mode 100644 public/UnicodeFileHelpers.h create mode 100644 public/UtlCachedFileData.h create mode 100644 public/VGuiMatSurface/IMatSystemSurface.h create mode 100644 public/VGuiMatSurface/IMatSystemSurfaceV5.h create mode 100644 public/XUnzip.cpp create mode 100644 public/XZip.cpp create mode 100644 public/appframework/AppFramework.h create mode 100644 public/appframework/IAppSystem.h create mode 100644 public/appframework/IAppSystemGroup.h create mode 100644 public/appframework/VguiMatSysApp.h create mode 100644 public/appframework/tier2app.h create mode 100644 public/appframework/tier3app.h create mode 100644 public/arraystack.h create mode 100644 public/avi/iavi.h create mode 100644 public/avi/ibik.h create mode 100644 public/basehandle.h create mode 100644 public/bitmap/bitmap.h create mode 100644 public/bitmap/colorformat.h create mode 100644 public/bitmap/cubemap.h create mode 100644 public/bitmap/imageformat.h create mode 100644 public/bitmap/psd.h create mode 100644 public/bitmap/psheet.h create mode 100644 public/bitmap/tgaloader.h create mode 100644 public/bitmap/tgawriter.h create mode 100644 public/bittools.h create mode 100644 public/bitvec.h create mode 100644 public/blockingudpsocket.cpp create mode 100644 public/blockingudpsocket.h create mode 100644 public/bone_accessor.cpp create mode 100644 public/bone_accessor.h create mode 100644 public/bone_setup.h create mode 100644 public/branchingsingleton.h create mode 100644 public/bspfile.h create mode 100644 public/bspflags.h create mode 100644 public/bsptreedata.cpp create mode 100644 public/bsptreedata.h create mode 100644 public/builddisp.cpp create mode 100644 public/builddisp.h create mode 100644 public/captioncompiler.h create mode 100644 public/cdll_int.h create mode 100644 public/chunkfile.cpp create mode 100644 public/chunkfile.h create mode 100644 public/client_class.cpp create mode 100644 public/client_class.h create mode 100644 public/client_render_handle.h create mode 100644 public/client_textmessage.h create mode 100644 public/clientstats.h create mode 100644 public/closedcaptions.cpp create mode 100644 public/closedcaptions.h create mode 100644 public/cmodel.h create mode 100644 public/collisionutils.cpp create mode 100644 public/collisionutils.h create mode 100644 public/con_nprint.h create mode 100644 public/const.h create mode 100644 public/coordsize.h create mode 100644 public/crtmemdebug.cpp create mode 100644 public/crtmemdebug.h create mode 100644 public/datacache/idatacache.h create mode 100644 public/datacache/imdlcache.h create mode 100644 public/datacache/iprecachesystem.h create mode 100644 public/datacache/iresourceaccesscontrol.h create mode 100644 public/datamap.h create mode 100644 public/datamodel/attributeflags.h create mode 100644 public/datamodel/dmattribute.h create mode 100644 public/datamodel/dmattributetypes.h create mode 100644 public/datamodel/dmattributevar.h create mode 100644 public/datamodel/dmehandle.h create mode 100644 public/datamodel/dmelement.h create mode 100644 public/datamodel/dmelementfactoryhelper.h create mode 100644 public/datamodel/dmelementhandle.h create mode 100644 public/datamodel/dmvar.h create mode 100644 public/datamodel/idatamodel.h create mode 100644 public/disp_common.cpp create mode 100644 public/disp_common.h create mode 100644 public/disp_powerinfo.cpp create mode 100644 public/disp_powerinfo.h create mode 100644 public/disp_tesselate.h create mode 100644 public/disp_vertindex.h create mode 100644 public/dispcoll.cpp create mode 100644 public/dispcoll.h create mode 100644 public/dispcoll_common.cpp create mode 100644 public/dispcoll_common.h create mode 100644 public/dlight.h create mode 100644 public/dmserializers/idmserializers.h create mode 100644 public/dmxloader/dmxattribute.h create mode 100644 public/dmxloader/dmxelement.h create mode 100644 public/dmxloader/dmxloader.h create mode 100644 public/dt_common.h create mode 100644 public/dt_recv.cpp create mode 100644 public/dt_recv.h create mode 100644 public/dt_send.cpp create mode 100644 public/dt_send.h create mode 100644 public/dt_shared.cpp create mode 100644 public/dt_shared.h create mode 100644 public/dt_utlvector_common.cpp create mode 100644 public/dt_utlvector_common.h create mode 100644 public/dt_utlvector_recv.cpp create mode 100644 public/dt_utlvector_recv.h create mode 100644 public/dt_utlvector_send.cpp create mode 100644 public/dt_utlvector_send.h create mode 100644 public/edict.h create mode 100644 public/editor_sendcommand.cpp create mode 100644 public/editor_sendcommand.h create mode 100644 public/eiface.h create mode 100644 public/engine/IClientLeafSystem.h create mode 100644 public/engine/ICollideable.h create mode 100644 public/engine/IEngineSound.h create mode 100644 public/engine/IEngineTrace.h create mode 100644 public/engine/IStaticPropMgr.h create mode 100644 public/engine/SndInfo.h create mode 100644 public/engine/iserverplugin.h create mode 100644 public/engine/ishadowmgr.h create mode 100644 public/engine/ivdebugoverlay.h create mode 100644 public/engine/ivmodelinfo.h create mode 100644 public/engine/ivmodelrender.h create mode 100644 public/engine/thinktracecounter.h create mode 100644 public/engine/view_sharedv1.h create mode 100644 public/engine_hlds_api.h create mode 100644 public/entitydefs.h create mode 100644 public/event_flags.h create mode 100644 public/filesystem.h create mode 100644 public/filesystem_helpers.cpp create mode 100644 public/filesystem_helpers.h create mode 100644 public/filesystem_init.cpp create mode 100644 public/filesystem_init.h create mode 100644 public/filesystem_passthru.h create mode 100644 public/game/client/IGameClientExports.h create mode 100644 public/game/client/iclientrendertargets.h create mode 100644 public/game/client/iviewport.h create mode 100644 public/game/server/ientityinfo.h create mode 100644 public/game/server/igameinfo.h create mode 100644 public/game/server/iplayerinfo.h create mode 100644 public/game/server/pluginvariant.h create mode 100644 public/gamebspfile.h create mode 100644 public/gametrace.h create mode 100644 public/globalvars_base.h create mode 100644 public/iachievementmgr.h create mode 100644 public/ibsppack.h create mode 100644 public/iclient.h create mode 100644 public/iclientalphaproperty.h create mode 100644 public/icliententity.h create mode 100644 public/icliententitylist.h create mode 100644 public/iclientnetworkable.h create mode 100644 public/iclientrenderable.h create mode 100644 public/iclientthinkable.h create mode 100644 public/iclientunknown.h create mode 100644 public/icvar.h create mode 100644 public/idedicatedexports.h create mode 100644 public/iefx.h create mode 100644 public/ienginevgui.h create mode 100644 public/ifilelist.h create mode 100644 public/igameevents.h create mode 100644 public/igameresources.h create mode 100644 public/ihandleentity.h create mode 100644 public/ihltv.h create mode 100644 public/ihltvdirector.h create mode 100644 public/ilaunchabledll.h create mode 100644 public/imapoverview.h create mode 100644 public/inetchannel.h create mode 100644 public/inetchannelinfo.h create mode 100644 public/inetmessage.h create mode 100644 public/inetmsghandler.h create mode 100644 public/inetwork.h create mode 100644 public/inputsystem/AnalogCode.h create mode 100644 public/inputsystem/ButtonCode.h create mode 100644 public/inputsystem/InputEnums.h create mode 100644 public/inputsystem/iinputstacksystem.h create mode 100644 public/inputsystem/iinputsystem.h create mode 100644 public/interfaces/interfaces.h create mode 100644 public/interpolatortypes.cpp create mode 100644 public/interpolatortypes.h create mode 100644 public/iprediction.h create mode 100644 public/irecipientfilter.h create mode 100644 public/iregistry.h create mode 100644 public/ireplay.h create mode 100644 public/ireplaydirector.h create mode 100644 public/isaverestore.h create mode 100644 public/iscratchpad3d.h create mode 100644 public/iserver.h create mode 100644 public/iserverentity.h create mode 100644 public/iservernetworkable.h create mode 100644 public/iserverunknown.h create mode 100644 public/ishadercompiledll.h create mode 100644 public/isoundcombiner.h create mode 100644 public/ispatialpartition.h create mode 100644 public/ispsharedmemory.h create mode 100644 public/isqlwrapper.h create mode 100644 public/istudiorender.h create mode 100644 public/ivoiceserver.h create mode 100644 public/ivoicetweak.h create mode 100644 public/ivraddll.h create mode 100644 public/ivrenderview.h create mode 100644 public/ivtex.h create mode 100644 public/ixboxsystem.h create mode 100644 public/jigglebones.cpp create mode 100644 public/jigglebones.h create mode 100644 public/jpeglib/jconfig.h create mode 100644 public/jpeglib/jmorecfg.h create mode 100644 public/jpeglib/jpeglib.h create mode 100644 public/kevvaluescompiler.cpp create mode 100644 public/keyframe/keyframe.cpp create mode 100644 public/keyframe/keyframe.h create mode 100644 public/keyvaluescompiler.h create mode 100644 public/list.h create mode 100644 public/loadcmdline.cpp create mode 100644 public/loadcmdline.h create mode 100644 public/localflexcontroller.h create mode 100644 public/localize/ilocalize.h create mode 100644 public/lumpfiles.cpp create mode 100644 public/lumpfiles.h create mode 100644 public/map_utils.cpp create mode 100644 public/map_utils.h create mode 100644 public/matchmaking/idatacenter.h create mode 100644 public/matchmaking/imatchasync.h create mode 100644 public/matchmaking/imatchevents.h create mode 100644 public/matchmaking/imatchextensions.h create mode 100644 public/matchmaking/imatchframework.h create mode 100644 public/matchmaking/imatchnetworkmsg.h create mode 100644 public/matchmaking/imatchsystem.h create mode 100644 public/matchmaking/imatchtitle.h create mode 100644 public/matchmaking/imatchvoice.h create mode 100644 public/matchmaking/iplayer.h create mode 100644 public/matchmaking/iplayermanager.h create mode 100644 public/matchmaking/isearchmanager.h create mode 100644 public/matchmaking/iservermanager.h create mode 100644 public/matchmaking/swarm/imatchext_swarm.h create mode 100644 public/materialsystem/IColorCorrection.h create mode 100644 public/materialsystem/IShader.h create mode 100644 public/materialsystem/MaterialSystemUtil.cpp create mode 100644 public/materialsystem/MaterialSystemUtil.h create mode 100644 public/materialsystem/deformations.h create mode 100644 public/materialsystem/hardwareverts.h create mode 100644 public/materialsystem/idebugtextureinfo.h create mode 100644 public/materialsystem/imaterial.h create mode 100644 public/materialsystem/imaterialproxy.h create mode 100644 public/materialsystem/imaterialproxyfactory.h create mode 100644 public/materialsystem/imaterialsystem.h create mode 100644 public/materialsystem/imaterialsystemhardwareconfig.h create mode 100644 public/materialsystem/imaterialsystemstub.h create mode 100644 public/materialsystem/imaterialvar.h create mode 100644 public/materialsystem/imesh.h create mode 100644 public/materialsystem/imorph.h create mode 100644 public/materialsystem/ishaderapi.h create mode 100644 public/materialsystem/itexture.h create mode 100644 public/materialsystem/ivballoctracker.h create mode 100644 public/materialsystem/materialsystem_config.h create mode 100644 public/materialsystem/meshreader.h create mode 100644 public/materialsystem/shader_vcs_version.h create mode 100644 public/mathlib/IceKey.H create mode 100644 public/mathlib/anorms.h create mode 100644 public/mathlib/bumpvects.h create mode 100644 public/mathlib/camera.h create mode 100644 public/mathlib/compressed_3d_unitvec.h create mode 100644 public/mathlib/compressed_light_cube.h create mode 100644 public/mathlib/compressed_vector.h create mode 100644 public/mathlib/halton.h create mode 100644 public/mathlib/lightdesc.h create mode 100644 public/mathlib/math_pfns.h create mode 100644 public/mathlib/mathlib.h create mode 100644 public/mathlib/noise.h create mode 100644 public/mathlib/polyhedron.h create mode 100644 public/mathlib/quantize.h create mode 100644 public/mathlib/simdvectormatrix.h create mode 100644 public/mathlib/spherical_geometry.h create mode 100644 public/mathlib/ssemath.h create mode 100644 public/mathlib/ssequaternion.h create mode 100644 public/mathlib/vector.h create mode 100644 public/mathlib/vector2d.h create mode 100644 public/mathlib/vector4d.h create mode 100644 public/mathlib/vertexcolor.h create mode 100644 public/mathlib/vmatrix.h create mode 100644 public/mathlib/vplane.h create mode 100644 public/matsys_controls/QCGenerator.h create mode 100644 public/matsys_controls/assetpicker.h create mode 100644 public/matsys_controls/baseassetpicker.h create mode 100644 public/matsys_controls/colorpickerpanel.h create mode 100644 public/matsys_controls/curveeditorpanel.h create mode 100644 public/matsys_controls/gamefiletreeview.h create mode 100644 public/matsys_controls/manipulator.h create mode 100644 public/matsys_controls/matsyscontrols.h create mode 100644 public/matsys_controls/mdlpanel.h create mode 100644 public/matsys_controls/mdlpicker.h create mode 100644 public/matsys_controls/mdlsequencepicker.h create mode 100644 public/matsys_controls/picker.h create mode 100644 public/matsys_controls/potterywheelpanel.h create mode 100644 public/matsys_controls/proceduraltexturepanel.h create mode 100644 public/matsys_controls/sequencepicker.h create mode 100644 public/matsys_controls/tgapreviewpanel.h create mode 100644 public/matsys_controls/vmtpanel.h create mode 100644 public/matsys_controls/vmtpicker.h create mode 100644 public/matsys_controls/vmtpreviewpanel.h create mode 100644 public/matsys_controls/vtfpicker.h create mode 100644 public/matsys_controls/vtfpreviewpanel.h create mode 100644 public/maya/IMayaVGui.h create mode 100644 public/maya/VsMayaDmx.h create mode 100644 public/maya/VsMayaMPxFactory.h create mode 100644 public/maya/VsVGuiWindow.h create mode 100644 public/maya/valveMaya.h create mode 100644 public/maya/valveMaya/HyperShadeUtil.h create mode 100644 public/maya/valveMaya/Undo.h create mode 100644 public/mdllib/mdllib.h create mode 100644 public/measure_section.cpp create mode 100644 public/measure_section.h create mode 100644 public/minmax.h create mode 100644 public/missionchooser/iasw_map_builder.h create mode 100644 public/missionchooser/iasw_mission_chooser.h create mode 100644 public/missionchooser/iasw_mission_chooser_source.h create mode 100644 public/missionchooser/iasw_random_missions.h create mode 100644 public/missionchooser/iasw_spawn_selection.h create mode 100644 public/model_types.h create mode 100644 public/modes.h create mode 100644 public/mouthinfo.h create mode 100644 public/networkstringtabledefs.h create mode 100644 public/networkvar.cpp create mode 100644 public/networkvar.h create mode 100644 public/nmatrix.h create mode 100644 public/ntree.h create mode 100644 public/nvector.h create mode 100644 public/nvtc.h create mode 100644 public/optimize.h create mode 100644 public/overlaytext.h create mode 100644 public/p4lib/ip4.h create mode 100644 public/particles/particles.h create mode 100644 public/phonemeconverter.cpp create mode 100644 public/phonemeconverter.h create mode 100644 public/phonemeextractor/phonemeextractor.h create mode 100644 public/phyfile.h create mode 100644 public/pixelwriter.h create mode 100644 public/posedebugger.cpp create mode 100644 public/posedebugger.h create mode 100644 public/r_efx.h create mode 100644 public/raytrace.h create mode 100644 public/registry.cpp create mode 100644 public/renderparm.h create mode 100644 public/responserules/response_host_interface.h create mode 100644 public/responserules/response_types.h create mode 100644 public/responserules/rr_speechconcept.h create mode 100644 public/rope_physics.cpp create mode 100644 public/rope_physics.h create mode 100644 public/rope_shared.h create mode 100644 public/savegame_version.h create mode 100644 public/saverestoretypes.h create mode 100644 public/scenefilecache/ISceneFileCache.h create mode 100644 public/scenefilecache/SceneImageFile.h create mode 100644 public/scratchpad3d.cpp create mode 100644 public/scratchpad3d.h create mode 100644 public/sentence.cpp create mode 100644 public/sentence.h create mode 100644 public/server_class.cpp create mode 100644 public/server_class.h create mode 100644 public/shaderapi/IShaderDevice.h create mode 100644 public/shaderapi/commandbuffer.h create mode 100644 public/shaderapi/ishaderapi.h create mode 100644 public/shaderapi/ishaderdynamic.h create mode 100644 public/shaderapi/ishadershadow.h create mode 100644 public/shaderapi/ishaderutil.h create mode 100644 public/shaderapi/shareddefs.h create mode 100644 public/shaderlib/BaseShader.h create mode 100644 public/shaderlib/ShaderDLL.h create mode 100644 public/shaderlib/cshader.h create mode 100644 public/shake.h create mode 100644 public/shattersurfacetypes.h create mode 100644 public/simple_physics.cpp create mode 100644 public/simple_physics.h create mode 100644 public/smooth_average.h create mode 100644 public/soundchars.h create mode 100644 public/soundcombiner.cpp create mode 100644 public/soundflags.h create mode 100644 public/soundinfo.h create mode 100644 public/soundsystem/isoundsystem.h create mode 100644 public/soundsystem/snd_audio_source.h create mode 100644 public/soundsystem/snd_device.h create mode 100644 public/stdstring.h create mode 100644 public/steam/isteamapps.h create mode 100644 public/steam/isteamclient.h create mode 100644 public/steam/isteamfriends.h create mode 100644 public/steam/isteamgameserver.h create mode 100644 public/steam/isteamgameserverstats.h create mode 100644 public/steam/isteammasterserverupdater.h create mode 100644 public/steam/isteammatchmaking.h create mode 100644 public/steam/isteamnetworking.h create mode 100644 public/steam/isteamremotestorage.h create mode 100644 public/steam/isteamuser.h create mode 100644 public/steam/isteamuserstats.h create mode 100644 public/steam/isteamutils.h create mode 100644 public/steam/matchmakingtypes.h create mode 100644 public/steam/steam_api.h create mode 100644 public/steam/steam_gameserver.h create mode 100644 public/steam/steamclientpublic.h create mode 100644 public/steam/steamtypes.h create mode 100644 public/string_t.h create mode 100644 public/stringregistry.cpp create mode 100644 public/stringregistry.h create mode 100644 public/studio.cpp create mode 100644 public/studio.h create mode 100644 public/studio_generic_io.cpp create mode 100644 public/studio_virtualmodel.cpp create mode 100644 public/surfinfo.h create mode 100644 public/texture_group_names.h create mode 100644 public/tier0/EventMasks.h create mode 100644 public/tier0/EventModes.h create mode 100644 public/tier0/IOCTLCodes.h create mode 100644 public/tier0/K8PerformanceCounters.h create mode 100644 public/tier0/P4PerformanceCounters.h create mode 100644 public/tier0/P5P6PerformanceCounters.h create mode 100644 public/tier0/PMELib.h create mode 100644 public/tier0/afxmem_override.cpp create mode 100644 public/tier0/annotations.h create mode 100644 public/tier0/basetypes.h create mode 100644 public/tier0/cache_hints.h create mode 100644 public/tier0/commonmacros.h create mode 100644 public/tier0/dbg.h create mode 100644 public/tier0/dbgflag.h create mode 100644 public/tier0/fasttimer.h create mode 100644 public/tier0/ia32detect.h create mode 100644 public/tier0/icommandline.h create mode 100644 public/tier0/l2cache.h create mode 100644 public/tier0/logging.h create mode 100644 public/tier0/mem.h create mode 100644 public/tier0/memalloc.h create mode 100644 public/tier0/memdbgoff.h create mode 100644 public/tier0/memdbgon.h create mode 100644 public/tier0/memoverride.cpp create mode 100644 public/tier0/minidump.h create mode 100644 public/tier0/miniprofiler.h create mode 100644 public/tier0/platform.h create mode 100644 public/tier0/platwindow.h create mode 100644 public/tier0/pmc360.h create mode 100644 public/tier0/progressbar.h create mode 100644 public/tier0/protected_things.h create mode 100644 public/tier0/systeminformation.h create mode 100644 public/tier0/testthread.h create mode 100644 public/tier0/threadtools.h create mode 100644 public/tier0/tslist.h create mode 100644 public/tier0/validator.h create mode 100644 public/tier0/valobject.h create mode 100644 public/tier0/valve_off.h create mode 100644 public/tier0/valve_on.h create mode 100644 public/tier0/vprof.h create mode 100644 public/tier0/wchartypes.h create mode 100644 public/tier0/win32consoleio.h create mode 100644 public/tier0/xbox_codeline_defines.h create mode 100644 public/tier1/CommandBuffer.h create mode 100644 public/tier1/KeyValues.h create mode 100644 public/tier1/UtlSortVector.h create mode 100644 public/tier1/UtlStringMap.h create mode 100644 public/tier1/bitbuf.h create mode 100644 public/tier1/byteswap.h create mode 100644 public/tier1/callqueue.h create mode 100644 public/tier1/characterset.h create mode 100644 public/tier1/checksum_crc.h create mode 100644 public/tier1/checksum_md5.h create mode 100644 public/tier1/convar.h create mode 100644 public/tier1/convar_serverbounded.h create mode 100644 public/tier1/datamanager.h create mode 100644 public/tier1/delegates.h create mode 100644 public/tier1/diff.h create mode 100644 public/tier1/exprevaluator.h create mode 100644 public/tier1/fmtstr.h create mode 100644 public/tier1/functors.h create mode 100644 public/tier1/generichash.h create mode 100644 public/tier1/iconvar.h create mode 100644 public/tier1/interface.h create mode 100644 public/tier1/interpolatedvar.cpp create mode 100644 public/tier1/interpolatedvar.h create mode 100644 public/tier1/lerp_functions.h create mode 100644 public/tier1/lzmaDecoder.h create mode 100644 public/tier1/lzss.h create mode 100644 public/tier1/mempool.h create mode 100644 public/tier1/memstack.h create mode 100644 public/tier1/netadr.h create mode 100644 public/tier1/processor_detect.h create mode 100644 public/tier1/rangecheckedvar.h create mode 100644 public/tier1/refcount.h create mode 100644 public/tier1/smartptr.h create mode 100644 public/tier1/stringpool.h create mode 100644 public/tier1/strtools.h create mode 100644 public/tier1/tier1.h create mode 100644 public/tier1/timeutils.h create mode 100644 public/tier1/tokenset.h create mode 100644 public/tier1/uniqueid.h create mode 100644 public/tier1/utlbidirectionalset.h create mode 100644 public/tier1/utlblockmemory.h create mode 100644 public/tier1/utlbuffer.h create mode 100644 public/tier1/utlbufferutil.h create mode 100644 public/tier1/utldict.h create mode 100644 public/tier1/utlenvelope.h create mode 100644 public/tier1/utlfixedmemory.h create mode 100644 public/tier1/utlflags.h create mode 100644 public/tier1/utlhandletable.h create mode 100644 public/tier1/utlhash.h create mode 100644 public/tier1/utlhashdict.h create mode 100644 public/tier1/utlintrusivelist.h create mode 100644 public/tier1/utllinkedlist.h create mode 100644 public/tier1/utlmap.h create mode 100644 public/tier1/utlmemory.h create mode 100644 public/tier1/utlmultilist.h create mode 100644 public/tier1/utlntree.h create mode 100644 public/tier1/utlobjectreference.h create mode 100644 public/tier1/utlpriorityqueue.h create mode 100644 public/tier1/utlqueue.h create mode 100644 public/tier1/utlrbtree.h create mode 100644 public/tier1/utlsoacontainer.h create mode 100644 public/tier1/utlstack.h create mode 100644 public/tier1/utlstring.h create mode 100644 public/tier1/utlsymbol.h create mode 100644 public/tier1/utlsymbollarge.h create mode 100644 public/tier1/utltshash.h create mode 100644 public/tier1/utlvector.h create mode 100644 public/tier2/beamsegdraw.h create mode 100644 public/tier2/fileutils.h create mode 100644 public/tier2/interval.h create mode 100644 public/tier2/keybindings.h create mode 100644 public/tier2/meshutils.h create mode 100644 public/tier2/p4helpers.h create mode 100644 public/tier2/renderutils.h create mode 100644 public/tier2/resourceprecacher.h create mode 100644 public/tier2/riff.h create mode 100644 public/tier2/soundutils.h create mode 100644 public/tier2/tier2.h create mode 100644 public/tier2/tier2_logging.h create mode 100644 public/tier2/tier2dm.h create mode 100644 public/tier2/tokenreader.h create mode 100644 public/tier2/utlstreambuffer.h create mode 100644 public/tier2/vconfig.h create mode 100644 public/tier3/choreoutils.h create mode 100644 public/tier3/mdlutils.h create mode 100644 public/tier3/scenetokenprocessor.h create mode 100644 public/tier3/tier3.h create mode 100644 public/tier3/tier3dm.h create mode 100644 public/toolframework/iclientenginetools.h create mode 100644 public/toolframework/ienginetool.h create mode 100644 public/toolframework/iserverenginetools.h create mode 100644 public/toolframework/itooldictionary.h create mode 100644 public/toolframework/itoolentity.h create mode 100644 public/toolframework/itoolframework.h create mode 100644 public/toolframework/itoolsystem.h create mode 100644 public/toolframework/toolframework.cpp create mode 100644 public/tools/bonelist.cpp create mode 100644 public/tools/bonelist.h create mode 100644 public/trace.h create mode 100644 public/unitlib/unitlib.h create mode 100644 public/vallocator.cpp create mode 100644 public/vallocator.h create mode 100644 public/vaudio/ivaudio.h create mode 100644 public/vcollide.h create mode 100644 public/vcollide_parse.h create mode 100644 public/vgui/Cursor.h create mode 100644 public/vgui/Dar.h create mode 100644 public/vgui/IBorder.h create mode 100644 public/vgui/IClientPanel.h create mode 100644 public/vgui/IHTML.h create mode 100644 public/vgui/IImage.h create mode 100644 public/vgui/IInput.h create mode 100644 public/vgui/IInputInternal.h create mode 100644 public/vgui/ILocalize.h create mode 100644 public/vgui/IPanel.h create mode 100644 public/vgui/IScheme.h create mode 100644 public/vgui/ISurface.h create mode 100644 public/vgui/ISurfaceV30.h create mode 100644 public/vgui/ISystem.h create mode 100644 public/vgui/IVGui.h create mode 100644 public/vgui/IVguiMatInfo.h create mode 100644 public/vgui/IVguiMatInfoVar.h create mode 100644 public/vgui/KeyCode.h create mode 100644 public/vgui/MouseCode.h create mode 100644 public/vgui/Point.h create mode 100644 public/vgui/VGUI.h create mode 100644 public/vgui/ischemesurface.h create mode 100644 public/vgui_controls/AnalogBar.h create mode 100644 public/vgui_controls/AnimatingImagePanel.h create mode 100644 public/vgui_controls/AnimationController.h create mode 100644 public/vgui_controls/BitmapImagePanel.h create mode 100644 public/vgui_controls/BuildGroup.h create mode 100644 public/vgui_controls/BuildModeDialog.h create mode 100644 public/vgui_controls/Button.h create mode 100644 public/vgui_controls/CheckButton.h create mode 100644 public/vgui_controls/CheckButtonList.h create mode 100644 public/vgui_controls/CircularProgressBar.h create mode 100644 public/vgui_controls/ComboBox.h create mode 100644 public/vgui_controls/ControllerMap.h create mode 100644 public/vgui_controls/Controls.h create mode 100644 public/vgui_controls/CvarToggleCheckButton.h create mode 100644 public/vgui_controls/DialogManager.h create mode 100644 public/vgui_controls/DirectorySelectDialog.h create mode 100644 public/vgui_controls/Divider.h create mode 100644 public/vgui_controls/EditablePanel.h create mode 100644 public/vgui_controls/ExpandButton.h create mode 100644 public/vgui_controls/FileOpenDialog.h create mode 100644 public/vgui_controls/FileOpenStateMachine.h create mode 100644 public/vgui_controls/FocusNavGroup.h create mode 100644 public/vgui_controls/Frame.h create mode 100644 public/vgui_controls/GraphPanel.h create mode 100644 public/vgui_controls/HTML.h create mode 100644 public/vgui_controls/Image.h create mode 100644 public/vgui_controls/ImageList.h create mode 100644 public/vgui_controls/ImagePanel.h create mode 100644 public/vgui_controls/InputDialog.h create mode 100644 public/vgui_controls/KeyBindingHelpDialog.h create mode 100644 public/vgui_controls/KeyBindingMap.h create mode 100644 public/vgui_controls/KeyBoardEditorDialog.h create mode 100644 public/vgui_controls/Label.h create mode 100644 public/vgui_controls/ListPanel.h create mode 100644 public/vgui_controls/ListViewPanel.h create mode 100644 public/vgui_controls/Menu.h create mode 100644 public/vgui_controls/MenuBar.h create mode 100644 public/vgui_controls/MenuButton.h create mode 100644 public/vgui_controls/MenuItem.h create mode 100644 public/vgui_controls/MessageBox.h create mode 100644 public/vgui_controls/MessageDialog.h create mode 100644 public/vgui_controls/MessageMap.h create mode 100644 public/vgui_controls/PHandle.h create mode 100644 public/vgui_controls/Panel.h create mode 100644 public/vgui_controls/PanelAnimationVar.h create mode 100644 public/vgui_controls/PanelListPanel.h create mode 100644 public/vgui_controls/PerforceFileExplorer.h create mode 100644 public/vgui_controls/PerforceFileList.h create mode 100644 public/vgui_controls/ProgressBar.h create mode 100644 public/vgui_controls/ProgressBox.h create mode 100644 public/vgui_controls/PropertyDialog.h create mode 100644 public/vgui_controls/PropertyPage.h create mode 100644 public/vgui_controls/PropertySheet.h create mode 100644 public/vgui_controls/QueryBox.h create mode 100644 public/vgui_controls/RadioButton.h create mode 100644 public/vgui_controls/RichText.h create mode 100644 public/vgui_controls/RotatingProgressBar.h create mode 100644 public/vgui_controls/ScalableImagePanel.h create mode 100644 public/vgui_controls/ScrollBar.h create mode 100644 public/vgui_controls/ScrollBarSlider.h create mode 100644 public/vgui_controls/ScrollableEditablePanel.h create mode 100644 public/vgui_controls/SectionedListPanel.h create mode 100644 public/vgui_controls/Slider.h create mode 100644 public/vgui_controls/Splitter.h create mode 100644 public/vgui_controls/TextEntry.h create mode 100644 public/vgui_controls/TextImage.h create mode 100644 public/vgui_controls/ToggleButton.h create mode 100644 public/vgui_controls/ToolWindow.h create mode 100644 public/vgui_controls/Tooltip.h create mode 100644 public/vgui_controls/TreeView.h create mode 100644 public/vgui_controls/TreeViewListControl.h create mode 100644 public/vgui_controls/URLLabel.h create mode 100644 public/vgui_controls/WizardPanel.h create mode 100644 public/vgui_controls/WizardSubPanel.h create mode 100644 public/vgui_controls/consoledialog.h create mode 100644 public/vgui_controls/pch_vgui_controls.h create mode 100644 public/vgui_controls/perforcefilelistframe.h create mode 100644 public/vgui_controls/savedocumentquery.h create mode 100644 public/vgui_controls/subrectimage.h create mode 100644 public/vgui_controls/tgaimagepanel.h create mode 100644 public/vgui_controls/vgui_controls.cpp create mode 100644 public/videocfg/videocfg.h create mode 100644 public/view_shared.h create mode 100644 public/vphysics/collision_set.h create mode 100644 public/vphysics/constraints.h create mode 100644 public/vphysics/friction.h create mode 100644 public/vphysics/object_hash.h create mode 100644 public/vphysics/performance.h create mode 100644 public/vphysics/player_controller.h create mode 100644 public/vphysics/stats.h create mode 100644 public/vphysics/vehicles.h create mode 100644 public/vphysics/virtualmesh.h create mode 100644 public/vphysics_interface.h create mode 100644 public/vpklib/packedstore.h create mode 100644 public/vscript/ivscript.h create mode 100644 public/vscript/vscript_templates.h create mode 100644 public/vstdlib/IKeyValuesSystem.h create mode 100644 public/vstdlib/cvar.h create mode 100644 public/vstdlib/iprocessutils.h create mode 100644 public/vstdlib/jobthread.h create mode 100644 public/vstdlib/pch_vstdlib.h create mode 100644 public/vstdlib/random.h create mode 100644 public/vstdlib/vcover.h create mode 100644 public/vstdlib/vstdlib.h create mode 100644 public/vtf/vtf.h create mode 100644 public/wadtypes.h create mode 100644 public/winlite.h create mode 100644 public/worldsize.h create mode 100644 public/xwvfile.h create mode 100644 public/zip/XUnzip.h create mode 100644 public/zip/XZip.h create mode 100644 public/zip_uncompressed.h create mode 100644 public/zip_utils.cpp create mode 100644 public/zip_utils.h create mode 100644 scanner.cpp create mode 100644 scanner.hpp create mode 100644 workshopstopper9000.sln create mode 100644 workshopstopper9000.vcxproj create mode 100644 workshopstopper9000.vcxproj.user create mode 100644 workshopstopper9000.vdf diff --git a/.github/workflows/ms-build.yml b/.github/workflows/ms-build.yml new file mode 100644 index 0000000..b657fae --- /dev/null +++ b/.github/workflows/ms-build.yml @@ -0,0 +1,34 @@ +name: MSBuild + +on: + push + +permissions: + contents: read + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Add MSBuild into PATH + uses: microsoft/setup-msbuild@v2 + with: + # Version of Visual Studio to search; defaults to latest if not specified + vs-version: "latest" + # The preferred processor architecture of MSBuild. Can be either "x86", "x64", or "arm64". "x64" is only available from Visual Studio version 17.0 and later. + msbuild-architecture: "x86" + + - name: Build + working-directory: ${{env.GITHUB_WORKSPACE}} + # Add additional options to the MSBuild command line here (like platform or verbosity level). + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + run: msbuild /m /p:Configuration="Release" . + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: workshopstopper9000 + path: Release/workshopstopper9000.dll diff --git a/.gitignore b/.gitignore index 259148f..d671d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ *.exe *.out *.app +/.vs +/Release diff --git a/README.md b/README.md index 80fb585..2686aae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ -# WorkshopStopper9000 - A Source Engine Plugin for Portal 2 to stop workshop maps downloading for Sourcemods. +# ***WorkshopStopper9000*** + +## ***A Source Engine Plugin for Portal 2 Source Mods to stop workshop maps downloading.*** + +One issue that plagues Source Mods for Portal 2 is the issue where the game sees that the workshop folder is empty, so it re-downloads ***ALL*** of the users workshop maps into the Source Mods maps folder. There has been no proper way to stop this from happening, so here is a plugin to solve the issue! + +Simply stick this plugin and its respective `.vdc` file into a `addons` folder in the Source Mod's game directory, and Portal 2 should automatically start the plugin up with the game and thus stop the game from downloading workshop maps. That's pretty much it. This took about 20 minutes to make, so if there are any issues, make a Issue post. I'll get to it within the next 20 years. + +**As of writing, 11/09/2024, the plugin is only able compatible for Windows, the signature scanner needs to be made compatible for Linux still. Apologizes for any inconvenience.** + +``` +Please give credit to Orsell/OrsellGaming, Nanoman2525, and NULLderef, as well as the Portal 2: Multiplayer Mod Team (plugin is built off some of the P2:MM plugin's code) if you use this plugin or use its code in any way with your Source Mod. You also need to include this repository's and the P2:MM's License (see below). +``` + +``` +MIT License + +Copyright (c) 2024 Portal 2: Multiplayer Mod Team + +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. +``` diff --git a/common/ConfigManager.h b/common/ConfigManager.h new file mode 100644 index 0000000..9c7f4bc --- /dev/null +++ b/common/ConfigManager.h @@ -0,0 +1,116 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CONFIGMANAGER_H +#define CONFIGMANAGER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "KeyValues.h" +#include "utlvector.h" +#include "filesystem_init.h" + + +// See filesystem_init for the vconfig registry values. + + +#define TOKEN_GAMES "Games" +#define TOKEN_GAME_DIRECTORY "GameDir" +#define TOKEN_TOOLS "Tools" + +// STEAM CLOUD FLAGS +#define STEAMREMOTESTORAGE_CLOUD_CONFIG (1<<0) +#define STEAMREMOTESTORAGE_CLOUD_SPRAY (1<<1) + +#define STEAMREMOTESTORAGE_CLOUD_ALL 0x7fff // all bits set, so any new items added will be on by default + +struct defaultConfigInfo_t +{ + char gameName[MAX_PATH]; + char gameDir[MAX_PATH]; + char FGD[MAX_PATH]; + char steamPath[MAX_PATH]; + char defaultPointEntity[MAX_PATH]; + char exeName[MAX_PATH]; + int steamAppID; +}; + +enum eSDKEpochs +{ + HL2 = 1, + EP1 = 2, + EP2 = 3, + SWARM = 4, +}; + +extern defaultConfigInfo_t *gDefaultConfigs[]; + +class CGameConfigManager +{ +public: + + enum loadStatus_t + { + LOADSTATUS_NONE = 0, // Configs were loaded with no error + LOADSTATUS_CONVERTED, // GameConfig.txt did not exist and was created by converting GameCfg.INI + LOADSTATUS_CREATED, // GameCfg.INI was not found, the system created the default configuration based on found GameInfo.txt resources + LOADSTATUS_ERROR, // File was not loaded and was unable to perform the above fail-safe procedures + }; + + CGameConfigManager( void ); + CGameConfigManager( const char *fileName ); + + ~CGameConfigManager( void ); + + bool LoadConfigs( const char *baseDir = NULL ); + bool SaveConfigs( const char *baseDir = NULL ); + bool ResetConfigs( const char *baseDir = NULL ); + + int GetNumConfigs( void ); + + KeyValues *GetGameBlock( void ); + KeyValues *GetGameSubBlock( const char *keyName ); + bool GetDefaultGameBlock( KeyValues *pIn ); + + bool IsLoaded( void ) const { return m_pData != NULL; } + + bool WasConvertedOnLoad( void ) const { return m_LoadStatus == LOADSTATUS_CONVERTED; } + bool WasCreatedOnLoad( void ) const { return m_LoadStatus == LOADSTATUS_CREATED; } + + bool AddDefaultConfig( const defaultConfigInfo_t &info, KeyValues *out, const char *rootDirectory, const char *gameExeDir ); + + void SetBaseDirectory( const char *pDirectory ); + + void GetRootGameDirectory( char *out, size_t outLen, const char *rootDir, const char *steamDir ); + + const char *GetRootDirectory( void ); + void SetSDKEpoch( eSDKEpochs epoch ) { m_eSDKEpoch = epoch; }; + +private: + + void GetRootContentDirectory( char *out, size_t outLen, const char *rootDir ); + + const char *GetBaseDirectory( void ); + const char *GetIniFilePath( void ); + + bool LoadConfigsInternal( const char *baseDir, bool bRecursiveCall ); + void UpdateConfigsInternal( void ); + void VersionConfig( void ); + bool IsConfigCurrent( void ); + + bool ConvertGameConfigsINI( void ); + bool CreateAllDefaultConfigs( void ); + bool IsAppSubscribed( int nAppID ); + + loadStatus_t m_LoadStatus; // Holds various state about what occured while loading + KeyValues *m_pData; // Data as read from configuration file + char m_szBaseDirectory[MAX_PATH]; // Default directory + eSDKEpochs m_eSDKEpoch; // Holds the "working version" of the SDK for times when we need to create an older set of game configurations. + // This is required now that the SDK is deploying the tools for both the latest and previous versions of the engine. +}; + +#endif // CONFIGMANAGER_H diff --git a/common/GameUI/IGameConsole.h b/common/GameUI/IGameConsole.h new file mode 100644 index 0000000..543370f --- /dev/null +++ b/common/GameUI/IGameConsole.h @@ -0,0 +1,41 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IGAMECONSOLE_H +#define IGAMECONSOLE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" + + +//----------------------------------------------------------------------------- +// Purpose: interface to game/dev console +//----------------------------------------------------------------------------- +abstract_class IGameConsole : public IBaseInterface +{ +public: + // activates the console, makes it visible and brings it to the foreground + virtual void Activate() = 0; + + virtual void Initialize() = 0; + + // hides the console + virtual void Hide() = 0; + + // clears the console + virtual void Clear() = 0; + + // return true if the console has focus + virtual bool IsConsoleVisible() = 0; + + virtual void SetParent( int parent ) = 0; +}; + +#define GAMECONSOLE_INTERFACE_VERSION "GameConsole004" + +#endif // IGAMECONSOLE_H diff --git a/common/GameUI/IGameUI.h b/common/GameUI/IGameUI.h new file mode 100644 index 0000000..e66d73e --- /dev/null +++ b/common/GameUI/IGameUI.h @@ -0,0 +1,103 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IGAMEUI_H +#define IGAMEUI_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "vgui/ipanel.h" + +#if !defined( _X360 ) +#include "xbox/xboxstubs.h" +#endif + +class CCommand; + +// reasons why the user can't connect to a game server +enum ESteamLoginFailure +{ + STEAMLOGINFAILURE_NONE, + STEAMLOGINFAILURE_BADTICKET, + STEAMLOGINFAILURE_NOSTEAMLOGIN, + STEAMLOGINFAILURE_VACBANNED, + STEAMLOGINFAILURE_LOGGED_IN_ELSEWHERE +}; + +enum ESystemNotify +{ + SYSTEMNOTIFY_STORAGEDEVICES_CHANGED, + SYSTEMNOTIFY_USER_SIGNEDIN, + SYSTEMNOTIFY_USER_SIGNEDOUT, + SYSTEMNOTIFY_XLIVE_LOGON_ESTABLISHED, // we are logged into live service + SYSTEMNOTIFY_XLIVE_LOGON_CLOSED, // no longer logged into live - either from natural (signed out) or unnatural (e.g. severed net connection) causes + SYSTEMNOTIFY_XUIOPENING, + SYSTEMNOTIFY_XUICLOSED, + SYSTEMNOTIFY_INVITE_SHUTDOWN, // Cross-game invite is causing us to shutdown + SYSTEMNOTIFY_MUTECHANGED, // Player changed mute settings + SYSTEMNOTIFY_INPUTDEVICESCHANGED, // Input device has changed (used for controller disconnection) + SYSTEMNOTIFY_PROFILE_UNAVAILABLE, // Profile failed to read or write +}; + +//----------------------------------------------------------------------------- +// Purpose: contains all the functions that the GameUI dll exports +//----------------------------------------------------------------------------- +abstract_class IGameUI +{ +public: + // initialization/shutdown + virtual void Initialize( CreateInterfaceFn appFactory ) = 0; + virtual void PostInit() = 0; + + // connect to other interfaces at the same level (gameui.dll/server.dll/client.dll) + virtual void Connect( CreateInterfaceFn gameFactory ) = 0; + + virtual void Start() = 0; + virtual void Shutdown() = 0; + virtual void RunFrame() = 0; + + // notifications + virtual void OnGameUIActivated() = 0; + virtual void OnGameUIHidden() = 0; + + // OLD: Use OnConnectToServer2 + virtual void OLD_OnConnectToServer(const char *game, int IP, int port) = 0; + + virtual void OnDisconnectFromServer_OLD( uint8 eSteamLoginFailure, const char *username ) = 0; + virtual void OnLevelLoadingStarted( const char *levelName, bool bShowProgressDialog ) = 0; + virtual void OnLevelLoadingFinished(bool bError, const char *failureReason, const char *extendedReason) = 0; + + // level loading progress, returns true if the screen needs updating + virtual bool UpdateProgressBar(float progress, const char *statusText) = 0; + // Shows progress desc, returns previous setting... (used with custom progress bars ) + virtual bool SetShowProgressText( bool show ) = 0; + + // !!!!!!!!!members added after "GameUI011" initial release!!!!!!!!!!!!!!!!!!! + // Allows the level loading progress to show map-specific info + virtual void SetProgressLevelName( const char *levelName ) = 0; + + // inserts specified panel as background for level load dialog + virtual void SetLoadingBackgroundDialog( vgui::VPANEL panel ) = 0; + + virtual void OnConnectToServer2(const char *game, int IP, int connectionPort, int queryPort) = 0; + + virtual void SetProgressOnStart() = 0; + virtual void OnDisconnectFromServer( uint8 eSteamLoginFailure ) = 0; + + virtual void NeedConnectionProblemWaitScreen() = 0; + virtual void ShowPasswordUI( char const *pchCurrentPW ) = 0; + +#if defined( _X360 ) && defined( _DEMO ) + virtual void OnDemoTimeout( void ) = 0; +#endif +}; + +#define GAMEUI_INTERFACE_VERSION "GameUI011" + +#endif // IGAMEUI_H diff --git a/common/IRunGameEngine.h b/common/IRunGameEngine.h new file mode 100644 index 0000000..7e3c384 --- /dev/null +++ b/common/IRunGameEngine.h @@ -0,0 +1,81 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef IRUNGAMEENGINE_H +#define IRUNGAMEENGINE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +#ifdef GetUserName +#undef GetUserName +#endif + +//----------------------------------------------------------------------------- +// Purpose: Interface to running the game engine +//----------------------------------------------------------------------------- +abstract_class IRunGameEngine : public IBaseInterface +{ +public: + // Returns true if the engine is running, false otherwise. + virtual bool IsRunning() = 0; + + // Adds text to the engine command buffer. Only works if IsRunning() + // returns true on success, false on failure + virtual bool AddTextCommand(const char *text) = 0; + + // runs the engine with the specified command line parameters. Only works if !IsRunning() + // returns true on success, false on failure + virtual bool RunEngine(const char *gameDir, const char *commandLineParams) = 0; + + // returns true if the player is currently connected to a game server + virtual bool IsInGame() = 0; + + // gets information about the server the engine is currently connected to + // returns true on success, false on failure + virtual bool GetGameInfo(char *infoBuffer, int bufferSize) = 0; + + // tells the engine our userID + virtual void SetTrackerUserID(int trackerID, const char *trackerName) = 0; + + // this next section could probably moved to another interface + // iterates users + // returns the number of user + virtual int GetPlayerCount() = 0; + + // returns a playerID for a player + // playerIndex is in the range [0, GetPlayerCount) + virtual unsigned int GetPlayerFriendsID(int playerIndex) = 0; + + // gets the in-game name of another user, returns NULL if that user doesn't exists + virtual const char *GetPlayerName(int friendsID) = 0; + + // gets the friends name of a player + virtual const char *GetPlayerFriendsName(int friendsID) = 0; + + // returns the engine build number and mod version string for server versioning + virtual unsigned int GetEngineBuildNumber() = 0; + virtual const char *GetProductVersionString() = 0; + + // new interface to RunEngine (done so we don't have to roll the interface version) + virtual bool RunEngine2(const char *gameDir, const char *commandLineParams, bool isSourceGame) = 0; + + enum ERunResult + { + k_ERunResultOkay = 0, + k_ERunResultModNotInstalled = 1, + k_ERunResultAppNotFound = 2, + k_ERunResultNotInitialized = 3, + }; + virtual ERunResult RunEngine( int iAppID, const char *gameDir, const char *commandLineParams ) = 0; + +}; + +#define RUNGAMEENGINE_INTERFACE_VERSION "RunGameEngine005" + +#endif // IRUNGAMEENGINE_H diff --git a/common/IVGuiModule.h b/common/IVGuiModule.h new file mode 100644 index 0000000..7a7369d --- /dev/null +++ b/common/IVGuiModule.h @@ -0,0 +1,64 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVGUIMODULE_H +#define IVGUIMODULE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include + +//----------------------------------------------------------------------------- +// Purpose: Standard interface to loading vgui modules +//----------------------------------------------------------------------------- +abstract_class IVGuiModule : public IBaseInterface +{ +public: + // called first to setup the module with the vgui + // returns true on success, false on failure + virtual bool Initialize(CreateInterfaceFn *vguiFactories, int factoryCount) = 0; + + // called after all the modules have been initialized + // modules should use this time to link to all the other module interfaces + virtual bool PostInitialize(CreateInterfaceFn *modules, int factoryCount) = 0; + + // called when the module is selected from the menu or otherwise activated + virtual bool Activate() = 0; + + // returns true if the module is successfully initialized and available + virtual bool IsValid() = 0; + + // requests that the UI is temporarily disabled and all data files saved + virtual void Deactivate() = 0; + + // restart from a Deactivate() + virtual void Reactivate() = 0; + + // called when the module is about to be shutdown + virtual void Shutdown() = 0; + + // returns a handle to the main module panel + virtual vgui::VPANEL GetPanel() = 0; + + // sets the parent of the main module panel + virtual void SetParent(vgui::VPANEL parent) = 0; + + // messages sent through through the panel returned by GetPanel(): + // + // "ConnectedToGame" "ip" "port" "gamedir" + // "DisconnectedFromGame" + // "ActiveGameName" "name" + // "LoadingStarted" "type" "name" + // "LoadingFinished" "type" "name" +}; + +#define VGUIMODULE_INTERFACE_VERSION "VGuiModule002" + + +#endif // IVGUIMODULE_H diff --git a/common/IVGuiModuleLoader.h b/common/IVGuiModuleLoader.h new file mode 100644 index 0000000..7281aa7 --- /dev/null +++ b/common/IVGuiModuleLoader.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVGUIMODULELOADER_H +#define IVGUIMODULELOADER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +//----------------------------------------------------------------------------- +// Purpose: interface to accessing all loaded modules +//----------------------------------------------------------------------------- +abstract_class IVGuiModuleLoader : public IBaseInterface +{ +public: + virtual int GetModuleCount() = 0; + virtual const char *GetModuleLabel(int moduleIndex) = 0; + virtual CreateInterfaceFn GetModuleFactory(int moduleIndex) = 0; + virtual bool ActivateModule(int moduleIndex) = 0; + virtual bool ActivateModule(const char *moduleName) = 0; + virtual void SetPlatformToRestart() = 0; +}; + +#define VGUIMODULELOADER_INTERFACE_VERSION "VGuiModuleLoader003" + + +#endif // IVGUIMODULELOADER_H diff --git a/common/ServerBrowser/IServerBrowser.h b/common/ServerBrowser/IServerBrowser.h new file mode 100644 index 0000000..68337bc --- /dev/null +++ b/common/ServerBrowser/IServerBrowser.h @@ -0,0 +1,45 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISERVERBROWSER_H +#define ISERVERBROWSER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +//----------------------------------------------------------------------------- +// Purpose: Interface to server browser module +//----------------------------------------------------------------------------- +abstract_class IServerBrowser +{ +public: + // activates the server browser window, brings it to the foreground + virtual bool Activate() = 0; + + // joins a game directly + virtual bool JoinGame( uint32 unGameIP, uint16 usGamePort ) = 0; + + // joins a specified game - game info dialog will only be opened if the server is fully or passworded + virtual bool JoinGame( uint64 ulSteamIDFriend ) = 0; + + // opens a game info dialog to watch the specified server; associated with the friend 'userName' + virtual bool OpenGameInfoDialog( uint64 ulSteamIDFriend ) = 0; + + // forces the game info dialog closed + virtual void CloseGameInfoDialog( uint64 ulSteamIDFriend ) = 0; + + // closes all the game info dialogs + virtual void CloseAllGameInfoDialogs() = 0; +}; + +#define SERVERBROWSER_INTERFACE_VERSION "ServerBrowser003" + + + +#endif // ISERVERBROWSER_H diff --git a/common/blackbox_helper.cpp b/common/blackbox_helper.cpp new file mode 100644 index 0000000..a6d772a --- /dev/null +++ b/common/blackbox_helper.cpp @@ -0,0 +1,48 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "../../engine/iblackbox.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IBlackBox *blackboxrecorder; + +void BlackBox_Record( const char *type, const char *pFormat, ... ) +{ + static ConVarRef blackbox( "blackbox" ); + + if ( IsX360() ) + return; + + if ( !blackbox.IsValid() || !blackbox.GetBool() ) + return; + + int type_num; + for ( type_num = 0; type_num < blackboxrecorder->GetTypeCount(); type_num++ ) + { + if ( !V_strcasecmp( blackboxrecorder->GetTypeName( type_num ), type ) ) + break; + } + + if ( type_num >= blackboxrecorder->GetTypeCount() ) + { + Msg( "Invalid blackbox type: %s\n", type ); + return; + } + + char szMessage[1024]; + va_list marker; + + va_start( marker, pFormat); + Q_vsnprintf( szMessage, sizeof( szMessage ), pFormat, marker); + va_end( marker ); + + //Msg( "Record: %s: %s\n", type, szMessage ); + blackboxrecorder->Record( type_num, szMessage ); +} + diff --git a/common/blackbox_helper.h b/common/blackbox_helper.h new file mode 100644 index 0000000..192925a --- /dev/null +++ b/common/blackbox_helper.h @@ -0,0 +1,11 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#ifndef BLACKBOX_HELPER_H +#define BLACKBOX_HELPER_H 1 + +void BlackBox_Record(const char * type, const char *fmt, ...); + +#endif \ No newline at end of file diff --git a/common/compiledcaptionswap.cpp b/common/compiledcaptionswap.cpp new file mode 100644 index 0000000..757b4f5 --- /dev/null +++ b/common/compiledcaptionswap.cpp @@ -0,0 +1,103 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Swap a compiled caption file. +// +// $NoKeywords: $ +//=============================================================================// + +#include "utlbuffer.h" +#include "byteswap.h" +#include "filesystem.h" +#include "tier2/fileutils.h" +#include "captioncompiler.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +BEGIN_BYTESWAP_DATADESC( CompiledCaptionHeader_t ) + DEFINE_FIELD( magic, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_FIELD( numblocks, FIELD_INTEGER ), + DEFINE_FIELD( blocksize, FIELD_INTEGER ), + DEFINE_FIELD( directorysize, FIELD_INTEGER ), + DEFINE_FIELD( dataoffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( CaptionLookup_t ) + DEFINE_FIELD( hash, FIELD_INTEGER ), + DEFINE_FIELD( blockNum, FIELD_INTEGER ), + DEFINE_FIELD( offset, FIELD_SHORT ), + DEFINE_FIELD( length, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +//----------------------------------------------------------------------------- +// Swap a compiled closecaption file +//----------------------------------------------------------------------------- +bool SwapClosecaptionFile( void *pData ) +{ + CByteswap swap; + swap.ActivateByteSwapping( true ); + + CompiledCaptionHeader_t *pHdr = (CompiledCaptionHeader_t*)pData; + + if ( IsX360() ) + { + // pre-swap file header + swap.SwapFieldsToTargetEndian( pHdr ); + } + + if ( pHdr->magic != COMPILED_CAPTION_FILEID || pHdr->version != COMPILED_CAPTION_VERSION ) + { + // bad data + return false; + } + + // lookup headers + pData = (byte*)pData + sizeof(CompiledCaptionHeader_t); + swap.SwapFieldsToTargetEndian( (CaptionLookup_t*)pData, pHdr->directorysize ); + + // unicode data + pData = (byte*)pHdr + pHdr->dataoffset; + swap.SwapBufferToTargetEndian( (wchar_t*)pData, (wchar_t*)pData, pHdr->numblocks * pHdr->blocksize / sizeof(wchar_t) ); + + if ( IsPC() ) + { + // post-swap file header + swap.SwapFieldsToTargetEndian( pHdr ); + } + + return true; +} + +#if defined( CLIENT_DLL ) || defined( GAME_DLL ) +//----------------------------------------------------------------------------- +// Callback for UpdateOrCreate - generates .360 file +//----------------------------------------------------------------------------- +static bool CaptionCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData ) +{ + // Generate the file + CUtlBuffer buf; + bool bOk = g_pFullFileSystem->ReadFile( pSourceName, pPathID, buf ); + if ( bOk ) + { + bOk = SwapClosecaptionFile( buf.Base() ); + if ( bOk ) + { + bOk = g_pFullFileSystem->WriteFile( pTargetName, pPathID, buf ); + } + else + { + Warning( "Failed to create %s\n", pTargetName ); + } + } + return bOk; +} + +//----------------------------------------------------------------------------- +// Calls utility function UpdateOrCreate +//----------------------------------------------------------------------------- +int UpdateOrCreateCaptionFile( const char *pSourceName, char *pTargetName, int maxLen, bool bForce ) +{ + return ::UpdateOrCreate( pSourceName, pTargetName, maxLen, "GAME", CaptionCreateCallback, bForce ); +} +#endif \ No newline at end of file diff --git a/common/debug_dll_check.cpp b/common/debug_dll_check.cpp new file mode 100644 index 0000000..82d03ae --- /dev/null +++ b/common/debug_dll_check.cpp @@ -0,0 +1,16 @@ +//======= Copyright © 1996-2008, Valve Corporation, All rights reserved. ======// +// +// Purpose: Add a specially formatted string to each debug DLL of the form +// "%DLLNAME%.dll is built debug!". We can search for this string via +// a Perforce trigger to ensure that debug LIBs are not checked in. +// +//=============================================================================// + +#if defined(DEBUG) || defined(_DEBUG) +#include "tier0/platform.h" + +#define _DEBUGONLYSTRING(x) #x +#define DEBUGONLYSTRING(x) _DEBUGONLYSTRING(x) +DLL_GLOBAL_EXPORT char const *pDebugString = DEBUGONLYSTRING(DLLNAME) ".dll is built debug!"; + +#endif diff --git a/common/ifilesystemopendialog.h b/common/ifilesystemopendialog.h new file mode 100644 index 0000000..96df026 --- /dev/null +++ b/common/ifilesystemopendialog.h @@ -0,0 +1,51 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef IFILESYSTEMOPENDIALOG_H +#define IFILESYSTEMOPENDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + + + +#define FILESYSTEMOPENDIALOG_VERSION "FileSystemOpenDlg003" + + +class IFileSystem; + + +abstract_class IFileSystemOpenDialog +{ +public: + // You must call this first to set the hwnd. + virtual void Init( CreateInterfaceFn factory, void *parentHwnd ) = 0; + + // Call this to free the dialog. + virtual void Release() = 0; + + // Use these to configure the dialog. + virtual void AddFileMask( const char *pMask ) = 0; + virtual void SetInitialDir( const char *pDir, const char *pPathID = NULL ) = 0; + virtual void SetFilterMdlAndJpgFiles( bool bFilter ) = 0; + virtual void GetFilename( char *pOut, int outLen ) const = 0; // Get the filename they chose. + + // Call this to make the dialog itself. Returns true if they clicked OK and false + // if they canceled it. + virtual bool DoModal() = 0; + + // This uses the standard windows file open dialog. + virtual bool DoModal_WindowsDialog() = 0; + + // Mark the dialog as allowing us to multi-select + virtual void AllowMultiSelect( bool bAllow ) = 0; + + // Request the length of the buffer sufficient enough to hold the entire filename result + virtual int GetFilenameBufferSize() const = 0; +}; + + +#endif // IFILESYSTEMOPENDIALOG_H diff --git a/common/language.cpp b/common/language.cpp new file mode 100644 index 0000000..d22371c --- /dev/null +++ b/common/language.cpp @@ -0,0 +1,116 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: languages definition +// +//============================================================================= + +#include "language.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +struct Language_t +{ + char *m_pchName; + char *m_pchShortName; + char *m_pchVGUILocalizationName; + ELanguage m_ELanguage; +}; + + +static Language_t s_LanguageNames[] = +{ + { "None", "none", "None", k_Lang_None }, + { "English", "english", "#GameUI_Language_English", k_Lang_English }, + { "German", "german", "#GameUI_Language_German", k_Lang_German } , + { "French", "french", "#GameUI_Language_French", k_Lang_French } , + { "Italian", "italian", "#GameUI_Language_Italian", k_Lang_Italian } , + { "Korean", "koreana", "#GameUI_Language_Korean", k_Lang_Korean } , + { "Spanish", "spanish", "#GameUI_Language_Spanish", k_Lang_Spanish }, + { "Simplified_Chinese", "schinese", "#GameUI_Language_Simplified_Chinese", k_Lang_Simplified_Chinese } , + { "Traditional_Chinese", "tchinese", "#GameUI_Language_Traditional_Chinese", k_Lang_Traditional_Chinese } , + { "Russian", "russian", "#GameUI_Language_Russian", k_Lang_Russian } , + { "Thai", "thai", "#GameUI_Language_Thai", k_Lang_Thai } , + { "Japanese", "japanese", "#GameUI_Language_Japanese", k_Lang_Japanese } , + { "Portuguese", "portuguese", "#GameUI_Language_Portuguese", k_Lang_Portuguese } , + { "Polish", "polish", "#GameUI_Language_Polish", k_Lang_Polish } , + { "Danish", "danish", "#GameUI_Language_Danish", k_Lang_Danish } , + { "Dutch", "dutch", "#GameUI_Language_Dutch", k_Lang_Dutch } , + { "Finnish", "finnish", "#GameUI_Language_Finnish", k_Lang_Finnish } , + { "Norwegian", "norwegian", "#GameUI_Language_Norwegian", k_Lang_Norwegian } , + { "Swedish", "swedish", "#GameUI_Language_Swedish", k_Lang_Swedish } , +}; + + +//----------------------------------------------------------------------------- +// STATIC +// Purpose: find the language by name +//----------------------------------------------------------------------------- +ELanguage PchLanguageToELanguage( const char *pchShortName ) +{ + Assert( ARRAYSIZE(s_LanguageNames) == k_Lang_MAX + 1 ); + if ( !pchShortName ) + return k_Lang_English; + + for ( int iLang = 0; iLang < Q_ARRAYSIZE(s_LanguageNames); ++iLang ) + { + if ( !Q_stricmp( pchShortName, s_LanguageNames[iLang].m_pchShortName ) ) + { + return s_LanguageNames[iLang].m_ELanguage; + } + } + + // default to English + return k_Lang_English; +} + + +//----------------------------------------------------------------------------- +// Purpose: return the short string name used for this language by SteamUI +//----------------------------------------------------------------------------- +const char *GetLanguageShortName( ELanguage eLang ) +{ + Assert( Q_ARRAYSIZE(s_LanguageNames) == k_Lang_MAX + 1 ); + if ( s_LanguageNames[ eLang + 1 ].m_ELanguage == eLang ) + { + return s_LanguageNames[ eLang + 1 ].m_pchShortName; + } + + Assert( !"enum ELanguage order mismatched from Language_t s_LanguageNames, fix it!" ); + return s_LanguageNames[0].m_pchShortName; +} + +//----------------------------------------------------------------------------- +// Purpose: return the short string name used for this language by SteamUI +//----------------------------------------------------------------------------- +const char *GetLanguageName( ELanguage eLang ) +{ + Assert( Q_ARRAYSIZE(s_LanguageNames) == k_Lang_MAX + 1 ); + if ( s_LanguageNames[ eLang + 1 ].m_ELanguage == eLang ) + { + return s_LanguageNames[ eLang + 1 ].m_pchName; + } + + Assert( !"enum ELanguage order mismatched from Language_t s_LanguageNames, fix it!" ); + return s_LanguageNames[0].m_pchShortName; +} + + +//----------------------------------------------------------------------------- +// Purpose: return the short string name used for this language by SteamUI +//----------------------------------------------------------------------------- +const char *GetLanguageVGUILocalization( ELanguage eLang ) +{ + Assert( Q_ARRAYSIZE(s_LanguageNames) == k_Lang_MAX + 1 ); + if ( s_LanguageNames[ eLang + 1 ].m_ELanguage == eLang ) + { + return s_LanguageNames[ eLang + 1 ].m_pchVGUILocalizationName; + } + + Assert( !"enum ELanguage order mismatched from Language_t s_LanguageNames, fix it!" ); + return s_LanguageNames[0].m_pchVGUILocalizationName; +} + diff --git a/common/language.h b/common/language.h new file mode 100644 index 0000000..db47a9e --- /dev/null +++ b/common/language.h @@ -0,0 +1,43 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: represent a canonical list of the languages we support, +// +//============================================================================= + +#ifndef LANG_H +#define LANG_H +#ifdef _WIN32 +#pragma once +#endif + +// if you change this enum also change language.cpp:s_LanguageNames +enum ELanguage +{ + k_Lang_None = -1, + k_Lang_English = 0, + k_Lang_German, + k_Lang_French, + k_Lang_Italian, + k_Lang_Korean, + k_Lang_Spanish, + k_Lang_Simplified_Chinese, + k_Lang_Traditional_Chinese, + k_Lang_Russian, + k_Lang_Thai, + k_Lang_Japanese, + k_Lang_Portuguese, + k_Lang_Polish, + k_Lang_Danish, + k_Lang_Dutch, + k_Lang_Finnish, + k_Lang_Norwegian, + k_Lang_Swedish, + k_Lang_MAX +}; + +ELanguage PchLanguageToELanguage(const char *pchShortName); +const char *GetLanguageShortName( ELanguage eLang ); +const char *GetLanguageVGUILocalization( ELanguage eLang ); +const char *GetLanguageName( ELanguage eLang ); + +#endif /* LANG_H */ diff --git a/common/matchmaking/mm_helpers.h b/common/matchmaking/mm_helpers.h new file mode 100644 index 0000000..25ed8f1 --- /dev/null +++ b/common/matchmaking/mm_helpers.h @@ -0,0 +1,92 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: common routines to operate on matchmaking sessions and members +// Assumptions: caller should include all required headers before including mm_helpers.h +// +//===========================================================================// + +#ifndef __COMMON__MM_HELPERS_H_ +#define __COMMON__MM_HELPERS_H_ +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/keyvalues.h" +#include "tier1/fmtstr.h" + + +// +// Contains inline functions to deal with common tasks involving matchmaking and sessions +// + +inline KeyValues * SessionMembersFindPlayer( KeyValues *pSessionSettings, XUID xuidPlayer, KeyValues **ppMachine = NULL ) +{ + if ( ppMachine ) + *ppMachine = NULL; + + if ( !pSessionSettings ) + return NULL; + + KeyValues *pMembers = pSessionSettings->FindKey( "Members" ); + if ( !pMembers ) + return NULL; + + int numMachines = pMembers->GetInt( "numMachines" ); + for ( int k = 0; k < numMachines; ++ k ) + { + KeyValues *pMachine = pMembers->FindKey( CFmtStr( "machine%d", k ) ); + if ( !pMachine ) + continue; + + int numPlayers = pMachine->GetInt( "numPlayers" ); + for ( int j = 0; j < numPlayers; ++ j ) + { + KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", j ) ); + if ( !pPlayer ) + continue; + + if ( pPlayer->GetUint64( "xuid" ) == xuidPlayer ) + { + if ( ppMachine ) + *ppMachine = pMachine; + + return pPlayer; + } + } + } + + return NULL; +} + +inline XUID SessionMembersFindNonGuestXuid( XUID xuid ) +{ +#ifdef _X360 + if ( !g_pMatchFramework ) + return xuid; + + if ( !g_pMatchFramework->GetMatchSession() ) + return xuid; + + KeyValues *pMachine = NULL; + KeyValues *pPlayer = SessionMembersFindPlayer( g_pMatchFramework->GetMatchSession()->GetSessionSettings(), xuid, &pMachine ); + if ( !pPlayer || !pMachine ) + return xuid; + + if ( !strchr( pPlayer->GetString( "name" ), '(' ) ) + return xuid; + + int numPlayers = pMachine->GetInt( "numPlayers" ); + for ( int k = 0; k < numPlayers; ++ k ) + { + XUID xuidOtherPlayer = pMachine->GetUint64( CFmtStr( "player%d/xuid", k ) ); + if ( xuidOtherPlayer && !strchr( pMachine->GetString( CFmtStr( "player%d/xuid", k ) ), '(' ) ) + return xuidOtherPlayer; // found a replacement that is not guest + } +#endif + + return xuid; +} + + +#endif // __COMMON__MM_HELPERS_H_ + diff --git a/common/protocol.h b/common/protocol.h new file mode 100644 index 0000000..f37a39e --- /dev/null +++ b/common/protocol.h @@ -0,0 +1,176 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// protocol.h -- communications protocols +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#ifdef _WIN32 +#pragma once +#endif + +// Used to classify entity update types in DeltaPacketEntities. +enum UpdateType +{ + EnterPVS = 0, // Entity came back into pvs, create new entity if one doesn't exist + + LeavePVS, // Entity left pvs + + DeltaEnt, // There is a delta for this entity. + PreserveEnt, // Entity stays alive but no delta ( could be LOD, or just unchanged ) + + Finished, // finished parsing entities successfully + Failed, // parsing error occured while reading entities +}; + +// Flags for delta encoding header +enum +{ + FHDR_ZERO = 0x0000, + FHDR_LEAVEPVS = 0x0001, + FHDR_DELETE = 0x0002, + FHDR_ENTERPVS = 0x0004, +}; + + + +#define INSTANCE_BASELINE_TABLENAME "instancebaseline" +#define LIGHT_STYLES_TABLENAME "lightstyles" +#define USER_INFO_TABLENAME "userinfo" +#define SERVER_STARTUP_DATA_TABLENAME "server_query_info" // the name is a remnant... + + +//#define CURRENT_PROTOCOL 1 + + +#define DELTA_OFFSET_BITS 5 +#define DELTA_OFFSET_MAX ( ( 1 << DELTA_OFFSET_BITS ) - 1 ) + +#define DELTASIZE_BITS 20 // must be: 2^DELTASIZE_BITS > (NET_MAX_PAYLOAD * 8) + +// Largest # of commands to send in a packet +#define NUM_NEW_COMMAND_BITS 4 +#define MAX_NEW_COMMANDS ((1 << NUM_NEW_COMMAND_BITS)-1) + +// Max number of history commands to send ( 2 by default ) in case of dropped packets +#define NUM_BACKUP_COMMAND_BITS 3 +#define MAX_BACKUP_COMMANDS ((1 << NUM_BACKUP_COMMAND_BITS)-1) + + +#define PROTOCOL_AUTHCERTIFICATE 0x01 // Connection from client is using a WON authenticated certificate +#define PROTOCOL_HASHEDCDKEY 0x02 // Connection from client is using hashed CD key because WON comm. channel was unreachable +#define PROTOCOL_STEAM 0x03 // Steam certificates +#define PROTOCOL_LASTVALID 0x03 // Last valid protocol + +#define CONNECTIONLESS_HEADER 0xFFFFFFFF // all OOB packet start with this sequence +#define STEAM_KEYSIZE 2048 // max size needed to contain a steam authentication key (both server and client) + +// each channel packet has 1 byte of FLAG bits +#define PACKET_FLAG_RELIABLE (1<<0) // packet contains subchannel stream data +#define PACKET_FLAG_COMPRESSED (1<<1) // packet is compressed +#define PACKET_FLAG_ENCRYPTED (1<<2) // packet is encrypted +#define PACKET_FLAG_SPLIT (1<<3) // packet is split +#define PACKET_FLAG_CHOKED (1<<4) // packet was choked by sender + +// NOTE: Bits 5, 6, and 7 are used to specify the # of padding bits at the end of the packet!!! +#define ENCODE_PAD_BITS( x ) ( ( x << 5 ) & 0xff ) +#define DECODE_PAD_BITS( x ) ( ( x >> 5 ) & 0xff ) + +// shared commands used by all streams, handled by stream layer, TODO + +#define net_NOP 0 // nop command used for padding +#define net_Disconnect 1 // disconnect, last message in connection +#define net_File 2 // file transmission message request/deny + +#define net_LastControlMessage 2 + +#define net_SplitScreenUser 3 // Changes split screen user +#define net_Tick 4 // send last world tick +#define net_StringCmd 5 // a string command +#define net_SetConVar 6 // sends one/multiple convar settings +#define net_SignonState 7 // signals current signon state + +// +// server to client +// + +#define svc_ServerInfo 8 // first message from server about game, map etc +#define svc_SendTable 9 // sends a sendtable description for a game class +#define svc_ClassInfo 10 // Info about classes (first byte is a CLASSINFO_ define). +#define svc_SetPause 11 // tells client if server paused or unpaused + + +#define svc_CreateStringTable 12 // inits shared string tables +#define svc_UpdateStringTable 13 // updates a string table + +#define svc_VoiceInit 14 // inits used voice codecs & quality +#define svc_VoiceData 15 // Voicestream data from the server + +#define svc_Print 16 // print text to console + +#define svc_Sounds 17 // starts playing sound + +#define svc_SetView 18 // sets entity as point of view +#define svc_FixAngle 19 // sets/corrects players viewangle +#define svc_CrosshairAngle 20 // adjusts crosshair in auto aim mode to lock on traget + +#define svc_BSPDecal 21 // add a static decal to the world BSP + +#define svc_SplitScreen 22 // split screen style message + +// Message from server side to client side entity +#define svc_UserMessage 23 // a game specific message +#define svc_EntityMessage 24 // a message for an entity +#define svc_GameEvent 25 // global game event fired + +#define svc_PacketEntities 26 // non-delta compressed entities + +#define svc_TempEntities 27 // non-reliable event object + +#define svc_Prefetch 28 // only sound indices for now + +#define svc_Menu 29 // display a menu from a plugin + +#define svc_GameEventList 30 // list of known games events and fields + +#define svc_GetCvarValue 31 // Server wants to know the value of a cvar on the client + +#define svc_CmdKeyValues 32 // Server submits KeyValues command for the client + +#define SVC_LASTMSG 32 // last known server messages + +// +// client to server +// + +#define clc_ClientInfo 8 // client info (table CRC etc) +#define clc_Move 9 // [CUserCmd] +#define clc_VoiceData 10 // Voicestream data from a client +#define clc_BaselineAck 11 // client acknowledges a new baseline seqnr +#define clc_ListenEvents 12 // client acknowledges a new baseline seqnr +#define clc_RespondCvarValue 13 // client is responding to a svc_GetCvarValue message. +#define clc_FileCRCCheck 14 // client is sending a file's CRC to the server to be verified. +#define clc_LoadingProgress 15 // client loading progress +#define clc_SplitPlayerConnect 16 +#define clc_CmdKeyValues 17 +#define CLC_LASTMSG 17 // last known client message + +#define RES_FATALIFMISSING (1<<0) // Disconnect if we can't get this file. +#define RES_PRELOAD (1<<1) // Load on client rather than just reserving name + +#define SIGNONSTATE_NONE 0 // no state yet, about to connect +#define SIGNONSTATE_CHALLENGE 1 // client challenging server, all OOB packets +#define SIGNONSTATE_CONNECTED 2 // client is connected to server, netchans ready +#define SIGNONSTATE_NEW 3 // just got serverinfo and string tables +#define SIGNONSTATE_PRESPAWN 4 // received signon buffers +#define SIGNONSTATE_SPAWN 5 // ready to receive entity packets +#define SIGNONSTATE_FULL 6 // we are fully connected, first non-delta packet received +#define SIGNONSTATE_CHANGELEVEL 7 // server is changing level, please wait + +#endif // PROTOCOL_H + + diff --git a/common/qlimits.h b/common/qlimits.h new file mode 100644 index 0000000..f726aea --- /dev/null +++ b/common/qlimits.h @@ -0,0 +1,35 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef QLIMITS_H +#define QLIMITS_H + +#if defined( _WIN32 ) +#pragma once +#endif + +// DATA STRUCTURE INFO + +#define MAX_NUM_ARGVS 50 + +// SYSTEM INFO +#define MAX_QPATH 96 // max length of a game pathname +#define MAX_OSPATH 260 // max length of a filesystem pathname + +#define ON_EPSILON 0.1 // point on plane side epsilon + + +// Resource counts; +#define MAX_MODEL_INDEX_BITS 10 // sent as a short +#define MAX_MODELS (1<.360. +// +//=============================================================================// + +#include "studio.h" +#include "optimize.h" +#include "phyfile.h" +#include "studiobyteswap.h" +#include "vphysics_interface.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +#undef ALIGN16 +#undef ALIGN32 +#undef ALIGN4 +#define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3) +#define ALIGN16( a ) a = (byte *)((int)((byte *)a + 15) & ~ 15) +#define ALIGN32( a ) a = (byte *)((int)((byte *)a + 31) & ~ 31) +#define ALIGN64( a ) a = (byte *)((int)((byte *)a + 63) & ~ 63) + +// Fixup macros create variables that may not be referenced +#pragma warning( push ) +#pragma warning( disable:4189 ) // local variable is initialized but not referenced +#pragma warning( disable:4366 ) // The result of the unary '&' operator may be unaligned + +namespace StudioByteSwap +{ + +static bool g_bVerbose = true; +static bool g_bNativeSrc; +static CByteswap g_Swap; +static IPhysicsCollision *pCollision; +static CompressFunc_t g_pCompressFunc; +static const char *g_pFilename; + +void ActivateByteSwapping( bool activate ) +{ + g_Swap.ActivateByteSwapping( activate ); + SourceIsNative( IsPC() ); +} + +void SourceIsNative( bool bNative ) +{ + g_bNativeSrc = bNative; +} + +void SetCollisionInterface( IPhysicsCollision *pPhysicsCollision ) +{ + pCollision = pPhysicsCollision; +} + +void SetVerbose( bool bVerbose ) +{ + g_bVerbose = bVerbose; +} + +//---------------------------------------------------------------------- +// Helper to write a chunk of objects of the same type, and increment the buffer pointers. +//---------------------------------------------------------------------- +template inline void WriteObjects( byte **pOutputBuffer, byte **pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, *pBaseData, sizeof(T) ); + g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) ); + *pOutputBuffer += sizeof(T); + *pBaseData += sizeof(T); + } +} + +//---------------------------------------------------------------------- +// Helper to write a chunk of objects of the same type, and increment the buffer pointers. +//---------------------------------------------------------------------- +template inline void WriteObjects( T **pOutputBuffer, T **pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, *pBaseData, sizeof(T) ); + g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) ); + ++*pOutputBuffer; + ++*pBaseData; + } +} + +//---------------------------------------------------------------------- +// Helper to write a chunk of objects of the same type. +//---------------------------------------------------------------------- +template inline void WriteObjects( byte *pOutputBuffer, byte *pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, pBaseData, sizeof(T) ); + g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) ); + pOutputBuffer += sizeof(T); + pBaseData += sizeof(T); + } +} + +//---------------------------------------------------------------------- +// Helper to write a chunk of objects of the same type. +//---------------------------------------------------------------------- +template inline void WriteObjects( T *pOutputBuffer, T *pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, pBaseData, sizeof(T) ); + g_Swap.SwapFieldsToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) ); + ++pOutputBuffer; + ++pBaseData; + } +} + +//---------------------------------------------------------------------- +// Helper to write a buffer of some integral type, and increment the buffer pointers. +//---------------------------------------------------------------------- +template inline void WriteBuffer( byte **pOutputBuffer, byte **pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, *pBaseData, sizeof(T) ); + g_Swap.SwapBufferToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( *pOutputBuffer, &tempObject, sizeof(T) ); + *pOutputBuffer += sizeof(T); + *pBaseData += sizeof(T); + } +} + +//---------------------------------------------------------------------- +// Helper to write a buffer of some integral type +//---------------------------------------------------------------------- +template inline void WriteBuffer( byte *pOutputBuffer, byte *pBaseData, int objectCount = 1 ) +{ + T tempObject; + for ( int i = 0; i < objectCount; ++i ) + { + Q_memcpy( &tempObject, pBaseData, sizeof(T) ); + g_Swap.SwapBufferToTargetEndian( &tempObject, &tempObject ); + Q_memcpy( pOutputBuffer, &tempObject, sizeof(T) ); + pOutputBuffer += sizeof(T); + pBaseData += sizeof(T); + } +} + +//---------------------------------------------------------------------- +// For getting values in the correct source/dest endian format +//---------------------------------------------------------------------- +template< class T > +T SrcNative( T *idx ) +{ + T ret = *idx; + if ( !g_bNativeSrc ) + { + g_Swap.SwapBuffer( &ret, idx ); + } + return ret; +} + +template< class T > +T DestNative( T *idx ) +{ + T ret = *idx; + if ( g_bNativeSrc ) + { + g_Swap.SwapBuffer( &ret, idx ); + } + return ret; +} + +//---------------------------------------------------------------------- +// Declares objects pointers for src/dest buffer. +// Because the declared pointers and even the base are likely to get +// moved around by WriteObjects(), also stores off a copy of declared +// pointer with a _Debug suffix -- purely for ease of watch window. +//---------------------------------------------------------------------- +#define DECLARE_OBJECT_POINTERS( objPtr, base, type ) \ + type* objPtr##Src = (type*)base##Src; type* objPtr##Src_Debug = objPtr##Src; \ + type* objPtr##Dest = (type*)base##Dest; type* objPtr##Dest_Debug = objPtr##Dest; \ + type* objPtr = objPtr##Src; + +//---------------------------------------------------------------------- +// Same as DECLARE_OBJECT_POINTERS, but reuses existing type pointers. +//---------------------------------------------------------------------- +#define SET_OBJECT_POINTERS( objPtr, base, type ) \ + objPtr##Src = (type*)base##Src; \ + objPtr##Dest = (type*)base##Dest; \ + objPtr = objPtr##Src; + +//---------------------------------------------------------------------- +// Declares src/dest byte pointers and sets them to some index offset in the buffers. +//---------------------------------------------------------------------- +#define DECLARE_INDEX_POINTERS( ptr, base, index ) \ + byte *ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \ + byte *ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index ); + +//---------------------------------------------------------------------- +// Validates that the src pointer is DWORD aligned +//---------------------------------------------------------------------- +#define DECLARE_INDEX_POINTERS_VALIDATE( ptr, base, index ) \ + DECLARE_INDEX_POINTERS( ptr, base, index ) \ + VALIDATE_ALIGNMENT( ptr##Src ) + +//---------------------------------------------------------------------- +// Same as DECLARE_INDEX_POINTERS, but reuses existing byte pointers. +//---------------------------------------------------------------------- +#define SET_INDEX_POINTERS( ptr, base, index ) \ + ptr##Src = (byte*)base##Src + SrcNative( &base##Src->index ); \ + ptr##Dest = (byte*)base##Dest + SrcNative( &base##Src->index ); + +//---------------------------------------------------------------------- +// Validates that the src pointer is DWORD aligned +//---------------------------------------------------------------------- +#define SET_INDEX_POINTERS_VALIDATE( ptr, base, index ) \ + SET_INDEX_POINTERS( ptr, base, index ) \ + VALIDATE_ALIGNMENT( ptr##Src ) + +//---------------------------------------------------------------------- +// Same as DECLARE_INDEX_POINTERS, but reuses existing byte pointers. +//---------------------------------------------------------------------- +#define VALIDATE_ALIGNMENT( ptr ) \ + { \ + byte *ptr##aligned = (byte*)ptr; \ + ALIGN4( ptr##aligned ); \ + if ( ptr##aligned != (byte*)ptr ) \ + { \ + Warning( "Studiobyteswap line %d: Misaligned data found in %s\n", __LINE__, g_pFilename ); \ + return ERROR_MISALIGNED_DATA; \ + } \ + } + +//---------------------------------------------------------------------- +// for() loop header, updates all three object pointers (src,dest,native) +//---------------------------------------------------------------------- +#define ITERATE_BLOCK( objPtr, count ) \ + for ( int objPtr##_idx = 0; objPtr##_idx < SrcNative( &count ); ++objPtr##_idx, ++objPtr, ++objPtr##Src, ++objPtr##Dest ) + + +// Fake header declaration for easier phy swapping +struct swapcompactsurfaceheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int size; + int vphysicsID; + short version; + short modelType; + int surfaceSize; + Vector dragAxisAreas; + int axisMapSize; +}; + +BEGIN_BYTESWAP_DATADESC( swapcompactsurfaceheader_t ) + DEFINE_FIELD( size, FIELD_INTEGER ), + DEFINE_FIELD( vphysicsID, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_SHORT ), + DEFINE_FIELD( modelType, FIELD_SHORT ), + DEFINE_FIELD( surfaceSize, FIELD_INTEGER ), + DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ), + DEFINE_FIELD( axisMapSize, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +// Fake header declaration for old style phy format +#if defined( _X360 ) +#pragma bitfield_order( push, lsb_to_msb ) +#endif +struct legacysurfaceheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int size; + float mass_center[3]; + float rotation_inertia[3]; + float upper_limit_radius; + BEGIN_BITFIELD( bf ) + int max_deviation : 8; + int byte_size : 24; + END_BITFIELD() + int offset_ledgetree_root; + int dummy[3]; +}; +#if defined( _X360 ) +#pragma bitfield_order( pop ) +#endif + +BEGIN_BYTESWAP_DATADESC( legacysurfaceheader_t ) + DEFINE_FIELD( size, FIELD_INTEGER ), + DEFINE_ARRAY( mass_center, FIELD_FLOAT, 3 ), + DEFINE_ARRAY( rotation_inertia, FIELD_FLOAT, 3 ), + DEFINE_FIELD( upper_limit_radius, FIELD_FLOAT ), + DEFINE_BITFIELD( bf, FIELD_INTEGER, 32 ), + DEFINE_FIELD( offset_ledgetree_root, FIELD_INTEGER ), + DEFINE_ARRAY( dummy, FIELD_INTEGER, 3 ), +END_BYTESWAP_DATADESC() + +//---------------------------------------------------------------------- +// Swap a .phy file +//---------------------------------------------------------------------- +int ByteswapPHY( void *pDestBase, int destBaseSize, const void *pSrcBase, const int fileSize ) +{ + Assert( pCollision ); + if ( !pCollision ) + return 0; + + Q_memset( pDestBase, 0, fileSize ); + + byte *pSrc = (byte*)pSrcBase; + byte *pDest = (byte*)pDestBase; + vcollide_t collide = {0}; + + // file header + phyheader_t *pHdr = (phyheader_t*)( g_bNativeSrc ? pSrc : pDest ); + WriteObjects( &pDest, &pSrc ); + + if ( g_bNativeSrc ) + { + // Reset the pointers and let ivp swap the binary physics data + pSrc = (byte*)pSrcBase + pHdr->size; + pDest = (byte*)pDestBase + pHdr->size; + + int bufSize = fileSize - pHdr->size; + pCollision->VCollideLoad( &collide, pHdr->solidCount, (const char *)pSrc, bufSize, false ); + } + + // Swap the collision data headers + for ( int i = 0; i < pHdr->solidCount; ++i ) + { + swapcompactsurfaceheader_t *baseHdr = (swapcompactsurfaceheader_t*)( g_bNativeSrc ? pSrc : pDest ); + WriteObjects( pDest, pSrc ); + + int srcIncrement = baseHdr->surfaceSize + sizeof(swapcompactsurfaceheader_t); + int destIncrement = srcIncrement; + bool bCopyToSrc = !g_bNativeSrc; + + if ( baseHdr->vphysicsID != MAKEID('V','P','H','Y') ) + { + // May be old phy format + legacysurfaceheader_t *legacyHdr = (legacysurfaceheader_t*)( g_bNativeSrc ? pSrc : pDest ); + WriteObjects( pDest, pSrc ); + if ( legacyHdr->dummy[2] == MAKEID('I','V','P','S') || legacyHdr->dummy[2] == 0 ) + { + srcIncrement = legacyHdr->byte_size + sizeof(int); + destIncrement = legacyHdr->byte_size + sizeof(swapcompactsurfaceheader_t); + bCopyToSrc = false; + + if ( !g_bNativeSrc ) + { + // src needs the size member to be native to load vcollides + Q_memcpy( pSrc, pDest, sizeof(int) ); + } + } + else + { + // Not recognized + Assert(0); + return 0; + } + } + + if ( bCopyToSrc ) + { + // src needs the native header data to load the vcollides + Q_memcpy( pSrc, pDest, sizeof(swapcompactsurfaceheader_t) ); + } + + pSrc += srcIncrement; + pDest += destIncrement; + } + + // the rest of the file is text + int currPos = pSrc - (byte*)pSrcBase; + int remainingBytes = fileSize - currPos; + WriteBuffer( &pDest, &pSrc, remainingBytes ); + + if ( !g_bNativeSrc ) + { + // let ivp swap the ledge tree + pSrc = (byte*)pSrcBase + pHdr->size; + int bufSize = fileSize - pHdr->size; + pCollision->VCollideLoad( &collide, pHdr->solidCount, (const char *)pSrc, bufSize, true ); + } + + // Write out the ledge tree data + pDest = (byte*)pDestBase + pHdr->size; + for ( int i = 0; i < collide.solidCount; ++i ) + { + // skip over the size + pDest += sizeof(int); + int offset = pCollision->CollideWrite( (char*)pDest, collide.solids[i], g_bNativeSrc ); + int destSize = g_bNativeSrc ? SwapLong( offset ) : offset; + Q_memcpy( pDest - sizeof(int), &destSize, sizeof(int) ); + pDest += offset; + } + + // Free the memory + pCollision->VCollideUnload( &collide ); + + int newFileSize = pDest - (byte*)pDestBase + remainingBytes; + + if ( g_pCompressFunc ) + { + // compress entire swapped PHY + void *pInput = pDestBase; + int inputSize = newFileSize; + void *pOutput; + int outputSize; + if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) ) + { + // put the compressed version in its place + V_memcpy( pDestBase, pOutput, outputSize ); + free( pOutput ); + newFileSize = outputSize; + } + } + + return newFileSize; +} + +//---------------------------------------------------------------------- +// Swap a .vvd file +//---------------------------------------------------------------------- +int ByteswapVVD( void *pDestBase, int destBaseSize, const void *pSrcBase, const int fileSize ) +{ + Q_memset( pDestBase, 0, fileSize ); + + byte *pDataSrc = (byte*)pSrcBase; + byte *pDataDest = (byte*)pDestBase; + + /** FILE HEADER **/ + + DECLARE_OBJECT_POINTERS( pHdr, pData, vertexFileHeader_t ) + WriteObjects( &pDataDest, &pDataSrc ); + + /** FIXUP TABLE **/ + + SET_INDEX_POINTERS( pData, pHdr, fixupTableStart ) + WriteObjects( &pDataDest, &pDataSrc, SrcNative( &pHdr->numFixups ) ); + + /** VERTEX DATA **/ + + SET_INDEX_POINTERS( pData, pHdr, vertexDataStart ) + WriteObjects( &pDataDest, &pDataSrc, SrcNative( &pHdr->numLODVertexes[0] ) ); + + /** TANGENT DATA **/ + + if ( pHdr->tangentDataStart != 0 ) + { + SET_INDEX_POINTERS( pData, pHdr, tangentDataStart ) + WriteBuffer( &pDataDest, &pDataSrc, 4 * SrcNative( &pHdr->numLODVertexes[0] ) ); + } + + int newFileSize = pDataDest - (byte*)pDestBase; + + if ( g_pCompressFunc ) + { + void *pInput = (byte*)pDestBase + sizeof( vertexFileHeader_t ); + int inputSize = newFileSize - sizeof( vertexFileHeader_t ); + void *pOutput; + int outputSize; + if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) ) + { + // place the compressed data after the header + V_memcpy( pInput, pOutput, outputSize ); + free( pOutput ); + newFileSize = sizeof( vertexFileHeader_t ) + outputSize; + } + } + + return newFileSize; +} + + +//---------------------------------------------------------------------- +// Swap a .vtx file +//---------------------------------------------------------------------- +int ByteswapVTX( void *pDestBase, int destBaseSize, const void *pSrcBase, const int fileSize ) +{ + Q_memset( pDestBase, 0, fileSize ); + + // Do a straight copy first so the string table is transferred + memcpy( pDestBase, pSrcBase, fileSize ); + + // Start writing the file + byte *pDataSrc = (byte*)pSrcBase; + byte *pDataDest = (byte*)pDestBase; + + DECLARE_OBJECT_POINTERS( pVtxHeader, pData, OptimizedModel::FileHeader_t ) + + if ( pVtxHeader->version != OPTIMIZED_MODEL_FILE_VERSION ) + { + return ERROR_VERSION; + } + + WriteObjects( pVtxHeaderDest, pVtxHeaderSrc ); + + /** BODY PARTS **/ + + SET_INDEX_POINTERS( pData, pVtxHeader, bodyPartOffset ) + DECLARE_OBJECT_POINTERS( pBodyPartHeader, pData, OptimizedModel::BodyPartHeader_t ) + ITERATE_BLOCK( pBodyPartHeader, pVtxHeader->numBodyParts ) + { + WriteObjects( pBodyPartHeaderDest, pBodyPartHeaderSrc ); + + /** MODELS **/ + + SET_INDEX_POINTERS( pData, pBodyPartHeader, modelOffset ) + DECLARE_OBJECT_POINTERS( pModelHeader, pData, OptimizedModel::ModelHeader_t ) + ITERATE_BLOCK( pModelHeader, pBodyPartHeader->numModels ) + { + WriteObjects( pModelHeaderDest, pModelHeaderSrc ); + + /** MODEL LODS **/ + + unsigned int meshOffset = 0; + SET_INDEX_POINTERS( pData, pModelHeader, lodOffset ) + DECLARE_OBJECT_POINTERS( pModelLODHeader, pData, OptimizedModel::ModelLODHeader_t ) + ITERATE_BLOCK( pModelLODHeader, pModelHeader->numLODs ) + { + WriteObjects( pModelLODHeaderDest, pModelLODHeaderSrc ); + + /** MESHES **/ + + unsigned int prevOffset = meshOffset; + meshOffset = SrcNative( &pModelLODHeader->meshOffset ); + if ( prevOffset - sizeof(OptimizedModel::ModelLODHeader_t) == meshOffset ) + { + // This LOD shares data with the previous LOD - don't reswap. + continue; + } + + SET_INDEX_POINTERS( pData, pModelLODHeader, meshOffset ) + DECLARE_OBJECT_POINTERS( pMeshHeader, pData, OptimizedModel::MeshHeader_t ) + ITERATE_BLOCK( pMeshHeader, pModelLODHeader->numMeshes ) + { + WriteObjects( pMeshHeaderDest, pMeshHeaderSrc ); + + /** STRIP GROUPS **/ + + SET_INDEX_POINTERS( pData, pMeshHeader, stripGroupHeaderOffset ) + DECLARE_OBJECT_POINTERS( pStripGroupHeader, pData, OptimizedModel::StripGroupHeader_t ) + ITERATE_BLOCK( pStripGroupHeader, pMeshHeader->numStripGroups ) + { + WriteObjects( pStripGroupHeaderDest, pStripGroupHeaderSrc ); + + /** STRIP VERTS **/ + + SET_INDEX_POINTERS( pData, pStripGroupHeader, vertOffset ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pStripGroupHeader->numVerts ) ); + + /** VERT INDICES **/ + + SET_INDEX_POINTERS( pData, pStripGroupHeader, indexOffset ) + WriteBuffer( pDataDest, pDataSrc, SrcNative( &pStripGroupHeader->numIndices ) ); + + /** STRIPS **/ + + SET_INDEX_POINTERS( pData, pStripGroupHeader, stripOffset ) + DECLARE_OBJECT_POINTERS( pStripHeader, pData, OptimizedModel::StripHeader_t ) + ITERATE_BLOCK( pStripHeader, pStripGroupHeader->numStrips ) + { + WriteObjects( pStripHeaderDest, pStripHeaderSrc ); + + /** BONE STATE CHANGES **/ + + SET_INDEX_POINTERS( pData, pStripHeader, boneStateChangeOffset ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pStripHeader->numBoneStateChanges ) ); + } + } + } + } + } + } + + /** MATERIAL REPLACEMENT HEADERS **/ + + SET_INDEX_POINTERS( pData, pVtxHeader, materialReplacementListOffset ) + DECLARE_OBJECT_POINTERS( pMatRepListHeader, pData, OptimizedModel::MaterialReplacementListHeader_t ) + ITERATE_BLOCK( pMatRepListHeader, pVtxHeader->numLODs ) + { + WriteObjects( pMatRepListHeaderDest, pMatRepListHeaderSrc ); + + /** MATERIAL REPLACEMENTS **/ + + SET_INDEX_POINTERS( pData, pMatRepListHeader, replacementOffset ) + WriteObjects( &pDataDest, &pDataSrc, SrcNative( &pMatRepListHeader->numReplacements ) ); + } + + int newFileSize = fileSize; + + if ( g_pCompressFunc ) + { + void *pInput = (byte*)pDestBase + sizeof( OptimizedModel::FileHeader_t ); + int inputSize = fileSize - sizeof( OptimizedModel::FileHeader_t ); + void *pOutput; + int outputSize; + if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) ) + { + // place the compressed data after the header + V_memcpy( pInput, pOutput, outputSize ); + free( pOutput ); + newFileSize = sizeof( OptimizedModel::FileHeader_t ) + outputSize; + } + } + + return newFileSize; +} + +//---------------------------------------------------------------------- +// Swap RLE animation data (found inside .mdl and .ani) +//---------------------------------------------------------------------- +void ByteswapRLEAnimData( mstudioanimdesc_t *pAnimDesc, int section, byte *&pDataSrc, byte *&pDataDest ) +{ + /** ANIMATIONS **/ + DECLARE_OBJECT_POINTERS( pAnimation, pData, mstudio_rle_anim_t ) + WriteObjects( pAnimationDest, pAnimationSrc ); + if ( pAnimation->bone == 255 ) + { + // No animation data + pAnimation = 0; + } + + while( pAnimation ) + { + if ( pAnimation->flags & ( STUDIO_ANIM_RAWROT | STUDIO_ANIM_RAWPOS | STUDIO_ANIM_RAWROT2 ) ) + { + if ( pAnimation->flags & STUDIO_ANIM_RAWROT ) + { + int offset = (byte*)pAnimation->pQuat48() - (byte*)pAnimation; + pDataSrc = (byte*)pAnimationSrc + offset; + pDataDest = (byte*)pAnimationDest + offset; + + // Write the quaternion (bit fields contained in 3 unsigned shorts) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + + if ( pAnimation->flags & STUDIO_ANIM_RAWROT2 ) + { + int offset = (byte*)pAnimation->pQuat64() - (byte*)pAnimation; + pDataSrc = (byte*)pAnimationSrc + offset; + pDataDest = (byte*)pAnimationDest + offset; + + // Write the quaternion (bit fields contained in 1 64 bit int + WriteBuffer( &pDataDest, &pDataSrc, 1 ); + } + + if ( pAnimation->flags & STUDIO_ANIM_RAWPOS ) + { + int offset = (byte*)pAnimation->pPos() - (byte*)pAnimation; + pDataSrc = (byte*)pAnimationSrc + offset; + pDataDest = (byte*)pAnimationDest + offset; + + // Write the vector (3 float16) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + } + else + { + int offset = (byte*)pAnimation->pRotV() - (byte*)pAnimation; + pDataSrc = (byte*)pAnimationSrc + offset; + pDataDest = (byte*)pAnimationDest + offset; + + mstudioanim_valueptr_t *rotvptr = (mstudioanim_valueptr_t*)pDataSrc; + WriteObjects( &pDataDest, &pDataSrc ); + + int animValueCt = 0; + for ( int idx = 0; idx < 3; ++idx ) + { + animValueCt += rotvptr->offset[idx] ? 1 : 0; + } + + if ( pAnimation->flags & STUDIO_ANIM_ANIMPOS ) + { + int offset = (byte*)pAnimation->pPosV() - (byte*)pAnimation; + pDataSrc = (byte*)pAnimationSrc + offset; + pDataDest = (byte*)pAnimationDest + offset; + + mstudioanim_valueptr_t *posvptr = (mstudioanim_valueptr_t*)pDataSrc; + WriteObjects( &pDataDest, &pDataSrc ); + + for ( int idx = 0; idx < 3; ++idx ) + { + animValueCt += posvptr->offset[idx] ? 1 : 0; + } + } + + // Write position and rotation animations + + // Note: destanimvalue_t is a union that can be either two bytes or a short. + // This structure is used to compress animation data using RLE. + // The first object of a chunk acts as the header, and uses the two bytes to + // store how many objects follow, and how many frames are encoded by them. + // The objects that follow use the short to store a value. + // The total number of chunks has been determined by counting the number of valid (non-zero) offsets. + for ( int animValue = 0; animValue < animValueCt; ++animValue ) + { + int encodedFrames = 0; + int totalFrames = SrcNative( &pAnimDesc->numframes ); + int sectionFrames = SrcNative( &pAnimDesc->sectionframes ); + if ( sectionFrames ) + { + int iStartFrame = section * sectionFrames; + int iEndFrame = (section + 1) * sectionFrames; + + iStartFrame = MIN( iStartFrame, totalFrames - 1 ); + iEndFrame = MIN( iEndFrame, totalFrames - 1 ); + + totalFrames = iEndFrame - iStartFrame + 1; + } + + while ( encodedFrames < totalFrames ) + { + // Write the first animation value (struct of 2 bytes) + mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pDataSrc : pDataDest ); + WriteBuffer( &pDataDest, &pDataSrc, 2 ); + + // Write the remaining animation values from this group (shorts) + WriteBuffer( &pDataDest, &pDataSrc, pDestAnimvalue->num.valid ); + + encodedFrames += pDestAnimvalue->num.total; + } + } + } + + if ( pAnimation->nextoffset ) + { + // Set pointers to the next animation + pAnimationSrc = (mstudio_rle_anim_t*)( (byte*)pAnimationSrc + SrcNative( &pAnimation->nextoffset ) ); + pAnimationDest = (mstudio_rle_anim_t*)( (byte*)pAnimationDest + SrcNative( &pAnimation->nextoffset ) ); + pAnimation = pAnimationSrc; + + // Swap the next animation + WriteObjects( pAnimationDest, pAnimationSrc ); + } + else + { + pAnimation = 0; + pDataSrc += sizeof( mstudio_rle_anim_t ); + pDataDest += sizeof( mstudio_rle_anim_t ); + } + } + + ALIGN4( pDataSrc ); + ALIGN4( pDataDest ); +} + + +//---------------------------------------------------------------------- +// Swap Frame Animation data (found inside .mdl and .ani) +//---------------------------------------------------------------------- + +void ByteswapFrameAnimData( studiohdr_t *&pHdrSrc, mstudioanimdesc_t *pAnimDesc, int section, byte *&pDataSrc, byte *&pDataDest ) +{ + DECLARE_OBJECT_POINTERS( pFrame, pData, mstudio_frame_anim_t ); + + /** FRAME ANIMATION BLOCK **/ + WriteObjects( &pDataDest, &pDataSrc ); + + /** FLAGS **/ + byte *pBoneFlags = pDataSrc; + WriteBuffer( &pDataDest, &pDataSrc, pHdrSrc->numbones ); + + /** CONSTANTS **/ + SET_INDEX_POINTERS( pData, pFrame, constantsoffset ); + // printf("%s:\n", pAnimDesc->pszName() ); + for (int j = 0; j < pHdrSrc->numbones; j++) + { + if (pBoneFlags[j] & STUDIO_FRAME_RAWROT ) + { + Quaternion q = *(Quaternion48 *)(pDataSrc); + // printf(" %7.4f %7.4f %7.4f %7.4f\n", q.x, q.y, q.z, q.w ); + // Write the quaternion (bit fields contained in 3 unsigned shorts) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + + if (pBoneFlags[j] & STUDIO_FRAME_RAWPOS ) + { + // Write the vector (3 float16) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + } + + /** FRAME ANIMATIONS **/ + SET_INDEX_POINTERS( pData, pFrame, frameoffset ); + int numFrames = pAnimDesc->numframes; + if ( pAnimDesc->sectionframes ) + { + int iStartFrame = MIN( section * pAnimDesc->sectionframes, pAnimDesc->numframes - 1 ); + int iEndFrame = MIN( (section + 1) * pAnimDesc->sectionframes, pAnimDesc->numframes - 1 ); + + numFrames = iEndFrame - iStartFrame + 1; + } + + for (int iFrame = 0; iFrame < numFrames; iFrame++) + { + for (int j = 0; j < pHdrSrc->numbones; j++) + { + if (pBoneFlags[j] & STUDIO_FRAME_ANIMROT ) + { + Quaternion q = *(Quaternion48 *)(pDataSrc); + // printf(" %7.4f %7.4f %7.4f %7.4f\n", q.x, q.y, q.z, q.w ); + // Write the quaternion (bit fields contained in 3 unsigned shorts) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + + if (pBoneFlags[j] & STUDIO_FRAME_ANIMPOS ) + { + // Write the vector (3 float16) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + else if (pBoneFlags[j] & STUDIO_FRAME_FULLANIMPOS ) + { + // printf("%.1f %.1f %.1f\n", ((float *)pDataSrc)[0], ((float *)pDataSrc)[1], ((float *)pDataSrc)[2] ); + // Write the vector (3 float) + WriteBuffer( &pDataDest, &pDataSrc, 3 ); + } + } + } + + ALIGN4( pDataSrc ); + ALIGN4( pDataDest ); +} + + +//---------------------------------------------------------------------- +// Swap IKRules (found inside .mdl and .ani) +//---------------------------------------------------------------------- +int ByteswapIKRules( studiohdr_t *&pHdrSrc, int numikrules, int numFrames, byte *&pDataSrc, byte *&pDataDest, const int fileSize ) +{ + DECLARE_OBJECT_POINTERS( pIKRule, pData, mstudioikrule_t ) + + ITERATE_BLOCK( pIKRule, numikrules ) + { + WriteObjects( pIKRuleDest, pIKRuleSrc ); + + /** IK ERROR KEYS **/ + + // Calculate the number of ikerrors by converting the ikerror start and end float values to + // frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()). + float start = floorf( SrcNative( &pIKRule->start ) * (numFrames - 1) + 0.5f ); + float end = floorf( SrcNative( &pIKRule->end ) * (numFrames - 1) + 0.5f ); + int totalerror = (int)( end - start + 1 ); + if ( end >= numFrames ) + totalerror += 2; + + // Uncompressed - only found in some older models (shipped hl2) + if ( pIKRule->ikerrorindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pIKRule, ikerrorindex ) + WriteObjects( pDataDest, pDataSrc, totalerror ); + } + + // Compressed - all models since hl2 + if ( pIKRule->compressedikerrorindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pIKRule, compressedikerrorindex ) + WriteObjects( pDataDest, pDataSrc ); + + mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc; + + // Write the animvalues. + for ( int idx = 0; idx < 6; ++idx ) + { + if ( pCompressed->offset[idx] ) + { + byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] ); + byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] ); + + int numerror = 0; + while ( numerror < totalerror ) + { + // Write the first animation value (struct of 2 bytes) + mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest ); + WriteBuffer( &pAnimvalueDest, &pAnimvalueSrc, 2 ); + + // Write the remaining animation values from this group (shorts) + WriteBuffer( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid ); + + numerror += pDestAnimvalue->num.total; + } + } + } + + if ( pIKRule->szattachmentindex ) + { + SET_INDEX_POINTERS( pData, pIKRule, szattachmentindex ) + int size = strlen( (char*)pDataSrc ) + 1; + WriteBuffer( pDataDest, pDataSrc, size ); + } + } + } + + // successful swap + return fileSize; +} + +//---------------------------------------------------------------------- +// Swap IKErrors (found inside .mdl and .ani) +//---------------------------------------------------------------------- +int ByteswapIKErrors( byte *&pDataSrc, byte *&pDataDest, int numlocalhierarchy, int numFrames, int fileSize ) +{ + DECLARE_OBJECT_POINTERS( pLocalHierarchy, pData, mstudiolocalhierarchy_t ) + + // NOTE: The ITERATE_BLOCK macro assumes non-native endian data as input, + // but in this one case we know the function parameters are already native. + // So pre-swap numlocalhierarchy here to non-native so ITERATE_BLOCK gets the input data in the expected format. + int numlocalhierarchy_nonnative = SrcNative( &numlocalhierarchy ); + + ITERATE_BLOCK( pLocalHierarchy, numlocalhierarchy_nonnative ) + { + WriteObjects( pLocalHierarchyDest, pLocalHierarchySrc, numlocalhierarchy ); + + /** COMPRESSED IK ERRORS **/ + + if ( pLocalHierarchy->localanimindex != 0 ) + { + // Calculate the number of ikerrors by converting the ikerror start and end float values to + // frame numbers. (See the generation of these values in simplify.cpp: ProcessIKRules()). + float start = floorf( SrcNative( &pLocalHierarchy->start ) * (numFrames - 1) + 0.5f ); + float end = floorf( SrcNative( &pLocalHierarchy->end ) * (numFrames - 1) + 0.5f ); + int totalerror = (int)( end - start + 1 ); + if ( end >= numFrames ) + totalerror += 2; + + SET_INDEX_POINTERS( pData, pLocalHierarchy, localanimindex ) + WriteObjects( pDataDest, pDataSrc ); + + mstudiocompressedikerror_t *pCompressed = (mstudiocompressedikerror_t *)pDataSrc; + + // Write the animvalues. + for ( int idx = 0; idx < 6; ++idx ) + { + if ( pCompressed->offset[idx] ) + { + byte *pAnimvalueSrc = pDataSrc + SrcNative( &pCompressed->offset[idx] ); + byte *pAnimvalueDest = pDataDest + SrcNative( &pCompressed->offset[idx] ); + + int numerror = 0; + while ( numerror < totalerror ) + { + // Write the first animation value (struct of 2 bytes) + mstudioanimvalue_t *pDestAnimvalue = (mstudioanimvalue_t*)( g_bNativeSrc ? pAnimvalueSrc : pAnimvalueDest ); + WriteBuffer( &pAnimvalueDest, &pAnimvalueSrc, 2 ); + + // Write the remaining animation values from this group (shorts) + WriteBuffer( &pAnimvalueDest, &pAnimvalueSrc, pDestAnimvalue->num.valid ); + + numerror += pDestAnimvalue->num.total; + } + } + } + } + } // Local Hierarchy block + + // successful swap + return fileSize; +} + + +//---------------------------------------------------------------------- +// Swap an .ani file +//---------------------------------------------------------------------- +int ByteswapANI( studiohdr_t* pHdr, void *pDestBase, int destBaseSize, const void *pSrcBase, const int fileSize ) +{ + // Note, pHdr came from a native .mdl - + // so the header, animdescs and animblocks are already in native format. + Assert( pHdr ); + if ( !pHdr ) + return false; + + Q_memset( pDestBase, 0, fileSize ); + + // swap file header + { + byte *pHeaderSrc = (byte *)pSrcBase; + byte *pHeaderDest = (byte *)pDestBase; + DECLARE_OBJECT_POINTERS( pAniHeader, pHeader, studiohdr_t ) + WriteObjects( pAniHeaderDest, pAniHeaderSrc ); + } + + studiohdr_t *pHdrSrc = pHdr; + + // The animdesc_t header is always contained in the mdl file, but its data may be in + // the mdl or the ani. When the data is contained in the mdl, the animdesc index fields + // represent offsets from the location of the animdesc header. When the data is in the ani, + // the index fields contain offsets from the start of the animblock in which the animdesc data is contained. + + mstudioanimdesc_t *pAnimDesc = pHdr->pLocalAnimdesc( 0 ); + for ( int i = 0; i < pHdr->numlocalanim; ++i, ++pAnimDesc ) + { + // printf("anim %d : %d : %d\n", i, pAnimDesc->animblock, pAnimDesc->sectionframes ); + if ( pAnimDesc->animblock == -1) + { + // out of date model format + continue; + } + + if ( pAnimDesc->animblock == 0 && pAnimDesc->sectionframes == 0) + { + // already saved out + continue; + } + + if ( pAnimDesc->sectionframes == 0 ) + { + mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock ); + + // printf("block %d : start %d + %d\n", pAnimDesc->animblock, pAnimBlock->datastart, pAnimDesc->animindex ); + // Base address of the animblock + byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart; + byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart; + + // Base address of the animation in the animblock + byte *pDataSrc = pBlockBaseSrc + pAnimDesc->animindex; + byte *pDataDest = pBlockBaseDest + pAnimDesc->animindex; + VALIDATE_ALIGNMENT( pDataSrc ) + + if ( pAnimDesc->flags & STUDIO_FRAMEANIM ) + { + ByteswapFrameAnimData( pHdrSrc, pAnimDesc, 0, pDataSrc, pDataDest ); + } + else + { + ByteswapRLEAnimData( pAnimDesc, 0, pDataSrc, pDataDest ); + } + } + else + { + int numsections = pAnimDesc->numframes / pAnimDesc->sectionframes + 2; + + for ( int i = 0; i < numsections; ++i ) + { + int block = pAnimDesc->pSection( i )->animblock; + int index = pAnimDesc->pSection( i )->animindex; + + if ( block != 0 ) + { + // printf("%s %d %d\n", pAnimDesc->pszName(), block, index ); + + mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( block ); + + // Base address of the animblock + byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart; + byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart; + VALIDATE_ALIGNMENT( pBlockBaseSrc ) + + // Base address of the animation in the animblock + byte *pDataSrc = pBlockBaseSrc + index; + byte *pDataDest = pBlockBaseDest + index; + + if ( pAnimDesc->flags & STUDIO_FRAMEANIM ) + { + ByteswapFrameAnimData( pHdrSrc, pAnimDesc, i, pDataSrc, pDataDest ); + } + else + { + ByteswapRLEAnimData( pAnimDesc, i, pDataSrc, pDataDest ); + } + } + } + } + + if ( pAnimDesc->animblock == 0) + { + // already saved out + continue; + } + + mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( pAnimDesc->animblock ); + // Base address of the animblock + byte *pBlockBaseSrc = (byte*)pSrcBase + pAnimBlock->datastart; + byte *pBlockBaseDest = (byte*)pDestBase + pAnimBlock->datastart; + VALIDATE_ALIGNMENT( pBlockBaseSrc ) + + // Base address of the animation in the animblock + byte *pDataSrc = pBlockBaseSrc + pAnimDesc->animindex; + byte *pDataDest = pBlockBaseDest + pAnimDesc->animindex; + VALIDATE_ALIGNMENT( pDataSrc ) + + /** IK RULES **/ + + if ( pAnimDesc->animblockikruleindex ) + { + pDataSrc = (byte*)pBlockBaseSrc + pAnimDesc->animblockikruleindex; + pDataDest = (byte*)pBlockBaseDest + pAnimDesc->animblockikruleindex; + VALIDATE_ALIGNMENT( pDataSrc ) + + int numikrules = SrcNative( &pAnimDesc->numikrules ); + if ( ERROR_MISALIGNED_DATA == ByteswapIKRules( pHdrSrc, pAnimDesc->numikrules, pAnimDesc->numframes, pDataSrc, pDataDest, fileSize ) ) + return ERROR_MISALIGNED_DATA; + } + + /** LOCAL HIERARCHY **/ + + if ( pAnimDesc->localhierarchyindex ) + { + pDataSrc = (byte*)pBlockBaseSrc + pAnimDesc->localhierarchyindex; + pDataDest = (byte*)pBlockBaseDest + pAnimDesc->localhierarchyindex; + + if ( ERROR_MISALIGNED_DATA == ByteswapIKErrors( pDataSrc, pDataDest, pAnimDesc->numlocalhierarchy, pAnimDesc->numframes, fileSize ) ) + return ERROR_MISALIGNED_DATA; + } + } + + // compressed file size could be smaller or larger (due to streaming alignment of blocks) + int compressedFileSize = fileSize; + + // optionally compress the file + if ( g_pCompressFunc && pHdr->numanimblocks >= 2 ) + { + // assemble a new anim of compressed anim blocks + // start with original size, with room for streaming alignment padding + compressedFileSize += (pHdr->numanimblocks + 1) * 2048; + byte *pNewDestBase = (byte *)malloc( compressedFileSize ); + Q_memset( pNewDestBase, 0, compressedFileSize ); + byte *pNewDest = pNewDestBase; + + // get the header payload as is + // assuming the header is up to the first anim block + mstudioanimblock_t *pAnimBlock = pHdr->pAnimBlock( 1 ); + V_memcpy( pNewDest, pDestBase, pAnimBlock->datastart ); + pNewDest += pAnimBlock->datastart; + + int padding = AlignValue( (unsigned int)pNewDest - (unsigned int)pNewDestBase, 2048 ); + padding -= (unsigned int)pNewDest - (unsigned int)pNewDestBase; + pNewDest += padding; + + // iterate and compress anim blocks + // compression may fail, input block is used, but is streaming aligned + for ( int i = 1; i < pHdr->numanimblocks; ++i ) + { + pAnimBlock = pHdr->pAnimBlock( i ); + + void *pInput = (byte *)pDestBase + pAnimBlock->datastart; + int inputSize = pAnimBlock->dataend - pAnimBlock->datastart; + + pAnimBlock->datastart = (unsigned int)pNewDest - (unsigned int)pNewDestBase; + + void *pOutput; + int outputSize; + if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) ) + { + V_memcpy( pNewDest, pOutput, outputSize ); + pNewDest += outputSize; + free( pOutput ); + } + else + { + // as is + V_memcpy( pNewDest, pInput, inputSize ); + pNewDest += inputSize; + } + + padding = AlignValue( (unsigned int)pNewDest - (unsigned int)pNewDestBase, 2048 ); + padding -= (unsigned int)pNewDest - (unsigned int)pNewDestBase; + pNewDest += padding; + + pAnimBlock->dataend = (unsigned int)pNewDest - (unsigned int)pNewDestBase; + } + + compressedFileSize = pNewDest - pNewDestBase; + if ( compressedFileSize > destBaseSize ) + { + // provided destination is not large enough + free( pNewDestBase ); + return ERROR_MISALIGNED_DATA; + } + + V_memcpy( pDestBase, pNewDestBase, compressedFileSize ); + free( pNewDestBase ); + } + + // printf("returning %d\n", fixedFileSize ); + + return compressedFileSize; +} + +//---------------------------------------------------------------------- +// Write a .mdl file in big-endian format +//---------------------------------------------------------------------- +int ByteswapMDL( void *pDestBase, int destBaseSize, const void *pSrcBase, const int fileSize ) +{ + Q_memset( pDestBase, 0, fileSize ); + + byte *pDataSrc = (byte*)pSrcBase; + byte *pDataDest = (byte*)pDestBase; + + /** FILE HEADER **/ + + DECLARE_OBJECT_POINTERS( pHdr, pData, studiohdr_t ) + WriteObjects( pHdrDest, pHdrSrc ); + + /** BONES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, boneindex ) + DECLARE_OBJECT_POINTERS( pStudioBone, pData, mstudiobone_t ) + ITERATE_BLOCK( pStudioBone, pHdr->numbones ) + { + WriteObjects( pStudioBoneDest, pStudioBoneSrc ); + + if ( pStudioBone->procindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pStudioBone, procindex ) + + unsigned int index = SrcNative( &pStudioBone->proctype ); + switch( index ) + { + case STUDIO_PROC_AXISINTERP: + { + /** AXIS-INTERP BONES **/ + DECLARE_OBJECT_POINTERS( pAxisInterpBone, pData, mstudioaxisinterpbone_t ) + WriteObjects( pAxisInterpBoneDest, pAxisInterpBoneSrc ); + break; + } + case STUDIO_PROC_QUATINTERP: + { + /** QUAT-INTERP BONES **/ + DECLARE_OBJECT_POINTERS( pQuatInterpBone, pData, mstudioquatinterpbone_t ) + WriteObjects( pQuatInterpBoneDest, pQuatInterpBoneSrc ); + + /** QUAT-INTERP TRIGGERS **/ + SET_INDEX_POINTERS_VALIDATE( pData, pQuatInterpBone, triggerindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pQuatInterpBone->numtriggers ) ); + break; + } + case STUDIO_PROC_JIGGLE: + { + /** JIGGLE BONES **/ + DECLARE_OBJECT_POINTERS( pJiggleBone, pData, mstudiojigglebone_t ) + WriteObjects( pJiggleBoneDest, pJiggleBoneSrc ); + break; + } + case STUDIO_PROC_AIMATBONE: + case STUDIO_PROC_AIMATATTACH: + { + /** AIM AT BONES **/ + DECLARE_OBJECT_POINTERS( pAimAtBone, pData, mstudioaimatbone_t ) + WriteObjects( pAimAtBoneDest, pAimAtBoneSrc ); + break; + } + case STUDIO_PROC_TWIST_MASTER: + { + /** TWIST BONES **/ + DECLARE_OBJECT_POINTERS( pTwistBone, pData, mstudiotwistbone_t ) + WriteObjects( pTwistBoneDest, pTwistBoneSrc ); + + /** TWIST BONE TARGETS **/ + SET_INDEX_POINTERS_VALIDATE( pData, pTwistBone, m_nTargetIndex ); + WriteObjects< mstudiotwistbonetarget_t >( pDataDest, pDataSrc, SrcNative( &pTwistBone->m_nTargetCount ) ); + break; + } + default: + Assert( 0 ); + Warning( "Unknown bone type %d found!\n", index ); + } + } + } + + /** BONE CONTROLLERS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, bonecontrollerindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numbonecontrollers ) ); + + /** ATTACHMENTS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localattachmentindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalattachments ) ); + + /** HITBOX SETS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, hitboxsetindex ) + DECLARE_OBJECT_POINTERS( pHitboxSet, pData, mstudiohitboxset_t ) + ITERATE_BLOCK( pHitboxSet, pHdr->numhitboxsets ) + { + WriteObjects( pHitboxSetDest, pHitboxSetSrc ); + + /** HITBOXES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHitboxSet, hitboxindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHitboxSet->numhitboxes ) ); + } + + /** BONE TABLE **/ + + SET_INDEX_POINTERS( pData, pHdr, bonetablebynameindex ) + WriteBuffer( pDataDest, pDataSrc, SrcNative( &pHdr->numbones ) ); + + /** ANIMATION DESCRIPTIONS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localanimindex ) + DECLARE_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t ) + ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim ) + { + WriteObjects( pAnimDescDest, pAnimDescSrc ); + + if ( pAnimDesc->animblock == -1 ) + { + // out of date model format + continue; + } + + // section data can point to both internal and external blocks + int numsections = 0; + if ( pAnimDesc->sectionframes != 0 ) + { + numsections = pAnimDesc->numframes / pAnimDesc->sectionframes + 2; + + SET_INDEX_POINTERS( pData, pAnimDesc, sectionindex ) + DECLARE_OBJECT_POINTERS( pSection, pData, mstudioanimsections_t ) + + WriteObjects( pSectionDest, pSectionSrc, numsections ); + } + + if ( pAnimDesc->animblock == 0 ) + { + if ( pAnimDesc->animindex ) + { + if ( numsections == 0 ) + { + SET_INDEX_POINTERS( pData, pAnimDesc, animindex ) + if ( pAnimDesc->flags & STUDIO_FRAMEANIM ) + { + ByteswapFrameAnimData( pHdrSrc, pAnimDesc, 0, pDataSrc, pDataDest ); + } + else + { + ByteswapRLEAnimData( pAnimDesc, 0, pDataSrc, pDataDest ); + } + } + else + { + for ( int i = 0; i < numsections; ++i ) + { + if ( pAnimDesc->pSection( i )->animblock == 0 ) + { + int index = pAnimDesc->pSection( i )->animindex; + + // Base address of the animation in the animblock + byte *pDataSrc = (byte *)pAnimDescSrc + index; + byte *pDataDest = (byte *)pAnimDescDest + index; + + if ( pAnimDesc->flags & STUDIO_FRAMEANIM ) + { + ByteswapFrameAnimData( pHdrSrc, pAnimDesc, i, pDataSrc, pDataDest ); + } + else + { + ByteswapRLEAnimData( pAnimDesc, i, pDataSrc, pDataDest ); + } + + } + } + } + } + + /** IK RULES **/ + + if ( pAnimDesc->ikruleindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pAnimDesc, ikruleindex ) + // DECLARE_OBJECT_POINTERS( pIKRule, pData, mstudioikrule_t ) + + int numframes = SrcNative( &pAnimDesc->numframes ); + if ( ERROR_MISALIGNED_DATA == ByteswapIKRules( pHdrSrc, pAnimDesc->numikrules, numframes, pDataSrc, pDataDest, fileSize ) ) + return ERROR_MISALIGNED_DATA; + } + + /** LOCAL HIERARCHY **/ + + if ( pAnimDesc->localhierarchyindex ) + { + SET_INDEX_POINTERS( pData, pAnimDesc, localhierarchyindex ) + + if ( ERROR_MISALIGNED_DATA == ByteswapIKErrors( pDataSrc, pDataDest, SrcNative( &pAnimDesc->numlocalhierarchy ), SrcNative( &pAnimDesc->numframes ), fileSize ) ) + return ERROR_MISALIGNED_DATA; + } + } + } // Animdesc block + + /** MOVEMENTS **/ + + // Separate loop required by format of mstudioanimdesc_t data + SET_INDEX_POINTERS( pData, pHdr, localanimindex ) + SET_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t ) + ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim ) + { + if ( pAnimDesc->nummovements ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pAnimDesc, movementindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pAnimDesc->nummovements ) ); + } + } + + /** ZERO FRAMES **/ + + SET_INDEX_POINTERS( pData, pHdr, localanimindex ) + SET_OBJECT_POINTERS( pAnimDesc, pData, mstudioanimdesc_t ) + ITERATE_BLOCK( pAnimDesc, pHdr->numlocalanim ) + { + if ( pAnimDesc->pZeroFrameData( ) != NULL ) + { + int offset = pAnimDesc->pZeroFrameData( ) - (byte *)pAnimDesc; + + // Base address of the animation in the animblock + byte *pZeroFrameSrc = (byte *)pAnimDescSrc + offset; + byte *pZeroFrameDest = (byte *)pAnimDescDest + offset; + + SET_INDEX_POINTERS( pData, pHdr, boneindex ) + SET_OBJECT_POINTERS( pStudioBone, pData, mstudiobone_t ) + ITERATE_BLOCK( pStudioBone, pHdr->numbones ) + { + if ( pStudioBone->flags & BONE_HAS_SAVEFRAME_POS ) + { + for ( int j = 0; j < pAnimDesc->zeroframecount; j++) + { + WriteBuffer( &pZeroFrameDest, &pZeroFrameSrc, 3 ); + } + } + if ( pStudioBone->flags & BONE_HAS_SAVEFRAME_ROT64 ) + { + for ( int j = 0; j < pAnimDesc->zeroframecount; j++) + { + WriteBuffer( &pZeroFrameDest, &pZeroFrameSrc, 1 ); + } + } + else if ( pStudioBone->flags & BONE_HAS_SAVEFRAME_ROT32 ) + { + for ( int j = 0; j < pAnimDesc->zeroframecount; j++) + { + WriteBuffer( &pZeroFrameDest, &pZeroFrameSrc, 1 ); + } + } + } + } + + // write zero frame IK release rules + if ( pAnimDesc->pIKRuleZeroFrame( 0 ) ) + { + int offset = (byte *)pAnimDesc->pIKRuleZeroFrame( 0 ) - (byte *)pAnimDesc; + + // printf("%d : %d (%x %x)\n", offset, pAnimDesc->numikrules, pAnimDescSrc->ikrulezeroframeindex, pAnimDescDest->ikrulezeroframeindex ); + + // Base address of the animation in the animblock + byte *pIKRuleZeroFrameSrc = (byte *)pAnimDescSrc + offset; + byte *pIKRuleZeroFrameDest = (byte *)pAnimDescDest + offset; + + for ( int j = 0; j < pAnimDesc->numikrules; j++) + { + WriteBuffer( &pIKRuleZeroFrameDest, &pIKRuleZeroFrameSrc, 6 ); + } + } + + } + + /** SEQUENCE INFO **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localseqindex ) + DECLARE_OBJECT_POINTERS( pSequence, pData, mstudioseqdesc_t ) + ITERATE_BLOCK( pSequence, pHdr->numlocalseq ) + { + WriteObjects( pSequenceDest, pSequenceSrc ); + + /** POSE KEYS **/ + + if ( pSequence->posekeyindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pSequence, posekeyindex ) + WriteBuffer( pDataDest, pDataSrc, SrcNative( &pSequence->groupsize[0] ) + SrcNative( &pSequence->groupsize[1] ) ); + } + + /** STUDIO EVENTS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pSequence, eventindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pSequence->numevents ) ); + + /** AUTOLAYERS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pSequence, autolayerindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pSequence->numautolayers ) ); + + /** BONE WEIGHTS **/ + + // Data may be shared across sequences + DECLARE_INDEX_POINTERS_VALIDATE( pWeight, pSequence, weightlistindex ) + if ( pWeightSrc >= pDataSrc ) + { + int numBoneWeights = ( SrcNative( &pSequence->iklockindex ) - SrcNative( &pSequence->weightlistindex ) ) / sizeof(float); + WriteBuffer( pWeightDest, pWeightSrc, numBoneWeights ); + } + + /** IK LOCKS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pSequence, iklockindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pSequence->numiklocks ) ); + + /** ANIMATION INDICES **/ + + if ( pSequence->animindexindex ) + { + SET_INDEX_POINTERS( pData, pSequence, animindexindex ) + WriteBuffer( pDataDest, pDataSrc, SrcNative( &pSequence->groupsize[0] ) * SrcNative( &pSequence->groupsize[1] ) ); + } + + /** KEYVALUES **/ + + SET_INDEX_POINTERS( pData, pSequence, keyvalueindex ) + WriteBuffer( pDataDest, pDataSrc, SrcNative( &pSequence->keyvaluesize ) ); + + /** ACTIVITY MODIFIERS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pSequence, activitymodifierindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pSequence->numactivitymodifiers ) ); + } + + /** TRANSITION GRAPH **/ + + int numLocalNodes = SrcNative( &pHdr->numlocalnodes ); + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localnodenameindex ) + WriteBuffer( pDataDest, pDataSrc, numLocalNodes ); + + /** LOCAL NODES **/ + + SET_INDEX_POINTERS( pData, pHdr, localnodeindex ) + WriteBuffer( pDataDest, pDataSrc, numLocalNodes * numLocalNodes ); + + /** BODYPART INFO **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, bodypartindex ) + DECLARE_OBJECT_POINTERS( pBodypart, pData, mstudiobodyparts_t ) + ITERATE_BLOCK( pBodypart, pHdr->numbodyparts ) + { + WriteObjects( pBodypartDest, pBodypartSrc ); + + /** MODEL INFO **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pBodypart, modelindex ) + DECLARE_OBJECT_POINTERS( pModel, pData, mstudiomodel_t ) + ITERATE_BLOCK( pModel, pBodypart->nummodels ) + { + WriteObjects( pModelDest, pModelSrc ); + + /** MESHES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pModel, meshindex ) + DECLARE_OBJECT_POINTERS( pMesh, pData, mstudiomesh_t ) + ITERATE_BLOCK( pMesh, pModel->nummeshes ) + { + WriteObjects( pMeshDest, pMeshSrc ); + + if ( !pMesh->numflexes ) + continue; + + /** FLEXES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pMesh, flexindex ) + DECLARE_OBJECT_POINTERS( pFlex, pData, mstudioflex_t ) + ITERATE_BLOCK( pFlex, pMesh->numflexes ) + { + WriteObjects( pFlexDest, pFlexSrc ); + + /** VERT ANIMS **/ + + if ( SrcNative( &pFlex->vertanimtype ) == STUDIO_VERT_ANIM_WRINKLE ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pFlex, vertindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pFlex->numverts ) ); + } + else + { + SET_INDEX_POINTERS_VALIDATE( pData, pFlex, vertindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pFlex->numverts ) ); + } + } + } + + /** EYEBALLS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pModel, eyeballindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pModel->numeyeballs ) ); + } + } + + /** GLOBAL FLEX NAMES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, flexdescindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numflexdesc ) ); + + /** GLOBAL FLEX CONTROLLERS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, flexcontrollerindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numflexcontrollers ) ); + + /** GLOBAL FLEX CONTROLLER REMAPS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, flexcontrolleruiindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numflexcontrollerui ) ); + + // TODOKD: The remap indices after the flex controller remap headers need to be swapped as well? + + /** FLEX RULES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, flexruleindex ) + DECLARE_OBJECT_POINTERS( pFlexRule, pData, mstudioflexrule_t ) + ITERATE_BLOCK( pFlexRule, pHdr->numflexrules ) + { + WriteObjects( pFlexRuleDest, pFlexRuleSrc ); + + /** FLEX OPS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pFlexRule, opindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pFlexRule->numops ) ); + } + + /** IK CHAINS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, ikchainindex ) + DECLARE_OBJECT_POINTERS( pIKChain, pData, mstudioikchain_t ) + ITERATE_BLOCK( pIKChain, pHdr->numikchains ) + { + WriteObjects( pIKChainDest, pIKChainSrc ); + + /** IK LINKS **/ + + SET_INDEX_POINTERS( pData, pIKChain, linkindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pIKChain->numlinks ) ); + } + + /** IK AUTOPLAY LOCKS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localikautoplaylockindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalikautoplaylocks ) ); + + /** MOUTH INFO **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, mouthindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->nummouths ) ); + + /** POSE PARAMATERS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, localposeparamindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numlocalposeparameters ) ); + + /** MODEL GROUPS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, includemodelindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numincludemodels ) ); + + /** ANIMBLOCK GROUP INFO **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, animblockindex ) + WriteObjects( pDataDest, pDataSrc, SrcNative( &pHdr->numanimblocks ) ); + + /** TEXTURE INFO **/ + + // While swapping, kill off unwanted textures by name + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, textureindex ) + DECLARE_OBJECT_POINTERS( pTexture, pData, mstudiotexture_t ) + int textureCt = SrcNative( &pHdr->numtextures ); + int nameOffset = 0; + for ( int i = 0; i < SrcNative( &pHdr->numtextures ); ++i, ++pTexture, ++pTextureSrc ) + { + WriteObjects( pTextureDest, pTextureSrc ); + + int destnameindex = SrcNative( &pTexture->sznameindex ) + nameOffset; + pTextureDest->sznameindex = DestNative( &destnameindex ); + char *pName = (char*)pTexture + SrcNative( &pTexture->sznameindex ); +#if 0 // Undone: Killing textures here can cause crashes at runtime. + // Don't need pupil textures + if ( Q_stristr( pName, "pupil_" ) || !Q_stricmp( pName, "pupil" ) ) + { + --textureCt; + nameOffset += sizeof(mstudiotexture_t); + } + else +#endif + { + ++pTextureDest; + } + } + pHdrDest->numtextures = DestNative( &textureCt ); + + /** TEXTURE INDICES **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pHdr, cdtextureindex ) + WriteBuffer( &pDataDest, &pDataSrc, SrcNative( &pHdr->numcdtextures ) ); + + /** TEXTURE DICTIONARY **/ + + SET_INDEX_POINTERS( pData, pHdr, skinindex ) + WriteBuffer( &pDataDest, &pDataSrc, SrcNative( &pHdr->numskinfamilies ) * SrcNative( &pHdr->numskinref ) ); + + /** KEYVALUES **/ + + SET_INDEX_POINTERS( pData, pHdr, keyvalueindex ) + WriteBuffer( &pDataDest, &pDataSrc, SrcNative( &pHdr->keyvaluesize ) ); + + /** STUDIOHDR2 **/ + + if ( pHdr->studiohdr2index ) + { + DECLARE_INDEX_POINTERS_VALIDATE( pLocalData, pHdr, studiohdr2index ) + DECLARE_OBJECT_POINTERS( pStudioHdr2, pLocalData, studiohdr2_t ) + + // HACK: Pre-swap the constant "1" here so the automatic swap inside ITERATE_BLOCK will restore it + int studiohdr2ct = 1; + studiohdr2ct = SrcNative( &studiohdr2ct ); + ITERATE_BLOCK( pStudioHdr2, studiohdr2ct ) + { + WriteObjects( pStudioHdr2Dest, pStudioHdr2Src ); + + /** SRC BONE TRANSFORMS **/ + + if ( pStudioHdr2->numsrcbonetransform ) + { + // Note, srcbonetransformindex is an offset from the start of the file, not the start of the studiohdr2 + // as is the convention. That's why the macros can't be used here. + pDataSrc = (byte*)pHdrSrc + SrcNative( &pStudioHdr2->srcbonetransformindex ); + pDataDest = (byte*)pHdrDest + SrcNative( &pStudioHdr2->srcbonetransformindex ); + WriteObjects( &pDataDest, &pDataSrc, SrcNative( &pStudioHdr2->numsrcbonetransform ) ); + } + + if ( pStudioHdr2->linearboneindex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pStudioHdr2, linearboneindex ) + DECLARE_OBJECT_POINTERS( pLinearBone, pData, mstudiolinearbone_t ) + + WriteObjects( pLinearBoneDest, pLinearBoneSrc ); + + int numBones = SrcNative( &pLinearBone->numbones ); + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, flagsindex ) + WriteBuffer( &pDataDest, &pDataSrc, numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, parentindex ) + WriteBuffer( &pDataDest, &pDataSrc, numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, posindex ) + WriteBuffer( &pDataDest, &pDataSrc, 3*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, quatindex ) + WriteBuffer( &pDataDest, &pDataSrc, 4*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, rotindex ) + WriteBuffer( &pDataDest, &pDataSrc, 3*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, posetoboneindex ) + WriteBuffer( &pDataDest, &pDataSrc, 12*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, posscaleindex ) + WriteBuffer( &pDataDest, &pDataSrc, 3*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, rotscaleindex ) + WriteBuffer( &pDataDest, &pDataSrc, 3*numBones ); + + SET_INDEX_POINTERS_VALIDATE( pData, pLinearBone, qalignmentindex ) + WriteBuffer( &pDataDest, &pDataSrc, 4*numBones ); + } + + /** BONE FLEX DRIVERS **/ + if ( pStudioHdr2->m_nBoneFlexDriverIndex ) + { + SET_INDEX_POINTERS_VALIDATE( pData, pStudioHdr2, m_nBoneFlexDriverIndex ) + DECLARE_OBJECT_POINTERS( pBoneFlexDriver, pData, mstudioboneflexdriver_t ) + ITERATE_BLOCK( pBoneFlexDriver, pStudioHdr2->m_nBoneFlexDriverCount ) + { + WriteObjects( pBoneFlexDriverDest, pBoneFlexDriverSrc ); + + /** BONE FLEX DRIVER CONTROLS **/ + + SET_INDEX_POINTERS_VALIDATE( pData, pBoneFlexDriver, m_nControlIndex ); + WriteObjects< mstudioboneflexdrivercontrol_t >( &pDataDest, &pDataSrc, SrcNative( &pBoneFlexDriver->m_nControlCount ) ); + } + } + } + } + + /** STRING TABLE **/ + + // NOTE: The block of data (above) swapped immediately before the string table MUST update the + // pDataSrc pointer position, in order for this string table offset calculation to work correctly. + // To update the pointer position, pass the pointer address to WriteObjects(). + int offset = pDataSrc - (byte*)pSrcBase; + int stringTableBytes = fileSize - offset; + WriteBuffer( pDataDest, pDataSrc, stringTableBytes ); + + // Cleanup texture paths + // Some older MDL's have double terminal slashes + SET_INDEX_POINTERS( pData, pHdr, cdtextureindex ) + int numCdTextures = SrcNative( &pHdr->numcdtextures ); + for ( int i = 0; i < numCdTextures; ++i ) + { + char *pPath = (char*)pHdrDest + SrcNative( &((int *)pDataSrc)[i] ); + int len = strlen( pPath ); + if ( len >= 2 && ( pPath[len-1] == '\\' || pPath[len-1] == '/' ) && ( pPath[len-2] == '\\' || pPath[len-2] == '/' ) ) + { + pPath[len-1] = '\0'; + } + } + + int compressedFileSize = fileSize; + + // Optionally compress the file + if ( g_pCompressFunc ) + { + void *pInput = pDestBase; + int inputSize = compressedFileSize; + void *pOutput; + int outputSize; + if ( g_pCompressFunc( pInput, inputSize, &pOutput, &outputSize ) ) + { + V_memcpy( pDestBase, pOutput, outputSize ); + free( pOutput ); + compressedFileSize = outputSize; + } + } + + return compressedFileSize; +} + +//---------------------------------------------------------------------- +// Determines what kind of file this is and calls the correct swap function +//---------------------------------------------------------------------- +int ByteswapStudioFile( const char *pFilename, void *pOutBase, int outBaseSize, const void *pFileBase, int fileSize, studiohdr_t *pHdr, CompressFunc_t pCompressFunc ) +{ + Assert( pFilename ); + Assert( pOutBase != pFileBase ); + + g_pCompressFunc = pCompressFunc; + g_pFilename = pFilename; + + int retVal = 0; + + if ( Q_stristr( pFilename, ".mdl" ) ) + { + retVal = ByteswapMDL( pOutBase, outBaseSize, pFileBase, fileSize ); + } + else if ( Q_stristr( pFilename, ".vvd" ) ) + { + retVal = ByteswapVVD( pOutBase, outBaseSize, pFileBase, fileSize ); + } + else if ( Q_stristr( pFilename, ".vtx" ) ) + { + retVal = ByteswapVTX( pOutBase, outBaseSize, pFileBase, fileSize ); + } + else if ( Q_stristr( pFilename, ".phy" ) ) + { + retVal = ByteswapPHY( pOutBase, outBaseSize, pFileBase, fileSize ); + } + else if ( Q_stristr( pFilename, ".ani" ) ) + { + // some dead .ani files exist in the tree + // only process valid .ani files properly hooked to their .mdl + if ( pHdr && pHdr->numanimblocks != 0 ) + { + retVal = ByteswapANI( pHdr, pOutBase, outBaseSize, pFileBase, fileSize ); + } + } + + g_pCompressFunc = NULL; + + return retVal; +} + + +#pragma warning( pop ) // local variable is initialized but not referenced + +} // namespace StudioByteSwap + +// Data descriptions for byte swapping - only needed +// for structures that are written to file for use by the game. +// For any fields that reference other data in the file, use the +// DEFINE_INDEX macro to identify them as such. +BEGIN_BYTESWAP_DATADESC( studiohdr_t ) + DEFINE_FIELD( id, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_FIELD( checksum, FIELD_INTEGER ), + DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( length, FIELD_INTEGER ), + DEFINE_FIELD( eyeposition, FIELD_VECTOR ), + DEFINE_FIELD( illumposition, FIELD_VECTOR ), + DEFINE_FIELD( hull_min, FIELD_VECTOR ), + DEFINE_FIELD( hull_max, FIELD_VECTOR ), + DEFINE_FIELD( view_bbmin, FIELD_VECTOR ), + DEFINE_FIELD( view_bbmax, FIELD_VECTOR ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( numbones, FIELD_INTEGER ), // bones + DEFINE_INDEX( boneindex, FIELD_INTEGER ), + DEFINE_FIELD( numbonecontrollers, FIELD_INTEGER ), // bone controllers + DEFINE_INDEX( bonecontrollerindex, FIELD_INTEGER ), + DEFINE_FIELD( numhitboxsets, FIELD_INTEGER ), + DEFINE_INDEX( hitboxsetindex, FIELD_INTEGER ), + DEFINE_FIELD( numlocalanim, FIELD_INTEGER ), // animations/poses + DEFINE_INDEX( localanimindex, FIELD_INTEGER ), // animation descriptions + DEFINE_FIELD( numlocalseq, FIELD_INTEGER ), // sequences + DEFINE_INDEX( localseqindex, FIELD_INTEGER ), + DEFINE_FIELD( activitylistversion, FIELD_INTEGER ), // initialization flag - have the sequences been indexed? + DEFINE_FIELD( eventsindexed, FIELD_INTEGER ), + DEFINE_FIELD( numtextures, FIELD_INTEGER ), + DEFINE_INDEX( textureindex, FIELD_INTEGER ), + DEFINE_FIELD( numcdtextures, FIELD_INTEGER ), + DEFINE_INDEX( cdtextureindex, FIELD_INTEGER ), + DEFINE_FIELD( numskinref, FIELD_INTEGER ), + DEFINE_FIELD( numskinfamilies, FIELD_INTEGER ), + DEFINE_INDEX( skinindex, FIELD_INTEGER ), + DEFINE_FIELD( numbodyparts, FIELD_INTEGER ), + DEFINE_INDEX( bodypartindex, FIELD_INTEGER ), + DEFINE_FIELD( numlocalattachments, FIELD_INTEGER ), + DEFINE_INDEX( localattachmentindex, FIELD_INTEGER ), + DEFINE_FIELD( numlocalnodes, FIELD_INTEGER ), + DEFINE_INDEX( localnodeindex, FIELD_INTEGER ), + DEFINE_INDEX( localnodenameindex, FIELD_INTEGER ), + DEFINE_FIELD( numflexdesc, FIELD_INTEGER ), + DEFINE_INDEX( flexdescindex, FIELD_INTEGER ), + DEFINE_FIELD( numflexcontrollers, FIELD_INTEGER ), + DEFINE_INDEX( flexcontrollerindex, FIELD_INTEGER ), + DEFINE_FIELD( numflexrules, FIELD_INTEGER ), + DEFINE_INDEX( flexruleindex, FIELD_INTEGER ), + DEFINE_FIELD( numikchains, FIELD_INTEGER ), + DEFINE_INDEX( ikchainindex, FIELD_INTEGER ), + DEFINE_FIELD( nummouths, FIELD_INTEGER ), + DEFINE_INDEX( mouthindex, FIELD_INTEGER ), + DEFINE_FIELD( numlocalposeparameters, FIELD_INTEGER ), + DEFINE_INDEX( localposeparamindex, FIELD_INTEGER ), + DEFINE_INDEX( surfacepropindex, FIELD_INTEGER ), + DEFINE_INDEX( keyvalueindex, FIELD_INTEGER ), + DEFINE_FIELD( keyvaluesize, FIELD_INTEGER ), + DEFINE_FIELD( numlocalikautoplaylocks, FIELD_INTEGER ), + DEFINE_INDEX( localikautoplaylockindex, FIELD_INTEGER ), + DEFINE_FIELD( mass, FIELD_FLOAT ), + DEFINE_FIELD( contents, FIELD_INTEGER ), + DEFINE_FIELD( numincludemodels, FIELD_INTEGER ), + DEFINE_INDEX( includemodelindex, FIELD_INTEGER ), + DEFINE_FIELD( virtualModel, FIELD_INTEGER ), // void* + DEFINE_INDEX( szanimblocknameindex, FIELD_INTEGER ), + DEFINE_FIELD( numanimblocks, FIELD_INTEGER ), + DEFINE_INDEX( animblockindex, FIELD_INTEGER ), + DEFINE_FIELD( animblockModel, FIELD_INTEGER ), // void* + DEFINE_INDEX( bonetablebynameindex, FIELD_INTEGER ), + DEFINE_FIELD( pVertexBase, FIELD_INTEGER ), // void* + DEFINE_FIELD( pIndexBase, FIELD_INTEGER ), // void* + DEFINE_FIELD( constdirectionallightdot, FIELD_CHARACTER ), // byte + DEFINE_FIELD( rootLOD, FIELD_CHARACTER ), // byte + DEFINE_FIELD( numAllowedRootLODs, FIELD_CHARACTER ), // byte + DEFINE_ARRAY( unused, FIELD_CHARACTER, 1 ), // byte + DEFINE_INDEX( unused4, FIELD_INTEGER ), + DEFINE_FIELD( numflexcontrollerui, FIELD_INTEGER ), + DEFINE_INDEX( flexcontrolleruiindex, FIELD_INTEGER ), + DEFINE_FIELD( surfacepropLookup, FIELD_INTEGER ), // void* + DEFINE_FIELD( flVertAnimFixedPointScale, FIELD_FLOAT ), + DEFINE_INDEX( studiohdr2index, FIELD_INTEGER ), + DEFINE_ARRAY( unused2, FIELD_INTEGER, 1 ), +END_BYTESWAP_DATADESC() + +// NOTE! Next time we up the .mdl file format, remove studiohdr2_t +// and insert all fields in this structure into studiohdr_t. +BEGIN_BYTESWAP_DATADESC( studiohdr2_t ) + DEFINE_FIELD( numsrcbonetransform, FIELD_INTEGER ), + DEFINE_INDEX( srcbonetransformindex, FIELD_INTEGER ), + DEFINE_FIELD( illumpositionattachmentindex, FIELD_INTEGER ), + DEFINE_FIELD( flMaxEyeDeflection, FIELD_FLOAT ), + DEFINE_INDEX( linearboneindex, FIELD_INTEGER ), + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_INDEX( m_nBoneFlexDriverCount, FIELD_INTEGER ), + DEFINE_INDEX( m_nBoneFlexDriverIndex, FIELD_INTEGER ), + DEFINE_ARRAY( reserved, FIELD_INTEGER, 56 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiobone_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( parent, FIELD_INTEGER ), + DEFINE_ARRAY( bonecontroller, FIELD_INTEGER, 6 ), + DEFINE_FIELD( pos, FIELD_VECTOR ), + DEFINE_FIELD( quat, FIELD_QUATERNION ), + DEFINE_ARRAY( rot, FIELD_FLOAT, 3 ), // RadianEuler + DEFINE_FIELD( posscale, FIELD_VECTOR ), + DEFINE_FIELD( rotscale, FIELD_VECTOR ), + DEFINE_ARRAY( poseToBone, FIELD_FLOAT, 12 ), // matrix3x4_t + DEFINE_FIELD( qAlignment, FIELD_QUATERNION ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( proctype, FIELD_INTEGER ), + DEFINE_INDEX( procindex, FIELD_INTEGER ), + DEFINE_INDEX( physicsbone, FIELD_INTEGER ), + DEFINE_INDEX( surfacepropidx, FIELD_INTEGER ), + DEFINE_FIELD( contents, FIELD_INTEGER ), + DEFINE_FIELD( surfacepropLookup, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 7 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiolinearbone_t ) + DEFINE_FIELD( numbones, FIELD_INTEGER ), + DEFINE_INDEX( flagsindex, FIELD_INTEGER ), + DEFINE_INDEX( parentindex, FIELD_INTEGER ), + DEFINE_INDEX( posindex, FIELD_INTEGER ), + DEFINE_INDEX( quatindex, FIELD_INTEGER ), + DEFINE_INDEX( rotindex, FIELD_INTEGER ), + DEFINE_INDEX( posetoboneindex, FIELD_INTEGER ), + DEFINE_INDEX( posscaleindex, FIELD_INTEGER ), + DEFINE_INDEX( rotscaleindex, FIELD_INTEGER ), + DEFINE_INDEX( qalignmentindex, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 6 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioboneflexdrivercontrol_t ) + DEFINE_INDEX( m_nBoneComponent, FIELD_INTEGER ), + DEFINE_FIELD( m_nFlexControllerIndex, FIELD_INTEGER ), + DEFINE_INDEX( m_flMin, FIELD_FLOAT ), + DEFINE_INDEX( m_flMax, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioboneflexdriver_t ) + DEFINE_INDEX( m_nBoneIndex, FIELD_INTEGER ), + DEFINE_FIELD( m_nControlCount, FIELD_INTEGER ), + DEFINE_INDEX( m_nControlIndex, FIELD_FLOAT ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 3 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioaxisinterpbone_t ) + DEFINE_FIELD( control, FIELD_INTEGER ), + DEFINE_FIELD( axis, FIELD_INTEGER ), + DEFINE_ARRAY( pos, FIELD_VECTOR, 6 ), + DEFINE_ARRAY( quat, FIELD_QUATERNION, 6 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioquatinterpbone_t ) + DEFINE_FIELD( control, FIELD_INTEGER ), + DEFINE_FIELD( numtriggers, FIELD_INTEGER ), + DEFINE_INDEX( triggerindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiojigglebone_t ) + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( length, FIELD_FLOAT ), + DEFINE_FIELD( tipMass, FIELD_FLOAT ), + DEFINE_FIELD( yawStiffness, FIELD_FLOAT ), + DEFINE_FIELD( yawDamping, FIELD_FLOAT ), + DEFINE_FIELD( pitchStiffness, FIELD_FLOAT ), + DEFINE_FIELD( pitchDamping, FIELD_FLOAT ), + DEFINE_FIELD( alongStiffness, FIELD_FLOAT ), + DEFINE_FIELD( alongDamping, FIELD_FLOAT ), + DEFINE_FIELD( angleLimit, FIELD_FLOAT ), + DEFINE_FIELD( minYaw, FIELD_FLOAT ), + DEFINE_FIELD( maxYaw, FIELD_FLOAT ), + DEFINE_FIELD( yawFriction, FIELD_FLOAT ), + DEFINE_FIELD( yawBounce, FIELD_FLOAT ), + DEFINE_FIELD( minPitch, FIELD_FLOAT ), + DEFINE_FIELD( maxPitch, FIELD_FLOAT ), + DEFINE_FIELD( pitchFriction, FIELD_FLOAT ), + DEFINE_FIELD( pitchBounce, FIELD_FLOAT ), + DEFINE_FIELD( baseMass, FIELD_FLOAT ), + DEFINE_FIELD( baseStiffness, FIELD_FLOAT ), + DEFINE_FIELD( baseDamping, FIELD_FLOAT ), + DEFINE_FIELD( baseMinLeft, FIELD_FLOAT ), + DEFINE_FIELD( baseMaxLeft, FIELD_FLOAT ), + DEFINE_FIELD( baseLeftFriction, FIELD_FLOAT ), + DEFINE_FIELD( baseMinUp, FIELD_FLOAT ), + DEFINE_FIELD( baseMaxUp, FIELD_FLOAT ), + DEFINE_FIELD( baseUpFriction, FIELD_FLOAT ), + DEFINE_FIELD( baseMinForward, FIELD_FLOAT ), + DEFINE_FIELD( baseMaxForward, FIELD_FLOAT ), + DEFINE_FIELD( baseForwardFriction, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioaimatbone_t ) + DEFINE_FIELD( parent, FIELD_INTEGER ), + DEFINE_FIELD( aim, FIELD_INTEGER ), + DEFINE_ARRAY( aimvector, FIELD_FLOAT, 3 ), + DEFINE_ARRAY( upvector, FIELD_FLOAT, 3 ), + DEFINE_ARRAY( basepos, FIELD_FLOAT, 3 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiotwistbonetarget_t ) + DEFINE_FIELD( m_nBone, FIELD_INTEGER ), + DEFINE_FIELD( m_flWeight, FIELD_FLOAT ), + DEFINE_FIELD( m_vBaseTranslate, FIELD_VECTOR ), + DEFINE_FIELD( m_qBaseRotation, FIELD_QUATERNION ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiotwistbone_t ) + DEFINE_FIELD( m_bInverse, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vUpVector, FIELD_VECTOR ), + DEFINE_FIELD( m_nParentBone, FIELD_INTEGER ), + DEFINE_FIELD( m_qBaseInv, FIELD_QUATERNION ), + DEFINE_FIELD( m_nChildBone, FIELD_INTEGER ), + DEFINE_FIELD( m_nTargetCount, FIELD_INTEGER ), + DEFINE_FIELD( m_nTargetIndex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioquatinterpinfo_t ) + DEFINE_FIELD( inv_tolerance, FIELD_FLOAT ), + DEFINE_FIELD( trigger, FIELD_QUATERNION ), + DEFINE_FIELD( pos, FIELD_VECTOR ), + DEFINE_FIELD( quat, FIELD_QUATERNION ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiobonecontroller_t ) + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( type, FIELD_INTEGER ), + DEFINE_FIELD( start, FIELD_FLOAT ), + DEFINE_FIELD( end, FIELD_FLOAT ), + DEFINE_FIELD( rest, FIELD_INTEGER ), + DEFINE_FIELD( inputfield, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioattachment_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( localbone, FIELD_INTEGER ), + DEFINE_ARRAY( local, FIELD_FLOAT, 12 ), // matrix3x4_t + DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiohitboxset_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( numhitboxes, FIELD_INTEGER ), + DEFINE_INDEX( hitboxindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiosrcbonetransform_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_ARRAY( pretransform, FIELD_FLOAT, 12 ), // matrix3x4_t + DEFINE_ARRAY( posttransform, FIELD_FLOAT, 12 ), // matrix3x4_t +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiobbox_t ) + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( group, FIELD_INTEGER ), + DEFINE_FIELD( bbmin, FIELD_VECTOR ), + DEFINE_FIELD( bbmax, FIELD_VECTOR ), + DEFINE_INDEX( szhitboxnameindex, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioanim_valueptr_t ) + DEFINE_ARRAY( offset, FIELD_SHORT, 3 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiolocalhierarchy_t ) + DEFINE_FIELD( iBone, FIELD_INTEGER ), + DEFINE_FIELD( iNewParent, FIELD_INTEGER ), + DEFINE_FIELD( start, FIELD_FLOAT ), + DEFINE_FIELD( peak, FIELD_FLOAT ), + DEFINE_FIELD( tail, FIELD_FLOAT ), + DEFINE_FIELD( end, FIELD_FLOAT ), + DEFINE_FIELD( iStart, FIELD_INTEGER ), + DEFINE_INDEX( localanimindex, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioanimsections_t ) + DEFINE_FIELD( animblock, FIELD_INTEGER ), + DEFINE_INDEX( animindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioanimdesc_t ) + DEFINE_INDEX( baseptr, FIELD_INTEGER ), + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( fps, FIELD_FLOAT ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( numframes, FIELD_INTEGER ), + DEFINE_FIELD( nummovements, FIELD_INTEGER ), + DEFINE_INDEX( movementindex, FIELD_INTEGER ), + DEFINE_INDEX( ikrulezeroframeindex, FIELD_INTEGER ), + DEFINE_ARRAY( unused1, FIELD_INTEGER, 5 ), + DEFINE_FIELD( animblock, FIELD_INTEGER ), + DEFINE_INDEX( animindex, FIELD_INTEGER ), + DEFINE_FIELD( numikrules, FIELD_INTEGER ), + DEFINE_INDEX( ikruleindex, FIELD_INTEGER ), + DEFINE_INDEX( animblockikruleindex, FIELD_INTEGER ), + DEFINE_FIELD( numlocalhierarchy, FIELD_INTEGER ), + DEFINE_INDEX( localhierarchyindex, FIELD_INTEGER ), + DEFINE_INDEX( sectionindex, FIELD_INTEGER ), + DEFINE_FIELD( sectionframes, FIELD_INTEGER ), + DEFINE_FIELD( zeroframespan, FIELD_SHORT ), + DEFINE_FIELD( zeroframecount, FIELD_SHORT ), + DEFINE_INDEX( zeroframeindex, FIELD_INTEGER ), + DEFINE_FIELD( zeroframestalltime, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudio_rle_anim_t ) + DEFINE_FIELD( bone, FIELD_CHARACTER ), + DEFINE_FIELD( flags, FIELD_CHARACTER ), + DEFINE_INDEX( nextoffset, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudio_frame_anim_t ) + DEFINE_FIELD( constantsoffset, FIELD_INTEGER ), + DEFINE_FIELD( frameoffset, FIELD_INTEGER ), + DEFINE_FIELD( framelength, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 3 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioikerror_t ) + DEFINE_FIELD( pos, FIELD_VECTOR ), + DEFINE_FIELD( q, FIELD_QUATERNION ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiocompressedikerror_t ) + DEFINE_ARRAY( scale, FIELD_FLOAT, 6 ), + DEFINE_ARRAY( offset, FIELD_SHORT, 6 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioikrule_t ) + DEFINE_FIELD( index, FIELD_INTEGER ), + DEFINE_FIELD( type, FIELD_INTEGER ), + DEFINE_FIELD( chain, FIELD_INTEGER ), + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( slot, FIELD_INTEGER ), + DEFINE_FIELD( height, FIELD_FLOAT ), + DEFINE_FIELD( radius, FIELD_FLOAT ), + DEFINE_FIELD( floor, FIELD_FLOAT ), + DEFINE_FIELD( pos, FIELD_VECTOR ), + DEFINE_FIELD( q, FIELD_QUATERNION ), + DEFINE_INDEX( compressedikerrorindex, FIELD_INTEGER ), + DEFINE_FIELD( unused2, FIELD_INTEGER ), + DEFINE_FIELD( iStart, FIELD_INTEGER ), + DEFINE_INDEX( ikerrorindex, FIELD_INTEGER ), + DEFINE_FIELD( start, FIELD_FLOAT ), + DEFINE_FIELD( peak, FIELD_FLOAT ), + DEFINE_FIELD( tail, FIELD_FLOAT ), + DEFINE_FIELD( end, FIELD_FLOAT ), + DEFINE_FIELD( unused3, FIELD_FLOAT ), + DEFINE_FIELD( contact, FIELD_FLOAT ), + DEFINE_FIELD( drop, FIELD_FLOAT ), + DEFINE_FIELD( top, FIELD_FLOAT ), + DEFINE_FIELD( unused6, FIELD_INTEGER ), + DEFINE_FIELD( unused7, FIELD_INTEGER ), + DEFINE_FIELD( unused8, FIELD_INTEGER ), + DEFINE_INDEX( szattachmentindex, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 7 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiomovement_t ) + DEFINE_FIELD( endframe, FIELD_INTEGER ), + DEFINE_FIELD( motionflags, FIELD_INTEGER ), + DEFINE_FIELD( v0, FIELD_FLOAT ), + DEFINE_FIELD( v1, FIELD_FLOAT ), + DEFINE_FIELD( angle, FIELD_FLOAT ), + DEFINE_FIELD( vector, FIELD_VECTOR ), + DEFINE_FIELD( position, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioactivitymodifier_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioseqdesc_t ) + DEFINE_INDEX( baseptr, FIELD_INTEGER ), + DEFINE_INDEX( szlabelindex, FIELD_INTEGER ), + DEFINE_INDEX( szactivitynameindex, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_INTEGER ), // looping/non-looping flags + DEFINE_FIELD( activity, FIELD_INTEGER ), // initialized at loadtime to game DLL values + DEFINE_FIELD( actweight, FIELD_INTEGER ), + DEFINE_FIELD( numevents, FIELD_INTEGER ), + DEFINE_INDEX( eventindex, FIELD_INTEGER ), + DEFINE_FIELD( bbmin, FIELD_VECTOR ), + DEFINE_FIELD( bbmax, FIELD_VECTOR ), + DEFINE_FIELD( numblends, FIELD_INTEGER ), + DEFINE_INDEX( animindexindex, FIELD_INTEGER ), + DEFINE_INDEX( movementindex, FIELD_INTEGER ), // [blend] float array for blended movement + DEFINE_ARRAY( groupsize, FIELD_INTEGER, 2 ), + DEFINE_ARRAY( paramindex, FIELD_INTEGER, 2 ), // X, Y, Z, XR, YR, ZR + DEFINE_ARRAY( paramstart, FIELD_FLOAT, 2 ), // local (0..1) starting value + DEFINE_ARRAY( paramend, FIELD_FLOAT, 2 ), // local (0..1) ending value + DEFINE_FIELD( paramparent, FIELD_INTEGER ), + DEFINE_FIELD( fadeintime, FIELD_FLOAT ), // ideal cross fate in time (0.2 default) + DEFINE_FIELD( fadeouttime, FIELD_FLOAT ), // ideal cross fade out time (0.2 default) + DEFINE_FIELD( localentrynode, FIELD_INTEGER ), // transition node at entry + DEFINE_FIELD( localexitnode, FIELD_INTEGER ), // transition node at exit + DEFINE_FIELD( nodeflags, FIELD_INTEGER ), // transition rules + DEFINE_FIELD( entryphase, FIELD_FLOAT ), // used to match entry gait + DEFINE_FIELD( exitphase, FIELD_FLOAT ), // used to match exit gait + DEFINE_FIELD( lastframe, FIELD_FLOAT ), // frame that should generation EndOfSequence + DEFINE_FIELD( nextseq, FIELD_INTEGER ), // auto advancing sequences + DEFINE_FIELD( pose, FIELD_INTEGER ), // index of delta animation between end and nextseq + DEFINE_FIELD( numikrules, FIELD_INTEGER ), + DEFINE_FIELD( numautolayers, FIELD_INTEGER ), + DEFINE_INDEX( autolayerindex, FIELD_INTEGER ), + DEFINE_INDEX( weightlistindex, FIELD_INTEGER ), + DEFINE_INDEX( posekeyindex, FIELD_INTEGER ), + DEFINE_FIELD( numiklocks, FIELD_INTEGER ), + DEFINE_INDEX( iklockindex, FIELD_INTEGER ), + DEFINE_INDEX( keyvalueindex, FIELD_INTEGER ), + DEFINE_FIELD( keyvaluesize, FIELD_INTEGER ), + DEFINE_INDEX( cycleposeindex, FIELD_INTEGER ), + DEFINE_INDEX( activitymodifierindex, FIELD_INTEGER ), + DEFINE_FIELD( numactivitymodifiers, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 5 ), // remove/add as appropriate (grow back to 8 ints on version change!) +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioevent_t ) + DEFINE_FIELD( cycle, FIELD_FLOAT ), + DEFINE_FIELD( event, FIELD_INTEGER ), + DEFINE_FIELD( type, FIELD_INTEGER ), + DEFINE_ARRAY( options, FIELD_CHARACTER, 64 ), + DEFINE_INDEX( szeventindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioautolayer_t ) + DEFINE_FIELD( iSequence, FIELD_SHORT ), + DEFINE_FIELD( iPose, FIELD_SHORT ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( start, FIELD_FLOAT ), + DEFINE_FIELD( peak, FIELD_FLOAT ), + DEFINE_FIELD( tail, FIELD_FLOAT ), + DEFINE_FIELD( end, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioiklock_t ) + DEFINE_FIELD( chain, FIELD_INTEGER ), + DEFINE_FIELD( flPosWeight, FIELD_FLOAT ), + DEFINE_FIELD( flLocalQWeight, FIELD_FLOAT ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiobodyparts_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( nummodels, FIELD_INTEGER ), + DEFINE_FIELD( base, FIELD_INTEGER ), + DEFINE_INDEX( modelindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiomodel_t ) + DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ), + DEFINE_FIELD( type, FIELD_INTEGER ), + DEFINE_FIELD( boundingradius, FIELD_FLOAT ), + DEFINE_FIELD( nummeshes, FIELD_INTEGER ), + DEFINE_INDEX( meshindex, FIELD_INTEGER ), + DEFINE_FIELD( numvertices, FIELD_INTEGER ), + DEFINE_INDEX( vertexindex, FIELD_INTEGER ), + DEFINE_INDEX( tangentsindex, FIELD_INTEGER ), + DEFINE_FIELD( numattachments, FIELD_INTEGER ), + DEFINE_INDEX( attachmentindex, FIELD_INTEGER ), + DEFINE_FIELD( numeyeballs, FIELD_INTEGER ), + DEFINE_INDEX( eyeballindex, FIELD_INTEGER ), + DEFINE_EMBEDDED( vertexdata ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudio_modelvertexdata_t ) + DEFINE_FIELD( pVertexData, FIELD_INTEGER ), // void* + DEFINE_FIELD( pTangentData, FIELD_INTEGER ), // void* +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflexdesc_t ) + DEFINE_INDEX( szFACSindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflexcontroller_t ) + DEFINE_INDEX( sztypeindex, FIELD_INTEGER ), + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( localToGlobal, FIELD_INTEGER ), + DEFINE_FIELD( min, FIELD_FLOAT ), + DEFINE_FIELD( max, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflexcontrollerui_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_INDEX( szindex0, FIELD_INTEGER ), + DEFINE_INDEX( szindex1, FIELD_INTEGER ), + DEFINE_INDEX( szindex2, FIELD_INTEGER ), + DEFINE_FIELD( remaptype, FIELD_CHARACTER ), + DEFINE_FIELD( stereo, FIELD_BOOLEAN ), + DEFINE_ARRAY( unused, FIELD_CHARACTER, 2 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflexrule_t ) + DEFINE_FIELD( flex, FIELD_INTEGER ), + DEFINE_FIELD( numops, FIELD_INTEGER ), + DEFINE_INDEX( opindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflexop_t ) + DEFINE_FIELD( op, FIELD_INTEGER ), + DEFINE_FIELD( d, FIELD_INTEGER ), // int/float union +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioikchain_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( linktype, FIELD_INTEGER ), + DEFINE_FIELD( numlinks, FIELD_INTEGER ), + DEFINE_INDEX( linkindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioiklink_t ) + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( kneeDir, FIELD_VECTOR ), + DEFINE_FIELD( unused0, FIELD_VECTOR ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiomouth_t ) + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( forward, FIELD_VECTOR ), + DEFINE_FIELD( flexdesc, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioposeparamdesc_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( start, FIELD_FLOAT ), + DEFINE_FIELD( end, FIELD_FLOAT ), + DEFINE_FIELD( loop, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiomesh_t ) + DEFINE_FIELD( material, FIELD_INTEGER ), + DEFINE_INDEX( modelindex, FIELD_INTEGER ), + DEFINE_FIELD( numvertices, FIELD_INTEGER ), + DEFINE_FIELD( vertexoffset, FIELD_INTEGER ), + DEFINE_FIELD( numflexes, FIELD_INTEGER ), + DEFINE_INDEX( flexindex, FIELD_INTEGER ), + DEFINE_FIELD( materialtype, FIELD_INTEGER ), + DEFINE_FIELD( materialparam, FIELD_INTEGER ), + DEFINE_FIELD( meshid, FIELD_INTEGER ), + DEFINE_FIELD( center, FIELD_VECTOR ), + DEFINE_EMBEDDED( vertexdata ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 8 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudio_meshvertexdata_t ) + DEFINE_FIELD( modelvertexdata, FIELD_INTEGER ), // mstudio_modelvertexdata_t* + DEFINE_ARRAY( numLODVertexes, FIELD_INTEGER, MAX_NUM_LODS ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioeyeball_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( bone, FIELD_INTEGER ), + DEFINE_FIELD( org, FIELD_VECTOR ), + DEFINE_FIELD( zoffset, FIELD_FLOAT ), + DEFINE_FIELD( radius, FIELD_FLOAT ), + DEFINE_FIELD( up, FIELD_VECTOR ), + DEFINE_FIELD( forward, FIELD_VECTOR ), + DEFINE_FIELD( texture, FIELD_INTEGER ), + DEFINE_FIELD( unused1, FIELD_INTEGER ), + DEFINE_FIELD( iris_scale, FIELD_FLOAT ), + DEFINE_FIELD( unused2, FIELD_INTEGER ), + DEFINE_ARRAY( upperflexdesc, FIELD_INTEGER, 3 ), + DEFINE_ARRAY( lowerflexdesc, FIELD_INTEGER, 3 ), + DEFINE_ARRAY( uppertarget, FIELD_FLOAT, 3 ), + DEFINE_ARRAY( lowertarget, FIELD_FLOAT, 3 ), + DEFINE_FIELD( upperlidflexdesc, FIELD_INTEGER ), + DEFINE_FIELD( lowerlidflexdesc, FIELD_INTEGER ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 4 ), + DEFINE_FIELD( m_bNonFACS, FIELD_BOOLEAN ), + DEFINE_ARRAY( unused3, FIELD_CHARACTER, 3 ), + DEFINE_ARRAY( unused4, FIELD_INTEGER, 7 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioflex_t ) + DEFINE_FIELD( flexdesc, FIELD_INTEGER ), + DEFINE_FIELD( target0, FIELD_FLOAT ), + DEFINE_FIELD( target1, FIELD_FLOAT ), + DEFINE_FIELD( target2, FIELD_FLOAT ), + DEFINE_FIELD( target3, FIELD_FLOAT ), + DEFINE_FIELD( numverts, FIELD_INTEGER ), + DEFINE_INDEX( vertindex, FIELD_INTEGER ), + DEFINE_FIELD( flexpair, FIELD_INTEGER ), + DEFINE_FIELD( vertanimtype, FIELD_CHARACTER ), + DEFINE_ARRAY( unusedchar, FIELD_CHARACTER, 3 ), + DEFINE_ARRAY( unused, FIELD_INTEGER, 6 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiovertanim_t ) + DEFINE_FIELD( index, FIELD_SHORT ), + DEFINE_FIELD( speed, FIELD_CHARACTER ), + DEFINE_FIELD( side, FIELD_CHARACTER ), + DEFINE_ARRAY( delta, FIELD_SHORT, 3 ), // short[3]/float16[3]union + DEFINE_ARRAY( ndelta, FIELD_SHORT, 3 ), // short[3]/float16[3] union +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiovertanim_wrinkle_t ) + DEFINE_FIELD( index, FIELD_SHORT ), + DEFINE_FIELD( speed, FIELD_CHARACTER ), + DEFINE_FIELD( side, FIELD_CHARACTER ), + DEFINE_ARRAY( delta, FIELD_SHORT, 3 ), // short[3]/float16[3]union + DEFINE_ARRAY( ndelta, FIELD_SHORT, 3 ), // short[3]/float16[3] union + DEFINE_FIELD( wrinkledelta, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiomodelgroup_t ) + DEFINE_INDEX( szlabelindex, FIELD_INTEGER ), + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioanimblock_t ) + DEFINE_INDEX( datastart, FIELD_INTEGER ), + DEFINE_INDEX( dataend, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiotexture_t ) + DEFINE_INDEX( sznameindex, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_INTEGER ), + DEFINE_FIELD( used, FIELD_INTEGER ), + DEFINE_FIELD( unused1, FIELD_INTEGER ), + DEFINE_FIELD( material, FIELD_INTEGER ), // IMaterial* + DEFINE_FIELD( clientmaterial, FIELD_INTEGER ), // void* + DEFINE_ARRAY( unused, FIELD_INTEGER, 10 ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( vertexFileHeader_t ) + DEFINE_FIELD( id, FIELD_INTEGER ), + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_FIELD( checksum, FIELD_INTEGER ), + DEFINE_FIELD( numLODs, FIELD_INTEGER ), + DEFINE_ARRAY( numLODVertexes, FIELD_INTEGER, MAX_NUM_LODS ), + DEFINE_FIELD( numFixups, FIELD_INTEGER ), + DEFINE_FIELD( fixupTableStart, FIELD_INTEGER ), + DEFINE_FIELD( vertexDataStart, FIELD_INTEGER ), + DEFINE_FIELD( tangentDataStart, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( vertexFileFixup_t ) + DEFINE_FIELD( lod, FIELD_INTEGER ), + DEFINE_FIELD( sourceVertexID, FIELD_INTEGER ), + DEFINE_FIELD( numVertexes, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudioboneweight_t ) + DEFINE_ARRAY( weight, FIELD_FLOAT, MAX_NUM_BONES_PER_VERT ), + DEFINE_ARRAY( bone, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ), + DEFINE_FIELD( numbones, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( mstudiovertex_t ) + DEFINE_EMBEDDED( m_BoneWeights ), + DEFINE_FIELD( m_vecPosition, FIELD_VECTOR ), + DEFINE_FIELD( m_vecNormal, FIELD_VECTOR ), + DEFINE_ARRAY( m_vecTexCoord, FIELD_FLOAT, 2 ), +END_BYTESWAP_DATADESC() + +// Data descriptions from OptimizedModel.h +namespace OptimizedModel +{ + +BEGIN_BYTESWAP_DATADESC( BoneStateChangeHeader_t ) + DEFINE_FIELD( hardwareID, FIELD_INTEGER ), + DEFINE_FIELD( newBoneID, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( Vertex_t ) + DEFINE_ARRAY( boneWeightIndex, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ), + DEFINE_FIELD( numBones, FIELD_CHARACTER ), + DEFINE_FIELD( origMeshVertID, FIELD_SHORT ), + DEFINE_ARRAY( boneID, FIELD_CHARACTER, MAX_NUM_BONES_PER_VERT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StripHeader_t ) + DEFINE_FIELD( numIndices, FIELD_INTEGER ), + DEFINE_FIELD( indexOffset, FIELD_INTEGER ), + DEFINE_FIELD( numVerts, FIELD_INTEGER ), + DEFINE_FIELD( vertOffset, FIELD_INTEGER ), + DEFINE_FIELD( numBones, FIELD_SHORT ), + DEFINE_FIELD( flags, FIELD_CHARACTER ), + DEFINE_FIELD( numBoneStateChanges, FIELD_INTEGER ), + DEFINE_FIELD( boneStateChangeOffset, FIELD_INTEGER ), + DEFINE_FIELD( numTopologyIndices, FIELD_INTEGER ), + DEFINE_FIELD( topologyOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( StripGroupHeader_t ) + DEFINE_FIELD( numVerts, FIELD_INTEGER ), + DEFINE_FIELD( vertOffset, FIELD_INTEGER ), + DEFINE_FIELD( numIndices, FIELD_INTEGER ), + DEFINE_FIELD( indexOffset, FIELD_INTEGER ), + DEFINE_FIELD( numStrips, FIELD_INTEGER ), + DEFINE_FIELD( stripOffset, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_CHARACTER ), + DEFINE_FIELD( numTopologyIndices, FIELD_INTEGER ), + DEFINE_FIELD( topologyOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( MeshHeader_t ) + DEFINE_FIELD( numStripGroups, FIELD_INTEGER ), + DEFINE_FIELD( stripGroupHeaderOffset, FIELD_INTEGER ), + DEFINE_FIELD( flags, FIELD_CHARACTER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ModelLODHeader_t ) + DEFINE_FIELD( numMeshes, FIELD_INTEGER ), + DEFINE_FIELD( meshOffset, FIELD_INTEGER ), + DEFINE_FIELD( switchPoint, FIELD_FLOAT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ModelHeader_t ) + DEFINE_FIELD( numLODs, FIELD_INTEGER ), + DEFINE_FIELD( lodOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( BodyPartHeader_t ) + DEFINE_FIELD( numModels, FIELD_INTEGER ), + DEFINE_FIELD( modelOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( MaterialReplacementHeader_t ) + DEFINE_FIELD( materialID, FIELD_SHORT ), + DEFINE_FIELD( replacementMaterialNameOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( MaterialReplacementListHeader_t ) + DEFINE_FIELD( numReplacements, FIELD_INTEGER ), + DEFINE_FIELD( replacementOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( FileHeader_t ) + DEFINE_FIELD( version, FIELD_INTEGER ), + DEFINE_FIELD( vertCacheSize, FIELD_INTEGER ), + DEFINE_FIELD( maxBonesPerStrip, FIELD_SHORT ), + DEFINE_FIELD( maxBonesPerFace, FIELD_SHORT ), + DEFINE_FIELD( maxBonesPerVert, FIELD_INTEGER ), + DEFINE_FIELD( checkSum, FIELD_INTEGER ), + DEFINE_FIELD( numLODs, FIELD_INTEGER ), + DEFINE_FIELD( materialReplacementListOffset, FIELD_INTEGER ), + DEFINE_FIELD( numBodyParts, FIELD_INTEGER ), + DEFINE_FIELD( bodyPartOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +} // namespace OptimizedModel + +// Data descriptions from phyfile.h +BEGIN_BYTESWAP_DATADESC( phyheader_t ) + DEFINE_FIELD( size, FIELD_INTEGER ), + DEFINE_FIELD( id, FIELD_INTEGER ), + DEFINE_FIELD( solidCount, FIELD_INTEGER ), + DEFINE_FIELD( checkSum, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() diff --git a/common/studiobyteswap.h b/common/studiobyteswap.h new file mode 100644 index 0000000..b6c5a86 --- /dev/null +++ b/common/studiobyteswap.h @@ -0,0 +1,40 @@ +//========= Copyright © 1996-2006, Valve LLC, All rights reserved. ============ +// +// Purpose: StudioMDL byteswapping functions. +// +// $NoKeywords: $ +//============================================================================= +#ifndef STUDIOBYTESWAP_H +#define STUDIOBYTESWAP_H + +#if defined(_WIN32) +#pragma once +#endif + +#include "byteswap.h" +struct studiohdr_t; +class IPhysicsCollision; + +namespace StudioByteSwap +{ +typedef bool (*CompressFunc_t)( const void *pInput, int inputSize, void **pOutput, int *pOutputSize ); + +//void SetTargetBigEndian( bool bigEndian ); +void ActivateByteSwapping( bool bActivate ); +void SourceIsNative( bool bActivate ); +void SetVerbose( bool bVerbose ); +void SetCollisionInterface( IPhysicsCollision *pPhysicsCollision ); + +int ByteswapStudioFile( const char *pFilename, void *pOutBase, int outBaseSize, const void *pFileBase, int fileSize, studiohdr_t *pHdr, CompressFunc_t pCompressFunc = NULL ); +int ByteswapPHY( void *pOutBase, int outBaseSize, const void *pFileBase, int fileSize ); +int ByteswapANI( studiohdr_t* pHdr, void *pOutBase, int outBaseSize, const void *pFileBase, int filesize ); +int ByteswapVVD( void *pOutBase, int outBaseSize, const void *pFileBase, int fileSize ); +int ByteswapVTX( void *pOutBase, int outBaseSize, const void *pFileBase, int fileSize ); +int ByteswapMDL( void *pOutBase, int OutBaseSize, const void *pFileBase, int fileSize ); + +#define BYTESWAP_ALIGNMENT_PADDING 4096 +#define ERROR_MISALIGNED_DATA -1 +#define ERROR_VERSION -2 +} + +#endif // STUDIOBYTESWAP_H diff --git a/common/vgui_surfacelib/ifontsurface.h b/common/vgui_surfacelib/ifontsurface.h new file mode 100644 index 0000000..58c9dc3 --- /dev/null +++ b/common/vgui_surfacelib/ifontsurface.h @@ -0,0 +1,107 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IFONTSURFACE_H +#define IFONTSURFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector2d.h" // must be before the namespace line + +#ifdef CreateFont +#undef CreateFont +#endif + + +// returns true if the surface supports minimize & maximize capabilities +// Numbered this way to prevent interface change in surface. +enum FontFeature_t +{ + FONT_FEATURE_ANTIALIASED_FONTS = 1, + FONT_FEATURE_DROPSHADOW_FONTS = 2, + FONT_FEATURE_OUTLINE_FONTS = 6, +}; + +// adds to the font +enum FontFlags_t +{ + FONTFLAG_NONE, + FONTFLAG_ITALIC = 0x001, + FONTFLAG_UNDERLINE = 0x002, + FONTFLAG_STRIKEOUT = 0x004, + FONTFLAG_SYMBOL = 0x008, + FONTFLAG_ANTIALIAS = 0x010, + FONTFLAG_GAUSSIANBLUR = 0x020, + FONTFLAG_ROTARY = 0x040, + FONTFLAG_DROPSHADOW = 0x080, + FONTFLAG_ADDITIVE = 0x100, + FONTFLAG_OUTLINE = 0x200, + FONTFLAG_CUSTOM = 0x400, // custom generated font - never fall back to asian compatibility mode + FONTFLAG_BITMAP = 0x800, // compiled bitmap font - no fallbacks +}; + +enum FontDrawType_t +{ + // Use the "additive" value from the scheme file + FONT_DRAW_DEFAULT = 0, + + // Overrides + FONT_DRAW_NONADDITIVE, + FONT_DRAW_ADDITIVE, + + FONT_DRAW_TYPE_COUNT = 2, +}; + + +struct FontVertex_t +{ + FontVertex_t() {} + FontVertex_t( const Vector2D &pos, const Vector2D &coord = Vector2D( 0, 0 ) ) + { + m_Position = pos; + m_TexCoord = coord; + } + void Init( const Vector2D &pos, const Vector2D &coord = Vector2D( 0, 0 ) ) + { + m_Position = pos; + m_TexCoord = coord; + } + + Vector2D m_Position; + Vector2D m_TexCoord; +}; + +typedef unsigned long FontHandle_t; + +struct FontCharRenderInfo +{ + // Text pos + int x, y; + // Top left and bottom right + // This is now a pointer to an array maintained by the surface, to avoid copying the data on the 360 + FontVertex_t *verts; + int textureId; + int abcA; + int abcB; + int abcC; + int fontTall; + FontHandle_t currentFont; + // In: + FontDrawType_t drawType; + wchar_t ch; + + // Out + bool valid; + // In/Out (true by default) + bool shouldclip; +}; + + + +#endif // IFONTSURFACE_H diff --git a/common/xbox/xboxstubs.h b/common/xbox/xboxstubs.h new file mode 100644 index 0000000..8c6ffa5 --- /dev/null +++ b/common/xbox/xboxstubs.h @@ -0,0 +1,409 @@ +//========= Copyright 1996-2004, Valve LLC, All rights reserved. ============ +// +// Purpose: Win32 replacements for XBox. +// +//============================================================================= + +#if !defined( XBOXSTUBS_H ) && !defined( _X360 ) +#define XBOXSTUBS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +// Content creation/open flags +#define XCONTENTFLAG_NONE 0x00 +#define XCONTENTFLAG_CREATENEW 0x00 +#define XCONTENTFLAG_CREATEALWAYS 0x00 +#define XCONTENTFLAG_OPENEXISTING 0x00 +#define XCONTENTFLAG_OPENALWAYS 0x00 +#define XCONTENTFLAG_TRUNCATEEXISTING 0x00 + +// Content attributes +#define XCONTENTFLAG_NOPROFILE_TRANSFER 0x00 +#define XCONTENTFLAG_NODEVICE_TRANSFER 0x00 +#define XCONTENTFLAG_STRONG_SIGNED 0x00 +#define XCONTENTFLAG_ALLOWPROFILE_TRANSFER 0x00 +#define XCONTENTFLAG_MOVEONLY_TRANSFER 0x00 + +// XNet flags +#define XNET_GET_XNADDR_PENDING 0x00000000 // Address acquisition is not yet complete +#define XNET_GET_XNADDR_NONE 0x00000001 // XNet is uninitialized or no debugger found +#define XNET_GET_XNADDR_ETHERNET 0x00000002 // Host has ethernet address (no IP address) +#define XNET_GET_XNADDR_STATIC 0x00000004 // Host has statically assigned IP address +#define XNET_GET_XNADDR_DHCP 0x00000008 // Host has DHCP assigned IP address +#define XNET_GET_XNADDR_PPPOE 0x00000010 // Host has PPPoE assigned IP address +#define XNET_GET_XNADDR_GATEWAY 0x00000020 // Host has one or more gateways configured +#define XNET_GET_XNADDR_DNS 0x00000040 // Host has one or more DNS servers configured +#define XNET_GET_XNADDR_ONLINE 0x00000080 // Host is currently connected to online service +#define XNET_GET_XNADDR_TROUBLESHOOT 0x00008000 // Network configuration requires troubleshooting + +// Console device ports +#define XDEVICE_PORT0 0 +#define XDEVICE_PORT1 1 +#define XDEVICE_PORT2 2 +#define XDEVICE_PORT3 3 +#define XUSER_MAX_COUNT 4 +#define XUSER_INDEX_NONE 0x000000FE + +#define XBX_CLR_DEFAULT 0xFF000000 +#define XBX_CLR_WARNING 0x0000FFFF +#define XBX_CLR_ERROR 0x000000FF + +#define XBOX_MINBORDERSAFE 0 +#define XBOX_MAXBORDERSAFE 0 + +typedef enum +{ + XK_NULL, + XK_BUTTON_UP, + XK_BUTTON_DOWN, + XK_BUTTON_LEFT, + XK_BUTTON_RIGHT, + XK_BUTTON_START, + XK_BUTTON_BACK, + XK_BUTTON_STICK1, + XK_BUTTON_STICK2, + XK_BUTTON_A, + XK_BUTTON_B, + XK_BUTTON_X, + XK_BUTTON_Y, + XK_BUTTON_LEFT_SHOULDER, + XK_BUTTON_RIGHT_SHOULDER, + XK_BUTTON_LTRIGGER, + XK_BUTTON_RTRIGGER, + XK_STICK1_UP, + XK_STICK1_DOWN, + XK_STICK1_LEFT, + XK_STICK1_RIGHT, + XK_STICK2_UP, + XK_STICK2_DOWN, + XK_STICK2_LEFT, + XK_STICK2_RIGHT, + XK_BUTTON_INACTIVE_START, + XK_MAX_KEYS, +} xKey_t; + +//typedef enum +//{ +// XVRB_NONE, // off +// XVRB_ERROR, // fatal error +// XVRB_ALWAYS, // no matter what +// XVRB_WARNING, // non-fatal warnings +// XVRB_STATUS, // status reports +// XVRB_ALL, +//} xverbose_e; + +typedef unsigned short WORD; +typedef unsigned long DWORD; + +#ifndef POSIX +typedef void* HANDLE; +typedef unsigned __int64 ULONGLONG; +#endif + +#ifdef POSIX +typedef int32 COLORREF; +#endif + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE ((HANDLE)-1) +#endif + +/* +* Internet address (old style... should be updated) +*/ +#ifdef _POSIX +struct ip4_addr { + union { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +}; +typedef struct ip4_addr IN_ADDR; +#else +#ifndef s_addr +/* +* Internet address (old style... should be updated) +*/ +struct in_addr { + union { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; +typedef struct in_addr IN_ADDR; +#endif +#endif + +typedef unsigned int XIN_ADDR; + +typedef struct { + IN_ADDR ina; // IP address (zero if not static/DHCP) + IN_ADDR inaOnline; // Online IP address (zero if not online) + WORD wPortOnline; // Online port + BYTE abEnet[6]; // Ethernet MAC address + BYTE abOnline[20]; // Online identification +} XNADDR; + +typedef uint64 XUID; + +typedef struct { + BYTE ab[8]; // xbox to xbox key identifier +} XNKID; + +typedef struct { + BYTE ab[16]; // xbox to xbox key exchange key +} XNKEY; + +typedef struct _XSESSION_INFO +{ + XNKID sessionID; // 8 bytes + XNADDR hostAddress; // 36 bytes + XNKEY keyExchangeKey; // 16 bytes +} XSESSION_INFO, *PXSESSION_INFO; + +typedef struct _XUSER_DATA +{ + BYTE type; + + union + { + int nData; // XUSER_DATA_TYPE_INT32 + int64 i64Data; // XUSER_DATA_TYPE_INT64 + double dblData; // XUSER_DATA_TYPE_DOUBLE + struct // XUSER_DATA_TYPE_UNICODE + { + uint cbData; // Includes null-terminator + char * pwszData; + } string; + float fData; // XUSER_DATA_TYPE_FLOAT + struct // XUSER_DATA_TYPE_BINARY + { + uint cbData; + char * pbData; + } binary; + }; +} XUSER_DATA, *PXUSER_DATA; + +typedef struct _XUSER_PROPERTY +{ + DWORD dwPropertyId; + XUSER_DATA value; +} XUSER_PROPERTY, *PXUSER_PROPERTY; + +typedef struct _XUSER_CONTEXT +{ + DWORD dwContextId; + DWORD dwValue; +} XUSER_CONTEXT, *PXUSER_CONTEXT; + +typedef struct _XSESSION_SEARCHRESULT +{ + XSESSION_INFO info; + DWORD dwOpenPublicSlots; + DWORD dwOpenPrivateSlots; + DWORD dwFilledPublicSlots; + DWORD dwFilledPrivateSlots; + DWORD cProperties; + DWORD cContexts; + PXUSER_PROPERTY pProperties; + PXUSER_CONTEXT pContexts; +} XSESSION_SEARCHRESULT, *PXSESSION_SEARCHRESULT; + +typedef struct _XSESSION_SEARCHRESULT_HEADER +{ + DWORD dwSearchResults; + XSESSION_SEARCHRESULT *pResults; +} XSESSION_SEARCHRESULT_HEADER, *PXSESSION_SEARCHRESULT_HEADER; + +typedef struct _XSESSION_REGISTRANT +{ + uint64 qwMachineID; + DWORD bTrustworthiness; + DWORD bNumUsers; + XUID *rgUsers; + +} XSESSION_REGISTRANT; + +typedef struct _XSESSION_REGISTRATION_RESULTS +{ + DWORD wNumRegistrants; + XSESSION_REGISTRANT *rgRegistrants; +} XSESSION_REGISTRATION_RESULTS, *PXSESSION_REGISTRATION_RESULTS; + +typedef struct { + BYTE bFlags; + BYTE bReserved; + WORD cProbesXmit; + WORD cProbesRecv; + WORD cbData; + BYTE * pbData; + WORD wRttMinInMsecs; + WORD wRttMedInMsecs; + DWORD dwUpBitsPerSec; + DWORD dwDnBitsPerSec; +} XNQOSINFO; + +typedef struct { + uint cxnqos; + uint cxnqosPending; + XNQOSINFO axnqosinfo[1]; +} XNQOS; + +#define XSESSION_CREATE_HOST 0 +#define XSESSION_CREATE_USES_ARBITRATION 0 +#define XNET_QOS_LISTEN_ENABLE 0 +#define XNET_QOS_LISTEN_DISABLE 0 +#define XNET_QOS_LISTEN_SET_DATA 0 + +#define XUSER_DATA_TYPE_CONTEXT ((BYTE)0) +#define XUSER_DATA_TYPE_INT32 ((BYTE)1) +#define XUSER_DATA_TYPE_INT64 ((BYTE)2) +#define XUSER_DATA_TYPE_DOUBLE ((BYTE)3) +#define XUSER_DATA_TYPE_UNICODE ((BYTE)4) +#define XUSER_DATA_TYPE_FLOAT ((BYTE)5) +#define XUSER_DATA_TYPE_BINARY ((BYTE)6) +#define XUSER_DATA_TYPE_DATETIME ((BYTE)7) +#define XUSER_DATA_TYPE_NULL ((BYTE)0xFF) + +#define XPROFILE_TITLE_SPECIFIC1 0x3FFF +#define XPROFILE_TITLE_SPECIFIC2 0x3FFE +#define XPROFILE_TITLE_SPECIFIC3 0x3FFD +#define XPROFILE_SETTING_MAX_SIZE 1000 + +FORCEINLINE DWORD XBX_GetNumGameUsers() { return 1; } +FORCEINLINE void XBX_ProcessEvents() {} +FORCEINLINE void XBX_DispatchEventsQueue() {} +FORCEINLINE unsigned int XBX_GetSystemTime() { return 0; } +FORCEINLINE DWORD XBX_GetPrimaryUserId() { return 0; } +FORCEINLINE void XBX_SetPrimaryUserId( DWORD idx ) {} +FORCEINLINE void XBX_ResetStorageDeviceInfo() {} +FORCEINLINE DWORD XBX_DescribeStorageDevice( DWORD nStorageID ) { return 1; } +FORCEINLINE DWORD XBX_GetStorageDeviceId(int) { return 0; } +FORCEINLINE void XBX_SetStorageDeviceId( int, DWORD ) {} +FORCEINLINE const char *XBX_GetLanguageString() { return ""; } +FORCEINLINE bool XBX_IsLocalized() { return false; } +FORCEINLINE bool XBX_IsAudioLocalized() { return false; } +FORCEINLINE const char *XBX_GetNextSupportedLanguage( const char *pLanguage, bool *pbHasAudio ) { return NULL; } +FORCEINLINE bool XBX_IsRestrictiveLanguage() { return false; } + +FORCEINLINE int XBX_GetUserId( int nSlot ) { return nSlot; } +FORCEINLINE void XBX_SetUserId( int nSlot, int idx ) {} + + +#define XCONTENT_MAX_DISPLAYNAME_LENGTH 128 +#define XCONTENT_MAX_FILENAME_LENGTH 42 + +#define XBX_INVALID_STORAGE_ID ((DWORD) -1) +#define XBX_STORAGE_DECLINED ((DWORD) -2) + +enum XUSER_SIGNIN_STATE +{ + eXUserSigninState_NotSignedIn, + eXUserSigninState_SignedInLocally, + eXUserSigninState_SignedInToLive, +}; + +#if (defined(_POSIX)) +typedef size_t ULONG_PTR; +#else +typedef _W64 unsigned long ULONG_PTR; +#endif +typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR; + + +typedef void * PXOVERLAPPED_COMPLETION_ROUTINE; + + +#ifndef _POSIX +typedef struct _XOVERLAPPED { + ULONG_PTR InternalLow; + ULONG_PTR InternalHigh; + ULONG_PTR InternalContext; + HANDLE hEvent; + PXOVERLAPPED_COMPLETION_ROUTINE pCompletionRoutine; + DWORD_PTR dwCompletionContext; + DWORD dwExtendedError; +} XOVERLAPPED, *PXOVERLAPPED; +#endif + +#ifndef MAX_RICHPRESENCE_SIZE +#define MAX_RICHPRESENCE_SIZE 64 +#endif + +#ifndef XUSER_NAME_SIZE +#define XUSER_NAME_SIZE 16 +#endif + +#ifndef GPU_RESOLVE_ALIGNMENT +#define GPU_RESOLVE_ALIGNMENT 8 +#endif + +#define XCONTENT_MAX_DISPLAYNAME_LENGTH 128 +#define XCONTENT_MAX_FILENAME_LENGTH 42 +#define XCONTENTDEVICE_MAX_NAME_LENGTH 27 +typedef DWORD XCONTENTDEVICEID, *PXCONTENTDEVICEID; +typedef struct _XCONTENT_DATA +{ + XCONTENTDEVICEID DeviceID; + DWORD dwContentType; + wchar_t szDisplayName[XCONTENT_MAX_DISPLAYNAME_LENGTH]; + char szFileName[XCONTENT_MAX_FILENAME_LENGTH]; +} XCONTENT_DATA, *PXCONTENT_DATA; + +#define X_CONTEXT_PRESENCE 0x00010001 +#define X_CONTEXT_GAME_TYPE 0x0001000A +#define X_CONTEXT_GAME_MODE 0x0001000B + +#define X_PROPERTY_RANK 0x00011001 +#define X_PROPERTY_GAMERNAME 0x00011002 +#define X_PROPERTY_SESSION_ID 0x00011003 + +// System attributes used in matchmaking queries +#define X_PROPERTY_GAMER_ZONE 0x00011101 +#define X_PROPERTY_GAMER_COUNTRY 0x00011102 +#define X_PROPERTY_GAMER_LANGUAGE 0x00011103 +#define X_PROPERTY_GAMER_RATING 0x00011104 +#define X_PROPERTY_GAMER_MU 0x00011105 +#define X_PROPERTY_GAMER_SIGMA 0x00011106 +#define X_PROPERTY_GAMER_PUID 0x00011107 +#define X_PROPERTY_AFFILIATE_SCORE 0x00011108 +#define X_PROPERTY_GAMER_HOSTNAME 0x00011109 + +// Properties used to write to skill leaderboards +#define X_PROPERTY_RELATIVE_SCORE 0x0001100A +#define X_PROPERTY_SESSION_TEAM 0x0001100B + +// Properties written at the session level to override TrueSkill parameters +#define X_PROPERTY_PLAYER_PARTIAL_PLAY_PERCENTAGE 0x0001100C +#define X_PROPERTY_PLAYER_SKILL_UPDATE_WEIGHTING_FACTOR 0x0001100D +#define X_PROPERTY_SESSION_SKILL_BETA 0x0001100E +#define X_PROPERTY_SESSION_SKILL_TAU 0x0001100F +#define X_PROPERTY_SESSION_SKILL_DRAW_PROBABILITY 0x00011010 + +// Attachment size is written to a leaderboard when the entry qualifies for +// a gamerclip. The rating can be retrieved via XUserEstimateRankForRating. +#define X_PROPERTY_ATTACHMENT_SIZE 0x00011011 + +// Values for X_CONTEXT_GAME_TYPE +#define X_CONTEXT_GAME_TYPE_RANKED 0 +#define X_CONTEXT_GAME_TYPE_STANDARD 1 + +#endif // XBOXSTUBS_H diff --git a/engine/host.h b/engine/host.h new file mode 100644 index 0000000..5c58f43 --- /dev/null +++ b/engine/host.h @@ -0,0 +1,221 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( HOST_H ) +#define HOST_H +#ifdef _WIN32 +#pragma once +#endif + +#include "convar.h" +#ifdef _PS3 +#include "tls_ps3.h" +#endif + + +#define SCRIPT_DIR "scripts/" + +struct model_t; +struct AudioState_t; +class KeyValues; +class CMsg_CVars; + +class CCommonHostState +{ +public: + CCommonHostState() : worldmodel( NULL ), worldbrush( NULL ), interval_per_tick( 0.0f ), max_splitscreen_players( 1 ), max_splitscreen_players_clientdll( 1 ) {} + + // cl_entitites[0].model + model_t *worldmodel; + struct worldbrushdata_t *worldbrush; + // Tick interval for game + float interval_per_tick; + // 1, unless a game supports split screen, then probably 2 or 4 (4 is the max allowable) + int max_splitscreen_players; + // This is the # the client .dll thinks is the max, it might be > max_splitscreen_players in -tools mode, etc. + int max_splitscreen_players_clientdll; + void SetWorldModel( model_t *pModel ); +}; + +extern CCommonHostState host_state; + +//============================================================================= +// the host system specifies the base of the directory tree, the mod + base mod +// and the amount of memory available for the program to use +struct engineparms_t +{ + engineparms_t() : basedir( NULL ), mod( NULL ), game( NULL ), memsize( 0u ) {} + + char *basedir; // Executable directory ("c:/program files/half-life 2", for example) + char *mod; // Mod name ("cstrike", for example) + char *game; // Root game name ("hl2", for example, in the case of cstrike) + unsigned int memsize; +}; +extern engineparms_t host_parms; + + +//----------------------------------------------------------------------------- +// Human readable methods to get at engineparms info +//----------------------------------------------------------------------------- +inline const char *GetCurrentMod() +{ + return host_parms.mod; +} + +inline const char *GetCurrentGame() +{ + return host_parms.game; +} + +inline const char *GetBaseDirectory() +{ + return host_parms.basedir; +} + + +//============================================================================= + +// +// host +// FIXME, move all this crap somewhere else +// + +// when we are using -fork, and we are the parent controlling process, we want to avoid a bunch of stuff, such as registering ourselves with the xlsp master or steam, etc +#define FORK_ID_PARENT_PROCESS -1 +extern int g_nForkID; +extern int g_nSocketToParentProcess; // -1 if we are not a child, otherwise its a socket that talks to the parent process + +FORCEINLINE bool IsChildProcess( void ) +{ + return g_nForkID > 0; +} +#ifdef _LINUX +void SendStringToParentProcess( char const *pMsg ); +#endif + + + +extern ConVar developer; +extern bool host_initialized; // true if into command execution +extern float host_frametime; +extern float host_frametime_unbounded; +extern float host_frametime_unscaled; +extern float host_frametime_stddeviation; +extern float host_framestarttime_stddeviation; +extern float host_frameendtime_computationduration; +extern int host_framecount; // incremented every frame, never reset +extern double realtime; // not bounded in any way, changed at +// start of every frame, never reset +void Host_Error (PRINTF_FORMAT_STRING const char *error, ...) FMTFUNCTION( 1, 2 ); +void Host_EndGame (bool bShowMainMenu, PRINTF_FORMAT_STRING const char *message, ...) FMTFUNCTION( 2, 3 ); + +// user message +#define MAX_USER_MSG_BITS 12 +#define MAX_USER_MSG_DATA ( ( 1 << ( MAX_USER_MSG_BITS - 3 ) ) - 1 ) + +// build info +// day counter from Sep 30 2003 +extern int build_number( void ); + + +// Choke local client's/server's packets? +extern ConVar host_limitlocal; +// Print a debug message when the client or server cache is missed +extern ConVar host_showcachemiss; + +//#if !defined( LINUX ) +extern bool g_bInEditMode; +extern bool g_bInCommentaryMode; +//#endif + +extern bool g_bLowViolence; +extern KeyValues* g_pLaunchOptions; + +// Returns true if host is not single stepping/pausing through code/ +// FIXME: Remove from final, retail version of code. +bool Host_ShouldRun( void ); +void Host_FreeToLowMark( bool server ); +void Host_FreeStateAndWorld( bool server ); +void Host_Disconnect( bool bShowMainMenu ); +void Host_RunFrame( float time ); +void Host_DumpMemoryStats( void ); +void Host_UpdateMapList( void ); +float Host_GetSoundDuration( const char *pSample ); +bool Host_IsSinglePlayerGame( void ); +bool Host_IsLocalServer(); +int Host_GetServerCount( void ); +bool Host_AllowQueuedMaterialSystem( bool bAllow ); +void Host_EnsureHostNameSet(); +void Host_BeginThreadedSound(); + +// Force the voice stuff to send a packet out. +// bFinal is true when the user is done talking. +void CL_SendVoicePacket(bool bFinal); + +bool Host_IsSecureServerAllowed(); +void Host_DisallowSecureServers(); +bool Host_AllowLoadModule( const char *pFilename, const char *pPathID, bool bAllowUnknown ); + +// Accumulated filtered time on machine ( i.e., host_framerate can alter host_time ) +extern float host_time; + +struct NetMessageCvar_t; + +void Host_BuildConVarUpdateMessage( CMsg_CVars *rCvarList, int flags, bool nonDefault ); +void Host_BuildUserInfoUpdateMessage( int nSplitScreenSlot, CMsg_CVars *rCvarList, bool nonDefault ); +char const *Host_CleanupConVarStringValue( char const *invalue ); +void Host_SetAudioState( const AudioState_t &audioState ); + +bool CheckVarRange_Generic( ConVar *pVar, int minVal, int maxVal ); + +// Total ticks run +extern int host_tickcount; +// Number of ticks being run this frame +extern int host_frameticks; +// Which tick are we currently on for this frame +extern int host_currentframetick; + +// PERFORMANCE INFO +#define MIN_FPS 0.1 // Host minimum fps value for maxfps. +#define MAX_FPS 1000.0 // Upper limit for maxfps. + +#define MAX_FRAMETIME 0.1 +#define MIN_FRAMETIME 0.001 +#define MAX_TOOLS_FRAMETIME 2.0 +#define MIN_TOOLS_FRAMETIME 0.001 + +#define TIME_TO_TICKS( dt ) ( (int)( 0.5f + (float)(dt) / host_state.interval_per_tick ) ) +#define TICKS_TO_TIME( dt ) ( host_state.interval_per_tick * (float)(dt) ) + +// Set by the game DLL to tell us to do the same timing tricks as timedemo. +extern bool g_bDedicatedServerBenchmarkMode; +extern uint GetSteamAppID(); + +#include "steam/steamclientpublic.h" +extern EUniverse GetSteamUniverse(); + +// +// \return true iff PS3 (and only PS3) and Quitting (on user request or disk eject or other critical condition when we absolutely must quit within 10 seconds) +// + +// +// \return true iff PS3 (and only PS3) and Quitting (on user request or disk eject or other critical condition when we absolutely must quit within 10 seconds) +// +inline bool IsPS3QuitRequested() +{ +#ifdef _PS3 + return GetTLSGlobals()->bNormalQuitRequested; +#else + // if not on PS3, do not disturb the old logic of host_state which has a lot of dependencies, because other platforms do not require the game to quit cleanly + return false; +#endif +} + + + +#endif // HOST_H diff --git a/engine/iblackbox.h b/engine/iblackbox.h new file mode 100644 index 0000000..ce0b836 --- /dev/null +++ b/engine/iblackbox.h @@ -0,0 +1,19 @@ +#ifndef IBLACKBOX_H +#define IBLACKBOX_H + +#define BLACKBOX_INTERFACE_VERSION "BlackBoxVersion001" + +class IBlackBox +{ +public: + virtual void Record(int type, const char *fmt) = 0; + virtual void SetLimit(int type, unsigned int count) = 0; + virtual const char *Get(int type, unsigned int index) = 0; + virtual int Count(int type) = 0; + virtual void Flush(int type) = 0; + + virtual const char *GetTypeName(int type) = 0; + virtual int GetTypeCount() = 0; +}; + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..90a1fa4 --- /dev/null +++ b/main.cpp @@ -0,0 +1,185 @@ +//===========================================================================// +// +// Authors: Orsell & Nanoman2525 & NULLderef +// Purpose: WorkshopStopper9000 plugin +// +//===========================================================================// + +#include "main.hpp" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//--------------------------------------------------------------------------------- +// The plugin is a static singleton that is exported as an interface +//--------------------------------------------------------------------------------- +CWSS9000Plugin g_WSS9000Plugin; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CWSS9000Plugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_WSS9000Plugin); + +// Debug ConVars +ConVar wss9000_enable("wss9000_enable", "1", FCVAR_NONE, "Whether or not the WorkshopStopper9000 interlocking workshop stopping mechanism."); +ConVar wss9000_developer("wss9000_developer", "0", FCVAR_NONE, "Enable for developer messages."); + +//--------------------------------------------------------------------------------- +// Purpose: Logging for the plugin by adding a prefix and line break. +// Max character limit of 1024 characters. +// level: 0 = Msg/DevMsg, 1 = Warning/DevWarning +//--------------------------------------------------------------------------------- +void Log(int level, bool dev, const char* pMsgFormat, ...) +{ + va_list argptr; + char szFormattedText[1024]; + va_start(argptr, pMsgFormat); + V_vsnprintf(szFormattedText, sizeof(szFormattedText), pMsgFormat, argptr); + va_end(argptr); + + char completeMsg[1024]; + V_snprintf(completeMsg, sizeof(completeMsg), "(WorkshopStopper9000 PLUGIN): %s\n", szFormattedText); + + if (dev && !wss9000_developer.GetBool()) + { + return; + } + + switch (level) + { + case 0: + ConColorMsg(WSS9000_PLUGIN_CONSOLE_COLOR, completeMsg); + return; + case 1: + Warning(completeMsg); + return; + default: + Warning("(WorkshopStopper9000 PLUGIN): Log level set outside of 0-1, \"%i\", defaulting to ConColorMsg().\n", level); + ConColorMsg(WSS9000_PLUGIN_CONSOLE_COLOR, completeMsg); + return; + } +} + +//--------------------------------------------------------------------------------- +// Purpose: constructor +//--------------------------------------------------------------------------------- +CWSS9000Plugin::CWSS9000Plugin() +{ + this->m_bPluginLoaded = false; + this->m_bNoUnload = false; // If we fail to load, we don't want to run anything on Unload() to get what the error was. +} + +//--------------------------------------------------------------------------------- +// Purpose: destructor +//--------------------------------------------------------------------------------- +CWSS9000Plugin::~CWSS9000Plugin() {} + +//--------------------------------------------------------------------------------- +// Purpose: Description of plugin outputted when the "plugin_print" console command is executed. +//--------------------------------------------------------------------------------- +const char* CWSS9000Plugin::GetPluginDescription(void) +{ + return "WorkshopStopper9000 Plugin | Plugin Version: " WSS9000_PLUGIN_VERSION; +} + +// STOP THEM WORKSHOP DOWNLOADS! +class CUGCFileRequestManager; +void (__fastcall* CUGCFileRequestManager__Update_orig)(CUGCFileRequestManager* thisptr); +void __fastcall CUGCFileRequestManager__Update_hook(CUGCFileRequestManager* thisptr) { return; } + +//--------------------------------------------------------------------------------- +// Purpose: Called when the plugin is loaded, initialization process. +// Loads the interfaces we need from the engine and applies our patches. +//--------------------------------------------------------------------------------- +bool CWSS9000Plugin::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory) +{ + if (m_bPluginLoaded) + { + Log(1, false, "Already loaded!"); + m_bNoUnload = true; + return false; + } + + Log(0, false, "Loading plugin..."); + + Log(0, true, "Connecting tier libraries..."); + ConnectTier1Libraries(&interfaceFactory, 1); + ConnectTier2Libraries(&interfaceFactory, 1); + + // big ol' try catch because game has a TerminateProcess handler for exceptions... + // why this wasn't here is mystifying, - 10/2024 NULLderef + try { + // MinHook initialization and hooking + Log(0, true, "Initializing MinHook and hooking functions..."); + MH_Initialize(); + + // STOP THEM WORKSHOP DOWNLOADS: MinHook Edition +#if _WIN32 + MH_CreateHook( + Memory::Scanner::Scan(CLIENTDLL, "55 8B EC 81 EC 48 01 00 00 57"), + &CUGCFileRequestManager__Update_hook, (void**)&CUGCFileRequestManager__Update_orig + ); +#else // Linux + MH_CreateHook( + Memory::Scanner::Scan(CLIENTDLL, "55 89 E5 57 56 53 81 EC 5C 01 00 00 8B 5D"), + &CUGCFileRequestManager__Update_hook, (void**)&CUGCFileRequestManager__Update_orig + ); +#endif // _WIN32 + + MH_EnableHook(MH_ALL_HOOKS); + + Log(0, false, "Loaded plugin! Hooray! :D"); + m_bPluginLoaded = true; + } catch (const std::exception& ex) { + Log(0, false, "Failed to load plugin! :( Exception: \"%s\"", ex.what()); + this->m_bNoUnload = true; + return false; + } + + return true; +} + +//--------------------------------------------------------------------------------- +// Purpose: Called when the plugin is turning off/unloading. +//--------------------------------------------------------------------------------- +void CWSS9000Plugin::Unload(void) +{ + // If the plugin errors for some reason, prevent it from unloading to get what the error was. + if (m_bNoUnload) + { + m_bNoUnload = false; + return; + } + + Log(0, false, "Unloading Plugin..."); + + Log(0, true, "Disconnecting hooked functions and uninitializing MinHook..."); + MH_DisableHook(MH_ALL_HOOKS); + MH_Uninitialize(); + + m_bPluginLoaded = false; + Log(0, false, "Plugin unloaded! Goodbye!"); +} + +//--------------------------------------------------------------------------------- +// Purpose: Unused callbacks +//--------------------------------------------------------------------------------- +#pragma region UNUSED_CALLBACKS +void CWSS9000Plugin::SetCommandClient(int index) {} +void CWSS9000Plugin::ServerActivate(edict_t* pEdictList, int edictCount, int clientMax) {} +void CWSS9000Plugin::LevelInit(char const* pMapName) {} +PLUGIN_RESULT CWSS9000Plugin::ClientCommand(edict_t* pEntity, const CCommand& args) { return PLUGIN_CONTINUE; } +void CWSS9000Plugin::FireGameEvent(IGameEvent* event) {} +void CWSS9000Plugin::ClientActive(edict_t* pEntity) {} +void CWSS9000Plugin::GameFrame(bool simulating) {} +void CWSS9000Plugin::LevelShutdown(void) {} +void CWSS9000Plugin::Pause(void) {} +void CWSS9000Plugin::UnPause(void) {} +void CWSS9000Plugin::ClientDisconnect(edict_t* pEntity) {} +void CWSS9000Plugin::ClientFullyConnect(edict_t* pEntity) {} // Purpose: Called when a player is fully connected to the server. Player entity still has not spawned in so manipulation is not possible. +void CWSS9000Plugin::ClientPutInServer(edict_t* pEntity, char const* playername) {} +void CWSS9000Plugin::ClientSettingsChanged(edict_t* pEdict) {} +PLUGIN_RESULT CWSS9000Plugin::ClientConnect(bool* bAllowConnect, edict_t* pEntity, const char* pszName, const char* pszAddress, char* reject, int maxrejectlen) { return PLUGIN_CONTINUE; } +PLUGIN_RESULT CWSS9000Plugin::NetworkIDValidated(const char* pszUserName, const char* pszNetworkID) { return PLUGIN_CONTINUE; } +void CWSS9000Plugin::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t* pPlayerEntity, EQueryCvarValueStatus eStatus, const char* pCvarName, const char* pCvarValue) {} +void CWSS9000Plugin::OnEdictAllocated(edict_t* edict) {} +void CWSS9000Plugin::OnEdictFreed(const edict_t* edict) {} +bool CWSS9000Plugin::BNetworkCryptKeyCheckRequired(uint32 unFromIP, uint16 usFromPort, uint32 unAccountIdProvidedByClient, bool bClientWantsToUseCryptKey) { return false; } +bool CWSS9000Plugin::BNetworkCryptKeyValidate(uint32 unFromIP, uint16 usFromPort, uint32 unAccountIdProvidedByClient, int nEncryptionKeyIndexFromClient, int numEncryptedBytesFromClient, byte* pbEncryptedBufferFromClient, byte* pbPlainTextKeyForNetchan) { return true; } +#pragma endregion \ No newline at end of file diff --git a/main.hpp b/main.hpp new file mode 100644 index 0000000..84d8aff --- /dev/null +++ b/main.hpp @@ -0,0 +1,70 @@ +//===========================================================================// +// +// Authors: Orsell & Nanoman2525 & NULLderef +// Purpose: WorkshopStopper9000 plugin +// +//===========================================================================// + +#include "minhook/include/MinHook.h" +#include "eiface.h" +#include "cdll_int.h" +#include "igameevents.h" +#include "engine/iserverplugin.h" + +#include "scanner.hpp" + +#ifdef _WIN32 +#pragma once +#include +#endif + +#include + +#define WSS9000_PLUGIN_VERSION "1.0.0" // Update this when a new version of the plugin is released +#define WSS9000_PLUGIN_CONSOLE_COLOR Color(100, 192, 252, 255) // Light Blue + +//--------------------------------------------------------------------------------- +// Purpose: Portal 2: Multiplayer Mod server plugin class +//--------------------------------------------------------------------------------- + +class CWSS9000Plugin : public IServerPluginCallbacks +{ +public: + CWSS9000Plugin(); + ~CWSS9000Plugin(); + + // IServerPluginCallbacks methods + virtual bool Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory); + virtual void Unload(void); + virtual void Pause(void); + virtual void UnPause(void); + virtual const char* GetPluginDescription(void); + virtual void LevelInit(char const* pMapName); + virtual void ServerActivate(edict_t* pEdictList, int edictCount, int clientMax); + virtual void GameFrame(bool simulating); + virtual void LevelShutdown(void); + virtual void ClientActive(edict_t* pEntity); + virtual void ClientDisconnect(edict_t* pEntity); + virtual void ClientPutInServer(edict_t* pEntity, char const* playername); + virtual void SetCommandClient(int index); + virtual void ClientSettingsChanged(edict_t* pEdict); + virtual PLUGIN_RESULT ClientConnect(bool* bAllowConnect, edict_t* pEntity, const char* pszName, const char* pszAddress, char* reject, int maxrejectlen); + virtual void ClientFullyConnect(edict_t* pEntity); + virtual PLUGIN_RESULT ClientCommand(edict_t* pEntity, const CCommand& args); + virtual PLUGIN_RESULT NetworkIDValidated(const char* pszUserName, const char* pszNetworkID); + virtual void OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t* pPlayerEntity, EQueryCvarValueStatus eStatus, const char* pCvarName, const char* pCvarValue); + virtual void OnEdictAllocated(edict_t* edict); + virtual void OnEdictFreed(const edict_t* edict); + virtual bool BNetworkCryptKeyCheckRequired(uint32 unFromIP, uint16 usFromPort, uint32 unAccountIdProvidedByClient, bool bClientWantsToUseCryptKey); + virtual bool BNetworkCryptKeyValidate(uint32 unFromIP, uint16 usFromPort, uint32 unAccountIdProvidedByClient, int nEncryptionKeyIndexFromClient, int numEncryptedBytesFromClient, byte* pbEncryptedBufferFromClient, byte* pbPlainTextKeyForNetchan); + + // IGameEventListener2 methods + virtual void FireGameEvent(IGameEvent* event); + +private: + + bool m_bPluginLoaded; + bool m_bNoUnload; +}; + +extern CWSS9000Plugin g_WSS9000Plugin; diff --git a/minhook/include/MinHook.h b/minhook/include/MinHook.h new file mode 100644 index 0000000..15c0a87 --- /dev/null +++ b/minhook/include/MinHook.h @@ -0,0 +1,186 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * 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 HOLDER + * 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. + */ + +#pragma once + +#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) + #error MinHook supports only x86 and x64 systems. +#endif + +#include + +// MinHook Error Codes. +typedef enum MH_STATUS +{ + // Unknown error. Should not be returned. + MH_UNKNOWN = -1, + + // Successful. + MH_OK = 0, + + // MinHook is already initialized. + MH_ERROR_ALREADY_INITIALIZED, + + // MinHook is not initialized yet, or already uninitialized. + MH_ERROR_NOT_INITIALIZED, + + // The hook for the specified target function is already created. + MH_ERROR_ALREADY_CREATED, + + // The hook for the specified target function is not created yet. + MH_ERROR_NOT_CREATED, + + // The hook for the specified target function is already enabled. + MH_ERROR_ENABLED, + + // The hook for the specified target function is not enabled yet, or already + // disabled. + MH_ERROR_DISABLED, + + // The specified pointer is invalid. It points the address of non-allocated + // and/or non-executable region. + MH_ERROR_NOT_EXECUTABLE, + + // The specified target function cannot be hooked. + MH_ERROR_UNSUPPORTED_FUNCTION, + + // Failed to allocate memory. + MH_ERROR_MEMORY_ALLOC, + + // Failed to change the memory protection. + MH_ERROR_MEMORY_PROTECT, + + // The specified module is not loaded. + MH_ERROR_MODULE_NOT_FOUND, + + // The specified function is not found. + MH_ERROR_FUNCTION_NOT_FOUND +} +MH_STATUS; + +// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, +// MH_QueueEnableHook or MH_QueueDisableHook. +#define MH_ALL_HOOKS NULL + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the MinHook library. You must call this function EXACTLY ONCE + // at the beginning of your program. + MH_STATUS WINAPI MH_Initialize(VOID); + + // Uninitialize the MinHook library. You must call this function EXACTLY + // ONCE at the end of your program. + MH_STATUS WINAPI MH_Uninitialize(VOID); + + // Creates a Hook for the specified target function, in disabled state. + // Parameters: + // pTarget [in] A pointer to the target function, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); + + // Creates a Hook for the specified API function, in disabled state. + // Parameters: + // pszModule [in] A pointer to the loaded module name which contains the + // target function. + // pszTarget [in] A pointer to the target function name, which will be + // overridden by the detour function. + // pDetour [in] A pointer to the detour function, which will override + // the target function. + // ppOriginal [out] A pointer to the trampoline function, which will be + // used to call the original target function. + // This parameter can be NULL. + // ppTarget [out] A pointer to the target function, which will be used + // with other functions. + // This parameter can be NULL. + MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); + + // Removes an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); + + // Enables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // enabled in one go. + MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); + + // Disables an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // disabled in one go. + MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); + + // Queues to enable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be enabled. + MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); + + // Queues to disable an already created hook. + // Parameters: + // pTarget [in] A pointer to the target function. + // If this parameter is MH_ALL_HOOKS, all created hooks are + // queued to be disabled. + MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); + + // Applies all queued changes in one go. + MH_STATUS WINAPI MH_ApplyQueued(VOID); + + // Translates the MH_STATUS to its name as a string. + const char * WINAPI MH_StatusToString(MH_STATUS status); + +#ifdef __cplusplus +} +#endif + diff --git a/public/.editorconfig b/public/.editorconfig new file mode 100644 index 0000000..b4670ec --- /dev/null +++ b/public/.editorconfig @@ -0,0 +1,69 @@ +# Visual Studio generated .editorconfig file with C++ settings. +root = true + +[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] + +# Visual C++ Code Style settings + +cpp_generate_documentation_comments = xml + +# Visual C++ Formatting settings + +cpp_indent_braces = false +cpp_indent_multi_line_relative_to = innermost_parenthesis +cpp_indent_within_parentheses = indent +cpp_indent_preserve_within_parentheses = true +cpp_indent_case_contents = true +cpp_indent_case_labels = false +cpp_indent_case_contents_when_block = false +cpp_indent_lambda_braces_when_parameter = true +cpp_indent_goto_labels = one_left +cpp_indent_preprocessor = leftmost_column +cpp_indent_access_specifiers = false +cpp_indent_namespace_contents = true +cpp_indent_preserve_comments = false +cpp_new_line_before_open_brace_namespace = ignore +cpp_new_line_before_open_brace_type = ignore +cpp_new_line_before_open_brace_function = ignore +cpp_new_line_before_open_brace_block = ignore +cpp_new_line_before_open_brace_lambda = ignore +cpp_new_line_scope_braces_on_separate_lines = false +cpp_new_line_close_brace_same_line_empty_type = false +cpp_new_line_close_brace_same_line_empty_function = false +cpp_new_line_before_catch = true +cpp_new_line_before_else = true +cpp_new_line_before_while_in_do_while = false +cpp_space_before_function_open_parenthesis = remove +cpp_space_within_parameter_list_parentheses = false +cpp_space_between_empty_parameter_list_parentheses = false +cpp_space_after_keywords_in_control_flow_statements = true +cpp_space_within_control_flow_statement_parentheses = false +cpp_space_before_lambda_open_parenthesis = false +cpp_space_within_cast_parentheses = false +cpp_space_after_cast_close_parenthesis = false +cpp_space_within_expression_parentheses = false +cpp_space_before_block_open_brace = true +cpp_space_between_empty_braces = false +cpp_space_before_initializer_list_open_brace = false +cpp_space_within_initializer_list_braces = true +cpp_space_preserve_in_initializer_list = true +cpp_space_before_open_square_bracket = false +cpp_space_within_square_brackets = false +cpp_space_before_empty_square_brackets = false +cpp_space_between_empty_square_brackets = false +cpp_space_group_square_brackets = true +cpp_space_within_lambda_brackets = false +cpp_space_between_empty_lambda_brackets = false +cpp_space_before_comma = false +cpp_space_after_comma = true +cpp_space_remove_around_member_operators = true +cpp_space_before_inheritance_colon = true +cpp_space_before_constructor_colon = true +cpp_space_remove_before_semicolon = true +cpp_space_after_semicolon = true +cpp_space_remove_around_unary_operator = true +cpp_space_around_binary_operator = insert +cpp_space_around_assignment_operator = insert +cpp_space_pointer_reference_alignment = left +cpp_space_around_ternary_operator = insert +cpp_wrap_preserve_blocks = one_liners diff --git a/public/ATI_Compress.h b/public/ATI_Compress.h new file mode 100644 index 0000000..4cc1f14 --- /dev/null +++ b/public/ATI_Compress.h @@ -0,0 +1,144 @@ +////////////////////////////////////////////////////////////////////////////// +// +// ATI Technologies Inc. +// 1 Commerce Valley Drive East +// Markham, Ontario +// CANADA L3T 7X6 +// +// File Name: ATI_Compress.h +// Description: A library to compress/decompress textures +// +// Copyright (c) 2004-2006 ATI Technologies Inc. +// +// Version: 1.4 +// +// Developer: Seth Sowerby +// Email: gputools.support@amd.com +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef ATI_COMPRESS +#define ATI_COMPRESS + +#define ATI_COMPRESS_VERSION_MAJOR 1 +#define ATI_COMPRESS_VERSION_MINOR 4 + +typedef unsigned long ATI_TC_DWORD; +typedef unsigned short ATI_TC_WORD; +typedef unsigned char ATI_TC_BYTE; + +#if defined(WIN32) || defined(_WIN64) +# define ATI_TC_API __cdecl +#else +# define ATI_TC_API +#endif + +#ifdef ATI_COMPRESS_INTERNAL_BUILD +#include "ATI_Compress_Internal.h" +#else // ATI_COMPRESS_INTERNAL_BUILD + +typedef enum +{ + ATI_TC_FORMAT_ARGB_8888, + ATI_TC_FORMAT_ARGB_2101010, + ATI_TC_FORMAT_ARGB_16, + ATI_TC_FORMAT_ARGB_16F, + ATI_TC_FORMAT_ARGB_32F, + ATI_TC_FORMAT_DXT1, + ATI_TC_FORMAT_DXT3, + ATI_TC_FORMAT_DXT5, + ATI_TC_FORMAT_DXT5_xGBR, + ATI_TC_FORMAT_DXT5_RxBG, + ATI_TC_FORMAT_DXT5_RBxG, + ATI_TC_FORMAT_DXT5_xRBG, + ATI_TC_FORMAT_DXT5_RGxB, + ATI_TC_FORMAT_DXT5_xGxR, + ATI_TC_FORMAT_ATI1N, + ATI_TC_FORMAT_ATI2N, + ATI_TC_FORMAT_ATI2N_XY, + ATI_TC_FORMAT_ATI2N_DXT5, + ATI_TC_FORMAT_MAX = ATI_TC_FORMAT_ATI2N_DXT5 +} ATI_TC_FORMAT; + +typedef struct _ATI_TC_CompressOptions +{ + ATI_TC_DWORD dwSize; /* Size of this structure */ + + /* Channel Weightings */ + /* With swizzled formats the weighting applies to the data within the specified channel */ + /* not the channel itself. */ + BOOL bUseChannelWeighting; + double fWeightingRed; /* Weighting of the Red or X Channel */ + double fWeightingGreen; /* Weighting of the Green or Y Channel */ + double fWeightingBlue; /* Weighting of the Blue or Z Channel */ + BOOL bUseAdaptiveWeighting; /* Adapt weighting on a per-block basis */ + BOOL bDXT1UseAlpha; + ATI_TC_BYTE nAlphaThreshold; +} ATI_TC_CompressOptions; +#endif // !ATI_COMPRESS_INTERNAL_BUILD + +typedef struct _ATI_TC_Texture +{ + ATI_TC_DWORD dwSize; /* Size of this structure */ + ATI_TC_DWORD dwWidth; /* Width of the texture */ + ATI_TC_DWORD dwHeight; /* Height of the texture */ + ATI_TC_DWORD dwPitch; /* Distance to start of next line - necessary only for uncompressed textures */ + ATI_TC_FORMAT format; /* Format of the texture */ + ATI_TC_DWORD dwDataSize; /* Size of the allocated texture data */ + ATI_TC_BYTE* pData; /* Pointer to the texture data */ +} ATI_TC_Texture; + +typedef enum +{ + ATI_TC_OK = 0, + ATI_TC_ABORTED, + ATI_TC_ERR_INVALID_SOURCE_TEXTURE, + ATI_TC_ERR_INVALID_DEST_TEXTURE, + ATI_TC_ERR_UNSUPPORTED_SOURCE_FORMAT, + ATI_TC_ERR_UNSUPPORTED_DEST_FORMAT, + ATI_TC_ERR_SIZE_MISMATCH, + ATI_TC_ERR_UNABLE_TO_INIT_CODEC, + ATI_TC_ERR_GENERIC +} ATI_TC_ERROR; + +#define MINIMUM_WEIGHT_VALUE 0.01f + + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** ATI_TC_Feedback_Proc +** Feedback proc for conversion +** Return non-NULL(true) value to abort conversion +*/ + +typedef bool (ATI_TC_API * ATI_TC_Feedback_Proc)(float fProgress, uint32* pUser1, uint32* pUser2); + +/* +** ATI_TC_CalculateBufferSize +** Calculates the required buffer size for the specified texture +*/ + +ATI_TC_DWORD ATI_TC_API ATI_TC_CalculateBufferSize(const ATI_TC_Texture* pTexture); + + +/* +** ATI_TC_ConvertTexture +** Converts the source texture to the destination texture +*/ + +ATI_TC_ERROR ATI_TC_API ATI_TC_ConvertTexture(const ATI_TC_Texture* pSourceTexture, /* [in] - Pointer to the source texture */ + ATI_TC_Texture* pDestTexture, /* [out] - Pointer to the destination texture */ + const ATI_TC_CompressOptions* pOptions, /* [in] - Pointer to the compression options - can be NULL */ + ATI_TC_Feedback_Proc pFeedbackProc, /* [in] - Pointer to the feedback proc - can be NULL */ + uint32* pUser1, /* [in] - User data to pass to the feedback proc */ + uint32* pUser2); /* [in] - User data to pass to the feedback proc */ + + +#ifdef __cplusplus +}; +#endif + +#endif // !ATI_COMPRESS diff --git a/public/BitmapFontFile.h b/public/BitmapFontFile.h new file mode 100644 index 0000000..3efae76 --- /dev/null +++ b/public/BitmapFontFile.h @@ -0,0 +1,54 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ====== +// +// Baked Bitmap fonts +// +//=========================================================================== + +#ifndef _BITMAPFONTFILE_H_ +#define _BITMAPFONTFILE_H_ + +#include "datamap.h" + +#define BITMAPFONT_ID (('T'<<24)|('N'<<16)|('F'<<8)|('V')) +#define BITMAPFONT_VERSION 3 + +// style flags +#define BF_BOLD 0x0001 +#define BF_ITALIC 0x0002 +#define BF_OUTLINED 0x0004 +#define BF_DROPSHADOW 0x0008 +#define BF_BLURRED 0x0010 +#define BF_SCANLINES 0x0020 +#define BF_ANTIALIASED 0x0040 +#define BF_CUSTOM 0x0080 + +#pragma pack(1) //X360TBD +typedef struct BitmapGlyph_s +{ + DECLARE_BYTESWAP_DATADESC(); + short x; + short y; + short w; + short h; + short a; + short b; + short c; +} BitmapGlyph_t; + +typedef struct BitmapFont_s +{ + DECLARE_BYTESWAP_DATADESC(); + int m_id; + int m_Version; + short m_PageWidth; + short m_PageHeight; + short m_MaxCharWidth; + short m_MaxCharHeight; + short m_Flags; + short m_Ascent; + short m_NumGlyphs; + unsigned char m_TranslateTable[256]; +} BitmapFont_t; +#pragma pack() + +#endif \ No newline at end of file diff --git a/public/Color.h b/public/Color.h new file mode 100644 index 0000000..d84cc11 --- /dev/null +++ b/public/Color.h @@ -0,0 +1,124 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef COLOR_H +#define COLOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/basetypes.h" + +//----------------------------------------------------------------------------- +// Purpose: Basic handler for an rgb set of colors +// This class is fully inline +//----------------------------------------------------------------------------- +class Color +{ +public: + // constructors + Color() + { + *((int *)this) = 0; + } + Color(int _r,int _g,int _b) + { + SetColor(_r, _g, _b, 0); + } + Color(int _r,int _g,int _b,int _a) + { + SetColor(_r, _g, _b, _a); + } + + // set the color + // r - red component (0-255) + // g - green component (0-255) + // b - blue component (0-255) + // a - alpha component, controls transparency (0 - transparent, 255 - opaque); + void SetColor(int _r, int _g, int _b, int _a = 0) + { + _color[0] = (unsigned char)_r; + _color[1] = (unsigned char)_g; + _color[2] = (unsigned char)_b; + _color[3] = (unsigned char)_a; + } + + void GetColor(int &_r, int &_g, int &_b, int &_a) const + { + _r = _color[0]; + _g = _color[1]; + _b = _color[2]; + _a = _color[3]; + } + + void SetRawColor( int color32 ) + { + *((int *)this) = color32; + } + + int GetRawColor() const + { + return *((int *)this); + } + + inline int r() const { return _color[0]; } + inline int g() const { return _color[1]; } + inline int b() const { return _color[2]; } + inline int a() const { return _color[3]; } + + unsigned char &operator[](int index) + { + return _color[index]; + } + + const unsigned char &operator[](int index) const + { + return _color[index]; + } + + bool operator == (const Color &rhs) const + { + return ( *((int *)this) == *((int *)&rhs) ); + } + + bool operator != (const Color &rhs) const + { + return !(operator==(rhs)); + } + + Color &operator=( const Color &rhs ) + { + SetRawColor( rhs.GetRawColor() ); + return *this; + } + + Color &operator=( const color32 &rhs ) + { + _color[0] = rhs.r; + _color[1] = rhs.g; + _color[2] = rhs.b; + _color[3] = rhs.a; + return *this; + } + + color32 ToColor32() const + { + color32 newColor; + newColor.r = _color[0]; + newColor.g = _color[1]; + newColor.b = _color[2]; + newColor.a = _color[3]; + return newColor; + } + +private: + unsigned char _color[4]; +}; + + +#endif // COLOR_H diff --git a/public/Friends/AddOns/AddOnMessages.h b/public/Friends/AddOns/AddOnMessages.h new file mode 100644 index 0000000..d1eff40 --- /dev/null +++ b/public/Friends/AddOns/AddOnMessages.h @@ -0,0 +1,56 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ADDONMESSAGES_H +#define ADDONMESSAGES_H +#pragma once + +enum GameMessageIDs +{ + GAME_MSG_TEXT = 1, // text chat message + GAME_MSG_DATA, // binary game data + + GAME_MSG_INVITE_REQUEST, + GAME_MSG_INVITE_RESPONSE, + GAME_MSG_REJOIN_REQUEST, + GAME_MSG_REJOIN_RESPONSE, + + GAME_MSG_INVITE_PERMISSION, // ask permission to invite someone + GAME_MSG_INVITE_NOTIFY, // tell everybody that we're inviting + GAME_MSG_INVITE_DENIED, + + GAME_MSG_PLAYER_STATUS_UPDATE, + GAME_MSG_SETUP_INFO, // when user joins a game, host send the setup information of who's in the game + GAME_MSG_INVITE_CANCEL, // host has cancelled an invite + GAME_MSG_GAME_START, // if a game has a setup phase, this tells everybody the game has started + + GAME_MSG_PLAYER_KICK, // player kicked from game + GAME_MSG_UPDATING, + GAME_MSG_UP_TO_DATE, // player is up to date and ready to get data + + GAME_MSG_STARTING_CARD_HAND = 300, + GAME_MSG_STARTING_PLAYER, + GAME_MSG_CARD_PLAY, + + GAME_MSG_CHEAT_POSSIBLE = 400, // when host detects a possible cheat + + GAME_MSG_MOVE = 500, + GAME_MSG_COLOR_CHOICE, + GAME_MSG_RECONNECT_DATA, + GAME_MSG_QUIT, + GAME_MSG_PASS, + + GAME_MSG_ABORT, // phase these out + GAME_MSG_WAITING_ABORT, + +// GAME_MSG_CLOSE_WINDOW, + + // special individual game messages should take IDs 1000 and over +}; + + +#endif // ADDONMESSAGES_H + diff --git a/public/Friends/AddOns/AddOnTypes.h b/public/Friends/AddOns/AddOnTypes.h new file mode 100644 index 0000000..7295044 --- /dev/null +++ b/public/Friends/AddOns/AddOnTypes.h @@ -0,0 +1,18 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ADDONTYPES_H +#define ADDONTYPES_H +#pragma once + +#ifndef WIN32 + typedef unsigned long long SessionInt64; +#else + typedef unsigned __int64 SessionInt64; +#endif + +#endif // ADDONTYPES_H + diff --git a/public/Friends/AddOns/ISteamAddOn.h b/public/Friends/AddOns/ISteamAddOn.h new file mode 100644 index 0000000..5af4114 --- /dev/null +++ b/public/Friends/AddOns/ISteamAddOn.h @@ -0,0 +1,69 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Interface to a Steam Add On +// +//=============================================================================// + +#ifndef ISTEAMADDON_H +#define ISTEAMADDON_H +#pragma once + +#include "interface.h" +#include "AddOnTypes.h" +#include + +class CUtlMsgBuffer; + +class ISteamAddOn : public IBaseInterface +{ +public: + // allows SteamAddOn to link to the core vgui factories + virtual bool Initialize(CreateInterfaceFn *vguiFactories, int factoryCount) = 0; + + // allows SteamAddOns to link to all other modules + virtual bool PostInitialize(CreateInterfaceFn *modules, int factoryCount) = 0; + + // when Friends closes down - all SteamAddOns are notified + virtual void Deactivate() = 0; + + // notifies the addon who its VGUI parent panel is + virtual void SetParent( vgui::Panel *parent ) = 0; + + // notifies the SteamAddOn of the user's ID and username. + // Note: username can be set mulitple times due to changing of name + virtual void SetUserID(unsigned int userID) = 0; + virtual void SetUserName(const char *userName) = 0; + + // Query if there are any 'open' sessions - open meaning allowing new users to join the sessions + virtual int QueryOpenSessionCount() = 0; + + // will be valid right after a call to QueryOpenInviteCount will set the addOnSessionID and hostname for + // any open sessions for this addOn. Return true if it's a valid index + virtual bool QueryOpenSessionInfo(int nOpenGameIndex, SessionInt64 &addOnSessionID, char *pszHostName) = 0; + + // returns true if this userID is involved in an addOnSession with this ID + virtual bool QueryUserInvolved(SessionInt64 addOnSessionID, unsigned int userID) = 0; + + // if session doesn't exist, then the SteamAddOn body should deal with it + virtual bool OnReceiveMsg(SessionInt64 addOnSessionID, CUtlMsgBuffer *msgBuffer) = 0; + + // Let's the SteamAddOn know when when any friend's status has changed + virtual void OnFriendStatusChanged() = 0; + + // A request to start/join this AddOn with this user ID/name. addOnSessionID will be zero if it's a new session request + virtual void OnInviteUser(unsigned int targetUserID, const char *username, SessionInt64 addOnSessionID) = 0; + + // user accepted this host's invite request + virtual void OnAcceptInviteRequest(unsigned int hostID, const char *hostUserName, SessionInt64 addOnSessionID, const char *pAppData, int dataLen) = 0; + + // user accepted this host's rejoin request + virtual void OnAcceptRejoinRequest(unsigned int hostID, const char *hostUserName, SessionInt64 addOnSessionID, const char *pAppData, int dataLen) = 0; + + // user starts this addOn from a menu + virtual void StartAddOn() = 0; +}; + +#define STEAMADDON_INTERFACE_VERSION "SteamAddOn007" + +#endif // ISTEAMADDON_H + diff --git a/public/Friends/IFriendsNET.h b/public/Friends/IFriendsNET.h new file mode 100644 index 0000000..41593ba --- /dev/null +++ b/public/Friends/IFriendsNET.h @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FRIENDSNET_INTERFACE_H +#define FRIENDSNET_INTERFACE_H +#pragma once + +class CUtlMsgBuffer; +class CServerSession; + +#include "interface.h" +#include "Friends/AddOns/AddOnTypes.h" + +class IFriendsNET : public IBaseInterface +{ +public: + // check if we have network information for this user + virtual bool CheckUserRegistered(unsigned int userID) = 0; + + // update a user's network information + virtual void UpdateUserNetInfo(unsigned int userID, unsigned int netSessionID, int serverID, int IP, int port) = 0; + + // set whether or not we send directly to user or through the server + virtual void SetUserSendViaServer(unsigned int userID, bool bSendViaServer) = 0; + + // Gets a blob of data that represents this user's information + virtual bool GetUserNetInfoBlob(unsigned int userID, unsigned int dataBlob[8]) = 0; + // Sets a user's information using the same blob of data type + virtual bool SetUserNetInfoBlob(unsigned int userID, const unsigned int dataBlob[8]) = 0; + + // send binary data to user, marked with game/sessionID + virtual void SendAddOnPacket(const char *pszGameID, SessionInt64 addOnSessionID, unsigned int userID, const CUtlMsgBuffer& buffer) = 0; +}; + +#define FRIENDSNET_INTERFACE_VERSION "FriendsNET003" + +#endif // FRIENDSNET_INTERFACE_H + diff --git a/public/Friends/IFriendsUser.h b/public/Friends/IFriendsUser.h new file mode 100644 index 0000000..2c906a9 --- /dev/null +++ b/public/Friends/IFriendsUser.h @@ -0,0 +1,62 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IFRIENDSUSER_H +#define IFRIENDSUSER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +//----------------------------------------------------------------------------- +// Purpose: Interface to accessing information about Friends Users +//----------------------------------------------------------------------------- +class IFriendsUser : public IBaseInterface +{ +public: + // returns true if the interface is ready for use + virtual bool IsValid() = 0; + + // returns the Friends ID of the current user + virtual unsigned int GetFriendsID() = 0; + + // returns information about a user + // information may not be known about some users, "" will be returned + virtual const char *GetUserName(unsigned int friendsID) = 0; + virtual const char *GetFirstName(unsigned int friendsID) = 0; + virtual const char *GetLastName(unsigned int friendsID) = 0; + virtual const char *GetEmail(unsigned int friendsID) = 0; + + // returns true if buddyID is a buddy of the current user + // ie. the current is authorized to see when the buddy is online + virtual bool IsBuddy(unsigned int buddyID) = 0; + + // requests authorization from a user + virtual void RequestAuthorizationFromUser(unsigned int potentialBuddyID) = 0; + + // returns the status of the buddy, > 0 is online, 4 is ingame + virtual int GetBuddyStatus(unsigned int friendsID) = 0; + + // gets the IP address of the server the buddy is on, returns false if couldn't get + virtual bool GetBuddyGameAddress(unsigned int friendsID, int *ip, int *port) = 0; + + // returns the number of buddies + virtual int GetNumberOfBuddies() = 0; + + // returns the FriendsID of a buddy - buddyIndex is valid in the range [0, GetNumberOfBuddies) + virtual unsigned int GetBuddyFriendsID(int buddyIndex) = 0; + + // sets whether or not the user can receive messages at this time + // messages will be queued until this is set to true + virtual void SetCanReceiveMessages(bool state) = 0; +}; + +#define FRIENDSUSER_INTERFACE_VERSION "FriendsUser001" + + +#endif // IFRIENDSUSER_H diff --git a/public/IGameUIFuncs.h b/public/IGameUIFuncs.h new file mode 100644 index 0000000..e657c2a --- /dev/null +++ b/public/IGameUIFuncs.h @@ -0,0 +1,30 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IGAMEUIFUNCS_H +#define IGAMEUIFUNCS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/KeyCode.h" + +abstract_class IGameUIFuncs +{ +public: + virtual bool IsKeyDown( const char *keyname, bool& isdown ) = 0; + virtual const char *GetBindingForButtonCode( ButtonCode_t code ) = 0; + virtual ButtonCode_t GetButtonCodeForBind( const char *pBind, int userId = -1 ) = 0; + virtual void GetVideoModes( struct vmode_s **liststart, int *count ) = 0; + virtual void SetFriendsID( uint friendsID, const char *friendsName ) = 0; + virtual void GetDesktopResolution( int &width, int &height ) = 0; + virtual bool IsConnectedToVACSecureServer() = 0; +}; + +#define VENGINE_GAMEUIFUNCS_VERSION "VENGINE_GAMEUIFUNCS_VERSION005" + +#endif // IGAMEUIFUNCS_H diff --git a/public/IHammer.h b/public/IHammer.h new file mode 100644 index 0000000..f49f5a7 --- /dev/null +++ b/public/IHammer.h @@ -0,0 +1,69 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: The application object. +// +//=============================================================================// + +#ifndef IHAMMER_H +#define IHAMMER_H + +#include "appframework/IAppSystem.h" + +typedef struct tagMSG MSG; + + +class IStudioDataCache; + + +//----------------------------------------------------------------------------- +// Return values for RequestNewConfig +//----------------------------------------------------------------------------- +enum RequestRetval_t +{ + REQUEST_OK = 0, + REQUEST_QUIT +}; + + +//----------------------------------------------------------------------------- +// Interface used to drive hammer +//----------------------------------------------------------------------------- +#define INTERFACEVERSION_HAMMER "Hammer001" +class IHammer : public IAppSystem +{ +public: + virtual bool HammerPreTranslateMessage( MSG * pMsg ) = 0; + virtual bool HammerIsIdleMessage( MSG * pMsg ) = 0; + virtual bool HammerOnIdle( long count ) = 0; + + virtual void RunFrame() = 0; + + // Returns the mod and the game to initially start up + virtual const char *GetDefaultMod() = 0; + virtual const char *GetDefaultGame() = 0; + + virtual bool InitSessionGameConfig( const char *szGameDir ) = 0; + + // Request a new config from hammer's config system + virtual RequestRetval_t RequestNewConfig() = 0; + + // Returns the full path to the mod and the game to initially start up + virtual const char *GetDefaultModFullPath() = 0; + + virtual int MainLoop() = 0; + + // When Foundry is using Hammer: + // - Hammer doesn't create its main window. + // - Select windows (like the object properties dialog) are parented to the game's window and share its message pump + // - A CMapView3D is created and renders only entities on top of the game's 3D window + virtual void InitFoundryMode( CreateInterfaceFn factory, void *hGameWnd, const char *szGameDir ) = 0; + + // Called when the engine's window took the focus. + virtual void NoteEngineGotFocus() = 0; + + // Used in Foundry to show and hide the full Hammer UI. + virtual bool IsHammerVisible() = 0; + virtual void ToggleHammerVisible() = 0; +}; + +#endif // IHAMMER_H diff --git a/public/OfflineMode.h b/public/OfflineMode.h new file mode 100644 index 0000000..0a0455c --- /dev/null +++ b/public/OfflineMode.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include + +#define STEAM_OFFLINE_MODE "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Offline" +#define STEAM_AFS_MODE "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\OfflineAFS" +#define OFFLINE_FILE "Steam\\cached\\offline_" // first part of filename + +inline bool IsSteamInOfflineMode() +{ + int offline = 0; + vgui::system()->GetRegistryInteger( STEAM_OFFLINE_MODE, offline ); + return ( offline == 1 ); +}inline bool IsSteamInAuthenticationFailSafeMode() +{ + int offline = 0; + vgui::system()->GetRegistryInteger( STEAM_AFS_MODE, offline ); + return ( offline == 1 ); +} + +inline bool IsSteamGameServerBrowsingEnabled() +{ + return (IsSteamInAuthenticationFailSafeMode() || !IsSteamInOfflineMode()); +} diff --git a/public/PlayerState.h b/public/PlayerState.h new file mode 100644 index 0000000..27dea81 --- /dev/null +++ b/public/PlayerState.h @@ -0,0 +1,63 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PLAYERSTATE_H +#define PLAYERSTATE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "edict.h" +#include "networkvar.h" +// Only care about this stuff in game/client .dlls +#if defined( CLIENT_DLL ) +#include "predictable_entity.h" +#endif + +class CPlayerState +{ +public: + DECLARE_CLASS_NOBASE( CPlayerState ); + DECLARE_EMBEDDED_NETWORKVAR(); + + // This virtual method is necessary to generate a vtable in all cases + // (DECLARE_PREDICTABLE will generate a vtable also)! + virtual ~CPlayerState() {} + + // true if the player is dead + CNetworkVar( bool, deadflag ); + // Viewing angle (player only) + QAngle v_angle; + +// The client .dll only cares about deadflag +// the game and engine .dlls need to worry about the rest of this data +#if !defined( CLIENT_DLL ) + // Player's network name + string_t netname; + // 0:nothing, 1:force view angles, 2:add avelocity + int fixangle; + // delta angle for fixangle == FIXANGLE_RELATIVE + QAngle anglechange; + // flag to single the HLTV/Replay fake client, not transmitted + bool hltv; + bool replay; + int frags; + int deaths; +#endif + +// NOTE: Only care about this stuff in game/client dlls +// Put at end in case it has any effect on size of structure +#if defined( GAME_DLL ) + DECLARE_SIMPLE_DATADESC(); +#endif + +#if defined( CLIENT_DLL ) + DECLARE_PREDICTABLE(); +#endif +}; + +#endif // PLAYERSTATE_H diff --git a/public/ScratchPadUtils.cpp b/public/ScratchPadUtils.cpp new file mode 100644 index 0000000..b8b4d70 --- /dev/null +++ b/public/ScratchPadUtils.cpp @@ -0,0 +1,467 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include "iscratchpad3d.h" +#include "mathlib/mathlib.h" +#include "ScratchPadUtils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +// --------------------------------------------------------------------------------------------------------------------- // +// CScratchPadGraph implementation. +// --------------------------------------------------------------------------------------------------------------------- // + +CScratchPadGraph::CScratchPadGraph() +{ + m_pPad = NULL; +} + + +void CScratchPadGraph::Init( + IScratchPad3D *pPad, + + Vector vTimeAxis, + float flInchesPerSecond, + Vector vTimeLineColor, + float flTimeOrigin, + + float flTimeLabelEveryNSeconds, + + Vector vValueAxis, + float flInchesPerValue, + Vector vValueLineColor, + float flValueOrigin + + ) +{ + m_pPad = pPad; + m_vTimeAxis = vTimeAxis; + m_flInchesPerSecond = flInchesPerSecond; + m_vValueAxis = vValueAxis; + m_flInchesPerValue = flInchesPerValue; + m_flTimeLabelEveryNSeconds = flTimeLabelEveryNSeconds; + + m_vTimeLineColor = vTimeLineColor; + m_vValueLineColor = vValueLineColor; + + m_flTimeOrigin = flTimeOrigin; + m_flValueOrigin = flValueOrigin; + + m_nTimeLabelsDrawn = 0; + m_flHighestTime = flTimeOrigin; + m_flHighestValue = flValueOrigin; +} + + +bool CScratchPadGraph::IsInitted() const +{ + return m_pPad != NULL; +} + + +CScratchPadGraph::LineID CScratchPadGraph::AddLine( Vector vColor ) +{ + CScratchPadGraph::CLineInfo info; + info.m_bFirst = true; + info.m_vColor = vColor; + return m_LineInfos.AddToTail( info ); +} + + +void CScratchPadGraph::AddSample( LineID iLine, float flTime, float flValue ) +{ + CScratchPadGraph::CLineInfo *pInfo = &m_LineInfos[iLine]; + + UpdateTicksAndStuff( flTime, flValue ); + + if ( !pInfo->m_bFirst ) + { + // Draw a line from the last value to the current one. + Vector vStart = GetSamplePosition( pInfo->m_flLastTime, pInfo->m_flLastValue ); + Vector vEnd = GetSamplePosition( flTime, flValue ); + + m_pPad->DrawLine( + CSPVert( vStart, pInfo->m_vColor ), + CSPVert( vEnd, pInfo->m_vColor ) + ); + } + + pInfo->m_flLastTime = flTime; + pInfo->m_flLastValue = flValue; + pInfo->m_bFirst = false; +} + + +void CScratchPadGraph::AddVerticalLine( float flTime, float flMinValue, float flMaxValue, const CSPColor &vColor ) +{ + Vector v1 = GetSamplePosition( flTime, flMinValue ); + Vector v2 = GetSamplePosition( flTime, flMaxValue ); + m_pPad->DrawLine( + CSPVert( v1, vColor ), + CSPVert( v2, vColor ) ); +} + + +void CScratchPadGraph::UpdateTicksAndStuff( float flTime, float flValue ) +{ + if ( flTime > m_flHighestTime ) + { + // Update the left part of the time axis. + Vector vStart = GetSamplePosition( m_flHighestTime, m_flValueOrigin ); + Vector vEnd = GetSamplePosition( flTime, m_flValueOrigin ); + + m_pPad->DrawLine( + CSPVert( vStart, m_vTimeLineColor ), + CSPVert( vEnd, m_vTimeLineColor ) + ); + + m_flHighestTime = flTime; + } + + if ( flValue > m_flHighestValue ) + { + // Update the left part of the time axis. + Vector vStart = GetSamplePosition( m_flTimeOrigin, m_flHighestValue ); + Vector vEnd = GetSamplePosition( m_flTimeOrigin, flValue ); + + m_pPad->DrawLine( + CSPVert( vStart, m_vValueLineColor ), + CSPVert( vEnd, m_vValueLineColor ) + ); + + // Extend the lines attached to the time labels. + for ( int i=0; i < m_nTimeLabelsDrawn; i++ ) + { + float flTime = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; + + m_pPad->DrawLine( + CSPVert((const Vector&) GetSamplePosition( flTime, m_flHighestValue )), + CSPVert((const Vector&) GetSamplePosition( flTime, flValue ) ) + ); + } + + m_flHighestValue = flValue; + } + + // More text labels? + int iHighestTextLabel = (int)ceil( (flTime - m_flTimeOrigin) / m_flTimeLabelEveryNSeconds + 0.5f ); + while ( m_nTimeLabelsDrawn < iHighestTextLabel ) + { + CTextParams params; + + float flTime = m_flTimeOrigin + m_nTimeLabelsDrawn * m_flTimeLabelEveryNSeconds; + + params.m_bSolidBackground = true; + params.m_vPos = GetSamplePosition( flTime, m_flValueOrigin-5 ); + params.m_bTwoSided = true; + + char str[512]; + Q_snprintf( str, sizeof( str ), "time: %.2f", flTime ); + m_pPad->DrawText( str, params ); + + + // Now draw the vertical line for the value.. + m_pPad->DrawLine( + CSPVert( (const Vector&)GetSamplePosition( flTime, m_flValueOrigin ) ), + CSPVert( (const Vector&)GetSamplePosition( flTime, m_flHighestValue ) ) + ); + + + m_nTimeLabelsDrawn++; + } +} + + +Vector CScratchPadGraph::GetSamplePosition( float flTime, float flValue ) +{ + Vector vRet = + m_vTimeAxis * ((flTime - m_flTimeOrigin) * m_flInchesPerSecond) + + m_vValueAxis * ((flValue - m_flValueOrigin) * m_flInchesPerValue); + + return vRet; +} + + + +// --------------------------------------------------------------------------------------------------------------------- // +// Global functions. +// --------------------------------------------------------------------------------------------------------------------- // + +void ScratchPad_DrawLitCone( + IScratchPad3D *pPad, + const Vector &vBaseCenter, + const Vector &vTip, + const Vector &vBrightColor, + const Vector &vDarkColor, + const Vector &vLightDir, + float baseWidth, + int nSegments ) +{ + // Make orthogonal vectors. + Vector vDir = vTip - vBaseCenter; + VectorNormalize( vDir ); + + Vector vRight, vUp; + VectorVectors( vDir, vRight, vUp ); + vRight *= baseWidth; + vUp *= baseWidth; + + // Setup the top and bottom caps. + CSPVertList bottomCap, tri; + bottomCap.m_Verts.SetSize( nSegments ); + tri.m_Verts.SetSize( 3 ); + + float flDot = -vLightDir.Dot( vDir ); + Vector topColor, bottomColor; + VectorLerp( vDarkColor, vBrightColor, RemapVal( -flDot, -1, 1, 0, 1 ), bottomColor ); + + + // Draw each quad. + Vector vPrevBottom = vBaseCenter + vRight; + + for ( int i=0; i < nSegments; i++ ) + { + float flAngle = (float)(i+1) * M_PI * 2.0 / nSegments; + Vector vOffset = vRight * cos( flAngle ) + vUp * sin( flAngle ); + Vector vCurBottom = vBaseCenter + vOffset; + + const Vector &v1 = vTip; + const Vector &v2 = vPrevBottom; + const Vector &v3 = vCurBottom; + Vector vFaceNormal = (v2 - v1).Cross( v3 - v1 ); + VectorNormalize( vFaceNormal ); + + // Now light it. + flDot = -vLightDir.Dot( vFaceNormal ); + Vector vColor; + VectorLerp( vDarkColor, vBrightColor, RemapVal( flDot, -1, 1, 0, 1 ), vColor ); + + // Draw the quad. + tri.m_Verts[0] = CSPVert( v1, vColor ); + tri.m_Verts[1] = CSPVert( v2, vColor ); + tri.m_Verts[2] = CSPVert( v3, vColor ); + pPad->DrawPolygon( tri ); + + bottomCap.m_Verts[i] = CSPVert( vCurBottom, bottomColor ); + } + + pPad->DrawPolygon( bottomCap ); +} + + +void ScratchPad_DrawLitCylinder( + IScratchPad3D *pPad, + const Vector &v1, + const Vector &v2, + const Vector &vBrightColor, + const Vector &vDarkColor, + const Vector &vLightDir, + float width, + int nSegments ) +{ + // Make orthogonal vectors. + Vector vDir = v2 - v1; + VectorNormalize( vDir ); + + Vector vRight, vUp; + VectorVectors( vDir, vRight, vUp ); + vRight *= width; + vUp *= width; + + // Setup the top and bottom caps. + CSPVertList topCap, bottomCap, quad; + + topCap.m_Verts.SetSize( nSegments ); + bottomCap.m_Verts.SetSize( nSegments ); + quad.m_Verts.SetSize( 4 ); + + float flDot = -vLightDir.Dot( vDir ); + Vector topColor, bottomColor; + + VectorLerp( vDarkColor, vBrightColor, RemapVal( flDot, -1, 1, 0, 1 ), topColor ); + VectorLerp( vDarkColor, vBrightColor, RemapVal( -flDot, -1, 1, 0, 1 ), bottomColor ); + + + // Draw each quad. + Vector vPrevTop = v1 + vRight; + Vector vPrevBottom = v2 + vRight; + + for ( int i=0; i < nSegments; i++ ) + { + float flAngle = (float)(i+1) * M_PI * 2.0 / nSegments; + Vector vOffset = vRight * cos( flAngle ) + vUp * sin( flAngle ); + Vector vCurTop = v1 + vOffset; + Vector vCurBottom = v2 + vOffset; + + // Now light it. + VectorNormalize( vOffset ); + flDot = -vLightDir.Dot( vOffset ); + Vector vColor; + VectorLerp( vDarkColor, vBrightColor, RemapVal( flDot, -1, 1, 0, 1 ), vColor ); + + // Draw the quad. + quad.m_Verts[0] = CSPVert( vPrevTop, vColor ); + quad.m_Verts[1] = CSPVert( vPrevBottom, vColor ); + quad.m_Verts[2] = CSPVert( vCurBottom, vColor ); + quad.m_Verts[3] = CSPVert( vCurTop, vColor ); + pPad->DrawPolygon( quad ); + + topCap.m_Verts[i] = CSPVert( vCurTop, topColor ); + bottomCap.m_Verts[i] = CSPVert( vCurBottom, bottomColor ); + } + + pPad->DrawPolygon( topCap ); + pPad->DrawPolygon( bottomCap ); +} + + +void ScratchPad_DrawArrow( + IScratchPad3D *pPad, + const Vector &vPos, + const Vector &vDirection, + const Vector &vColor, + float flLength, + float flLineWidth, + float flHeadWidth, + int nCylinderSegments, + int nHeadSegments, + float flArrowHeadPercentage + ) +{ + Vector vNormDir = vDirection; + VectorNormalize( vNormDir ); + + Vector vConeBase = vPos + vNormDir * (flLength * ( 1 - flArrowHeadPercentage ) ); + Vector vConeEnd = vPos + vNormDir * flLength; + + Vector vLightDir( -1, -1, -1 ); + VectorNormalize( vLightDir ); // could precalculate this + + pPad->SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Solid ); + pPad->SetRenderState( IScratchPad3D::RS_ZRead, true ); + + ScratchPad_DrawLitCylinder( pPad, vPos, vConeBase, vColor, vColor*0.25f, vLightDir, flLineWidth, nCylinderSegments ); + ScratchPad_DrawLitCone( pPad, vConeBase, vConeEnd, vColor, vColor*0.25f, vLightDir, flHeadWidth, nHeadSegments ); +} + + +void ScratchPad_DrawArrowSimple( + IScratchPad3D *pPad, + const Vector &vPos, + const Vector &vDirection, + const Vector &vColor, + float flLength ) +{ + ScratchPad_DrawArrow( + pPad, + vPos, + vDirection, + vColor, + flLength, + flLength * 1.0/15, + flLength * 3.0/15, + 4, + 4 ); +} + + +void ScratchPad_DrawSphere( + IScratchPad3D *pPad, + const Vector &vCenter, + float flRadius, + const Vector &vColor, + int nSubDivs ) +{ + CUtlVector prevPoints; + prevPoints.SetSize( nSubDivs ); + + // For each vertical slice.. (the top and bottom ones are just a single point). + for ( int iSlice=0; iSlice < nSubDivs; iSlice++ ) + { + float flHalfSliceAngle = M_PI * (float)iSlice / (nSubDivs - 1); + + if ( iSlice == 0 ) + { + prevPoints[0] = vCenter + Vector( 0, 0, flRadius ); + for ( int z=1; z < prevPoints.Count(); z++ ) + prevPoints[z] = prevPoints[0]; + } + else + { + for ( int iSubPt=0; iSubPt < nSubDivs; iSubPt++ ) + { + float flHalfAngle = M_PI * (float)iSubPt / (nSubDivs - 1); + float flAngle = flHalfAngle * 2; + + Vector pt; + if ( iSlice == (nSubDivs - 1) ) + { + pt = vCenter - Vector( 0, 0, flRadius ); + } + else + { + pt.x = cos( flAngle ) * sin( flHalfSliceAngle ); + pt.y = sin( flAngle ) * sin( flHalfSliceAngle ); + pt.z = cos( flHalfSliceAngle ); + + pt *= flRadius; + pt += vCenter; + } + + pPad->DrawLine( CSPVert( pt, vColor ), CSPVert( prevPoints[iSubPt], vColor ) ); + prevPoints[iSubPt] = pt; + } + + if ( iSlice != (nSubDivs - 1) ) + { + for ( int i=0; i < nSubDivs; i++ ) + pPad->DrawLine( CSPVert( prevPoints[i], vColor ), CSPVert( prevPoints[(i+1)%nSubDivs], vColor ) ); + } + } + } +} + + +void ScratchPad_DrawAABB( + IScratchPad3D *pPad, + const Vector &vMins, + const Vector &vMaxs, + const Vector &vColor ) +{ + int vertOrder[4][2] = {{0,0},{1,0},{1,1},{0,1}}; + const Vector *vecs[2] = {&vMins, &vMaxs}; + + Vector vTop, vBottom, vPrevTop, vPrevBottom; + vTop.z = vPrevTop.z = vMaxs.z; + vBottom.z = vPrevBottom.z = vMins.z; + + vPrevTop.x = vPrevBottom.x = vecs[vertOrder[3][0]]->x; + vPrevTop.y = vPrevBottom.y = vecs[vertOrder[3][1]]->y; + + for ( int i=0; i < 4; i++ ) + { + vTop.x = vBottom.x = vecs[vertOrder[i][0]]->x; + vTop.y = vBottom.y = vecs[vertOrder[i][1]]->y; + + // Draw the top line. + pPad->DrawLine( CSPVert( vPrevTop, vColor ), CSPVert( vTop, vColor ) ); + pPad->DrawLine( CSPVert( vPrevBottom, vColor ), CSPVert( vBottom, vColor ) ); + pPad->DrawLine( CSPVert( vTop, vColor ), CSPVert( vBottom, vColor ) ); + + vPrevTop = vTop; + vPrevBottom = vBottom; + } +} + + +#endif // !_STATIC_LINKED || _SHARED_LIB + diff --git a/public/ScratchPadUtils.h b/public/ScratchPadUtils.h new file mode 100644 index 0000000..aa51d28 --- /dev/null +++ b/public/ScratchPadUtils.h @@ -0,0 +1,163 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This module contains helper functions for use with scratch pads. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCRATCHPADUTILS_H +#define SCRATCHPADUTILS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iscratchpad3d.h" + + +// Use this to make a graph. +class CScratchPadGraph +{ +public: + + typedef int LineID; + + CScratchPadGraph(); + + // Initialze the orientation and scales of the two axes. + // Axis indices are 0, 1, or 2 for x, y, and z. + void Init( + IScratchPad3D *pPad, + + Vector vTimeAxis = Vector(0,-1,0), + float flInchesPerSecond=1, + Vector vTimeLineColor=Vector(0,0,1), + float flTimeOrigin=0, // Where the origin of the graph is. + + float flTimeLabelEveryNSeconds=1, + + Vector vValueAxis = Vector(0,0,1), + float flInchesPerValue=1, + Vector vValueLineColor=Vector(1,0,0), + float flValueOrigin=0 // Where the origin of the graph is. + + ); + + bool IsInitted() const; + + // Add another line into the graph. + LineID AddLine( Vector vColor ); + void AddSample( LineID iLine, float flTime, float flValue ); + void AddVerticalLine( float flTime, float flMinValue, float flMaxValue, const CSPColor &vColor ); + + // Get the 3D position of a sample on the graph (so you can draw other things there). + Vector GetSamplePosition( float flTime, float flValue ); + + +private: + + void UpdateTicksAndStuff( float flTime, float flValue ); + + + +private: + class CLineInfo + { + public: + bool m_bFirst; + float m_flLastTime; + float m_flLastValue; + Vector m_vColor; + }; + + IScratchPad3D *m_pPad; + + CUtlVector m_LineInfos; + + Vector m_vTimeAxis; + float m_flInchesPerSecond; + + Vector m_vValueAxis; + float m_flInchesPerValue; + + // How often to make a time label. + float m_flTimeLabelEveryNSeconds; + int m_nTimeLabelsDrawn; + + Vector m_vTimeLineColor; + Vector m_vValueLineColor; + + float m_flTimeOrigin; + float m_flValueOrigin; + + // Used to extend the value border. + float m_flHighestValue; + float m_flHighestTime; +}; + + + +// Draw a cone. +void ScratchPad_DrawLitCone( + IScratchPad3D *pPad, + const Vector &vBaseCenter, + const Vector &vTip, + const Vector &vBrightColor, + const Vector &vDarkColor, + const Vector &vLightDir, + float baseWidth, + int nSegments ); + + +// Draw a cylinder. +void ScratchPad_DrawLitCylinder( + IScratchPad3D *pPad, + const Vector &v1, + const Vector &v2, + const Vector &vBrightColor, + const Vector &vDarkColor, + const Vector &vLightDir, + float width, + int nSegments ); + + +// Draw an arrow. +void ScratchPad_DrawArrow( + IScratchPad3D *pPad, + const Vector &vPos, + const Vector &vDirection, + const Vector &vColor, + float flLength=20, + float flLineWidth=3, + float flHeadWidth=8, + int nCylinderSegments=5, + int nHeadSegments=8, + float flArrowHeadPercentage = 0.3f // How much of the line is the arrow head. + ); + + +// Draw an arrow with less parameters.. it generates parameters based on length +// automatically to make the arrow look good. +void ScratchPad_DrawArrowSimple( + IScratchPad3D *pPad, + const Vector &vPos, + const Vector &vDirection, + const Vector &vColor, + float flLength ); + +void ScratchPad_DrawSphere( + IScratchPad3D *pPad, + const Vector &vCenter, + float flRadius, + const Vector &vColor, + int nSubDivs=7 ); + + +void ScratchPad_DrawAABB( + IScratchPad3D *pPad, + const Vector &vMins, + const Vector &vMaxs, + const Vector &vColor = Vector( 1,1,1 ) ); + + +#endif // SCRATCHPADUTILS_H diff --git a/public/SoundEmitterSystem/isoundemittersystembase.h b/public/SoundEmitterSystem/isoundemittersystembase.h new file mode 100644 index 0000000..d0bbe4b --- /dev/null +++ b/public/SoundEmitterSystem/isoundemittersystembase.h @@ -0,0 +1,283 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ISOUNDEMITTERSYSTEMBASE_H +#define ISOUNDEMITTERSYSTEMBASE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utldict.h" +#include "soundflags.h" +#include "mathlib/compressed_vector.h" +#include "appframework/IAppSystem.h" +#include "tier3/tier3.h" + + +#define SOUNDGENDER_MACRO "$gender" +#define SOUNDGENDER_MACRO_LENGTH 7 // Length of above including $ + +class KeyValues; +typedef unsigned int HSOUNDSCRIPTHASH; +#define SOUNDEMITTER_INVALID_HASH (HSOUNDSCRIPTHASH)-1 + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct CSoundParameters +{ + CSoundParameters() + { + channel = CHAN_AUTO; // 0 + volume = VOL_NORM; // 1.0f + pitch = PITCH_NORM; // 100 + + pitchlow = PITCH_NORM; + pitchhigh = PITCH_NORM; + + soundlevel = SNDLVL_NORM; // 75dB + soundname[ 0 ] = 0; + play_to_owner_only = false; + count = 0; + + delay_msec = 0; + + m_nSoundEntryVersion = 1; + m_hSoundScriptHash = SOUNDEMITTER_INVALID_HASH; + m_pOperatorsKV = NULL; + m_nRandomSeed = -1; + + } + + int channel; + float volume; + int pitch; + int pitchlow, pitchhigh; + soundlevel_t soundlevel; + // For weapon sounds... + bool play_to_owner_only; + int count; + char soundname[ 128 ]; + int delay_msec; + HSOUNDSCRIPTHASH m_hSoundScriptHash; + int m_nSoundEntryVersion; + KeyValues *m_pOperatorsKV; + int m_nRandomSeed; + +}; + +// A bit of a hack, but these are just utility function which are implemented in the SouneParametersInternal.cpp file which all users of this lib also compile +const char *SoundLevelToString( soundlevel_t level ); +const char *ChannelToString( int channel ); +const char *VolumeToString( float volume ); +const char *PitchToString( float pitch ); +soundlevel_t TextToSoundLevel( const char *key ); +int TextToChannel( const char *name ); + + +enum gender_t +{ + GENDER_NONE = 0, + GENDER_MALE, + GENDER_FEMALE, +}; + + +#pragma pack(1) +struct SoundFile +{ + SoundFile() + { + symbol = UTL_INVAL_SYMBOL; + gender = GENDER_NONE; + available = true; + COMPILE_TIME_ASSERT( sizeof(SoundFile) == 4 ); + } + + CUtlSymbol symbol; + byte gender; + byte available; +}; + +#pragma pack() + +#pragma pack(1) +template +struct sound_interval_t +{ + T start; + T range; + + interval_t &ToInterval( interval_t &dest ) const { dest.start = start; dest.range = range; return dest; } + void FromInterval( const interval_t &from ) { start = from.start; range = from.range; } + float Random() const { return RandomFloat( start, start+range ); } +}; + + +#pragma pack() + +typedef sound_interval_t volume_interval_t; +typedef sound_interval_t soundlevel_interval_t; +typedef sound_interval_t pitch_interval_t; + +#pragma pack(1) +struct CSoundParametersInternal +{ + CSoundParametersInternal(); + ~CSoundParametersInternal(); + + void CopyFrom( const CSoundParametersInternal& src ); + + bool operator == ( const CSoundParametersInternal& other ) const; + + const char *VolumeToString( void ) const; + const char *ChannelToString( void ) const; + const char *SoundLevelToString( void ) const; + const char *PitchToString( void ) const; + + void VolumeFromString( const char *sz ); + void ChannelFromString( const char *sz ); + void PitchFromString( const char *sz ); + void SoundLevelFromString( const char *sz ); + + int GetChannel() const { return channel; } + const volume_interval_t &GetVolume() const { return volume; } + const pitch_interval_t &GetPitch() const { return pitch; } + const soundlevel_interval_t &GetSoundLevel() const { return soundlevel; } + int GetDelayMsec() const { return delay_msec; } + bool OnlyPlayToOwner() const { return play_to_owner_only; } + bool HadMissingWaveFiles() const { return had_missing_wave_files; } + bool UsesGenderToken() const { return uses_gender_token; } + bool ShouldPreload() const { return m_bShouldPreload; } + + void SetChannel( int newChannel ) { channel = newChannel; } + void SetVolume( float start, float range = 0.0 ) { volume.start = start; volume.range = range; } + void SetPitch( float start, float range = 0.0 ) { pitch.start = start; pitch.range = range; } + void SetSoundLevel( float start, float range = 0.0 ) { soundlevel.start = start; soundlevel.range = range; } + void SetDelayMsec( int delay ) { delay_msec = delay; } + void SetShouldPreload( bool bShouldPreload ) { m_bShouldPreload = bShouldPreload; } + void SetOnlyPlayToOwner( bool b ) { play_to_owner_only = b; } + void SetHadMissingWaveFiles( bool b ) { had_missing_wave_files = b; } + void SetUsesGenderToken( bool b ) { uses_gender_token = b; } + + void AddSoundName( const SoundFile &soundFile ) { AddToTail( &m_pSoundNames, &m_nSoundNames, soundFile ); } + int NumSoundNames() const { return m_nSoundNames; } + SoundFile * GetSoundNames() { return ( m_nSoundNames == 1 ) ? (SoundFile *)&m_pSoundNames : m_pSoundNames; } + const SoundFile *GetSoundNames() const { return ( m_nSoundNames == 1 ) ? (SoundFile *)&m_pSoundNames : m_pSoundNames; } + + void AddConvertedName( const SoundFile &soundFile ) { AddToTail( &m_pConvertedNames, &m_nConvertedNames, soundFile ); } + int NumConvertedNames() const { return m_nConvertedNames; } + SoundFile * GetConvertedNames() { return ( m_nConvertedNames == 1 ) ? (SoundFile *)&m_pConvertedNames : m_pConvertedNames; } + const SoundFile *GetConvertedNames() const { return ( m_nConvertedNames == 1 ) ? (SoundFile *)&m_pConvertedNames : m_pConvertedNames; } + +private: + void operator=( const CSoundParametersInternal& src ); // disallow implicit copies + CSoundParametersInternal( const CSoundParametersInternal& src ); + + void AddToTail( SoundFile **pDest, uint16 *pDestCount, const SoundFile &source ); + + SoundFile * m_pSoundNames; // 4 + SoundFile * m_pConvertedNames; // 8 + uint16 m_nSoundNames; // 10 + uint16 m_nConvertedNames; // 12 + + volume_interval_t volume; // 16 + soundlevel_interval_t soundlevel; // 20 + pitch_interval_t pitch; // 22 + uint16 channel; // 24 + uint16 delay_msec; // 26 + + bool play_to_owner_only:1; // For weapon sounds... // 27 + // Internal use, for warning about missing .wav files + bool had_missing_wave_files:1; + bool uses_gender_token:1; + bool m_bShouldPreload:1; + + byte reserved; // 28 + + KeyValues * m_pGameData; // 32 +}; +#pragma pack() + + +//----------------------------------------------------------------------------- +// Purpose: Base class for sound emitter system handling (can be used by tools) +//----------------------------------------------------------------------------- +abstract_class ISoundEmitterSystemBase : public IAppSystem +{ +public: + // Unused, left in the interface so I don't have to rebuild all + virtual void Unused1() {} + virtual void Unused2() {} + + virtual int GetSoundIndex( const char *pName ) const = 0; + virtual bool IsValidIndex( int index ) = 0; + virtual int GetSoundCount( void ) = 0; + + virtual const char *GetSoundName( int index ) = 0; + virtual bool GetParametersForSound( const char *soundname, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ) = 0; + + virtual const char *GetWaveName( CUtlSymbol& sym ) = 0; + virtual CUtlSymbol AddWaveName( const char *name ) = 0; + + virtual soundlevel_t LookupSoundLevel( const char *soundname ) = 0; + virtual const char *GetWavFileForSound( const char *soundname, char const *actormodel ) = 0; + virtual const char *GetWavFileForSound( const char *soundname, gender_t gender ) = 0; + virtual int CheckForMissingWavFiles( bool verbose ) = 0; + virtual const char *GetSourceFileForSound( int index ) const = 0; + + // Iteration methods + virtual int First() const = 0; + virtual int Next( int i ) const = 0; + virtual int InvalidIndex() const = 0; + + virtual CSoundParametersInternal *InternalGetParametersForSound( int index ) = 0; + + // The host application is responsible for dealing with dirty sound scripts, etc. + virtual bool AddSound( const char *soundname, const char *scriptfile, const CSoundParametersInternal& params ) = 0; + virtual void RemoveSound( const char *soundname ) = 0; + virtual void MoveSound( const char *soundname, const char *newscript ) = 0; + virtual void RenameSound( const char *soundname, const char *newname ) = 0; + + virtual void UpdateSoundParameters( const char *soundname, const CSoundParametersInternal& params ) = 0; + + virtual int GetNumSoundScripts() const = 0; + virtual char const *GetSoundScriptName( int index ) const = 0; + virtual bool IsSoundScriptDirty( int index ) const = 0; + virtual int FindSoundScript( const char *name ) const = 0; + virtual void SaveChangesToSoundScript( int scriptindex ) = 0; + + virtual void ExpandSoundNameMacros( CSoundParametersInternal& params, char const *wavename ) = 0; + virtual gender_t GetActorGender( char const *actormodel ) = 0; + virtual void GenderExpandString( char const *actormodel, char const *in, char *out, int maxlen ) = 0; + virtual void GenderExpandString( gender_t gender, char const *in, char *out, int maxlen ) = 0; + virtual bool IsUsingGenderToken( char const *soundname ) = 0; + + // For blowing away caches based on filetimstamps of the manifest, or of any of the + // .txt files that are read into the sound emitter system + virtual unsigned int GetManifestFileTimeChecksum() = 0; + + virtual bool GetParametersForSoundEx( const char *soundname, HSOUNDSCRIPTHASH& handle, CSoundParameters& params, gender_t gender, bool isbeingemitted = false ) = 0; + virtual soundlevel_t LookupSoundLevelByHandle( char const *soundname, HSOUNDSCRIPTHASH& handle ) = 0; + virtual KeyValues *GetOperatorKVByHandle( HSOUNDSCRIPTHASH& handle ) = 0; + + virtual char const *GetSoundNameForHash( HSOUNDSCRIPTHASH hash ) const = 0; // Returns NULL if hash not found!!! + virtual int GetSoundIndexForHash( HSOUNDSCRIPTHASH hash ) const = 0; + virtual HSOUNDSCRIPTHASH HashSoundName( char const *pchSndName ) const = 0; + virtual bool IsValidHash( HSOUNDSCRIPTHASH hash ) const = 0; + + virtual void DescribeSound( char const *soundname ) = 0; + // Flush and reload + virtual void Flush() = 0; + + virtual void AddSoundsFromFile( const char *filename, bool bPreload, bool bAutoCache, bool bIsOverride = false ) = 0; + +}; + +#endif // ISOUNDEMITTERSYSTEMBASE_H diff --git a/public/SoundParametersInternal.cpp b/public/SoundParametersInternal.cpp new file mode 100644 index 0000000..510deb0 --- /dev/null +++ b/public/SoundParametersInternal.cpp @@ -0,0 +1,589 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "cbase.h" + +#if !defined(_STATIC_LINKED) || defined(SOUNDEMITTERSYSTEM_DLL) + +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "tier2/interval.h" +#include "soundchars.h" +#include "KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +struct SoundChannels +{ + int channel; + const char *name; +}; + +// NOTE: This will need to be updated if channel names are added/removed +static SoundChannels g_pChannelNames[] = +{ + { CHAN_AUTO, "CHAN_AUTO" }, + { CHAN_WEAPON, "CHAN_WEAPON" }, + { CHAN_VOICE, "CHAN_VOICE" }, + { CHAN_ITEM, "CHAN_ITEM" }, + { CHAN_BODY, "CHAN_BODY" }, + { CHAN_STREAM, "CHAN_STREAM" }, + { CHAN_STATIC, "CHAN_STATIC" }, +}; + +struct VolumeLevel +{ + float volume; + const char *name; +}; + +static VolumeLevel g_pVolumeLevels[] = +{ + { VOL_NORM, "VOL_NORM" }, +}; + +struct PitchLookup +{ + float pitch; + const char *name; +}; + +static PitchLookup g_pPitchLookup[] = +{ + { PITCH_NORM, "PITCH_NORM" }, + { PITCH_LOW, "PITCH_LOW" }, + { PITCH_HIGH, "PITCH_HIGH" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct SoundLevelLookup +{ + soundlevel_t level; + char const *name; +}; + +// NOTE: Needs to reflect the soundlevel_t enum defined in soundflags.h +static SoundLevelLookup g_pSoundLevels[] = +{ + { SNDLVL_NONE, "SNDLVL_NONE" }, + { SNDLVL_20dB, "SNDLVL_20dB" }, + { SNDLVL_25dB, "SNDLVL_25dB" }, + { SNDLVL_30dB, "SNDLVL_30dB" }, + { SNDLVL_35dB, "SNDLVL_35dB" }, + { SNDLVL_40dB, "SNDLVL_40dB" }, + { SNDLVL_45dB, "SNDLVL_45dB" }, + { SNDLVL_50dB, "SNDLVL_50dB" }, + { SNDLVL_55dB, "SNDLVL_55dB" }, + { SNDLVL_IDLE, "SNDLVL_IDLE" }, + { SNDLVL_TALKING, "SNDLVL_TALKING" }, + { SNDLVL_60dB, "SNDLVL_60dB" }, + { SNDLVL_65dB, "SNDLVL_65dB" }, + { SNDLVL_STATIC, "SNDLVL_STATIC" }, + { SNDLVL_70dB, "SNDLVL_70dB" }, + { SNDLVL_NORM, "SNDLVL_NORM" }, + { SNDLVL_75dB, "SNDLVL_75dB" }, + { SNDLVL_80dB, "SNDLVL_80dB" }, + { SNDLVL_85dB, "SNDLVL_85dB" }, + { SNDLVL_90dB, "SNDLVL_90dB" }, + { SNDLVL_95dB, "SNDLVL_95dB" }, + { SNDLVL_100dB, "SNDLVL_100dB" }, + { SNDLVL_105dB, "SNDLVL_105dB" }, + { SNDLVL_110dB, "SNDLVL_110dB" }, + { SNDLVL_120dB, "SNDLVL_120dB" }, + { SNDLVL_130dB, "SNDLVL_130dB" }, + { SNDLVL_GUNFIRE, "SNDLVL_GUNFIRE" }, + { SNDLVL_140dB, "SNDLVL_140dB" }, + { SNDLVL_150dB, "SNDLVL_150dB" }, + { SNDLVL_180dB, "SNDLVL_180dB" }, +}; + +static const char *_SoundLevelToString( soundlevel_t level ) +{ + int c = ARRAYSIZE( g_pSoundLevels ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + SoundLevelLookup *entry = &g_pSoundLevels[ i ]; + if ( entry->level == level ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%i", (int)level ); + return sz; +} + +static const char *_ChannelToString( int channel ) +{ + int c = ARRAYSIZE( g_pChannelNames ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + SoundChannels *entry = &g_pChannelNames[ i ]; + if ( entry->channel == channel ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%i", (int)channel ); + return sz; +} + +static const char *_VolumeToString( float volume ) +{ + int c = ARRAYSIZE( g_pVolumeLevels ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + VolumeLevel *entry = &g_pVolumeLevels[ i ]; + if ( entry->volume == volume ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%.3f", volume ); + return sz; +} + +static const char *_PitchToString( float pitch ) +{ + int c = ARRAYSIZE( g_pPitchLookup ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + PitchLookup *entry = &g_pPitchLookup[ i ]; + if ( entry->pitch == pitch ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%.3f", pitch ); + return sz; +} + +#define SNDLVL_PREFIX "SNDLVL_" + +soundlevel_t TextToSoundLevel( const char *key ) +{ + if ( !key ) + { + Assert( 0 ); + return SNDLVL_NORM; + } + + int c = ARRAYSIZE( g_pSoundLevels ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + SoundLevelLookup *entry = &g_pSoundLevels[ i ]; + if ( !Q_strcasecmp( key, entry->name ) ) + return entry->level; + } + + if ( !Q_strnicmp( key, SNDLVL_PREFIX, Q_strlen( SNDLVL_PREFIX ) ) ) + { + char const *val = key + Q_strlen( SNDLVL_PREFIX ); + int sndlvl = atoi( val ); + if ( sndlvl > 0 && sndlvl <= 180 ) + { + return ( soundlevel_t )sndlvl; + } + } + + DevMsg( "CSoundEmitterSystem: Unknown sound level %s\n", key ); + + return SNDLVL_NORM; +} + +//----------------------------------------------------------------------------- +// Purpose: Convert "chan_xxx" into integer value for channel +// Input : *name - +// Output : static int +//----------------------------------------------------------------------------- +int TextToChannel( const char *name ) +{ + if ( !name ) + { + Assert( 0 ); + // CHAN_AUTO + return CHAN_AUTO; + } + + if ( Q_strncasecmp( name, "chan_", strlen( "chan_" ) ) ) + { + return atoi( name ); + } + + int c = ARRAYSIZE( g_pChannelNames ); + int i; + + for ( i = 0; i < c; i++ ) + { + if ( !Q_strcasecmp( name, g_pChannelNames[ i ].name ) ) + { + return g_pChannelNames[ i ].channel; + } + } + + // At this point, it starts with chan_ but is not recognized + // atoi would return 0, so just do chan auto + DevMsg( "CSoundEmitterSystem: Warning, unknown channel type in sounds.txt (%s)\n", name ); + + return CHAN_AUTO; +} + +const char *SoundLevelToString( soundlevel_t level ) +{ + int c = ARRAYSIZE( g_pSoundLevels ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + SoundLevelLookup *entry = &g_pSoundLevels[ i ]; + if ( entry->level == level ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%i", (int)level ); + return sz; +} + +const char *ChannelToString( int channel ) +{ + int c = ARRAYSIZE( g_pChannelNames ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + SoundChannels *entry = &g_pChannelNames[ i ]; + if ( entry->channel == channel ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%i", (int)channel ); + return sz; +} + +const char *VolumeToString( float volume ) +{ + int c = ARRAYSIZE( g_pVolumeLevels ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + VolumeLevel *entry = &g_pVolumeLevels[ i ]; + if ( entry->volume == volume ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%.3f", volume ); + return sz; +} + +const char *PitchToString( float pitch ) +{ + int c = ARRAYSIZE( g_pPitchLookup ); + + int i; + + for ( i = 0 ; i < c; i++ ) + { + PitchLookup *entry = &g_pPitchLookup[ i ]; + if ( entry->pitch == pitch ) + return entry->name; + } + + static char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%.3f", pitch ); + return sz; +} + +CSoundParametersInternal::CSoundParametersInternal() +{ + m_pConvertedNames = m_pSoundNames = NULL; + m_nConvertedNames = m_nSoundNames = 0; + channel = CHAN_AUTO; // 0 + + volume.start = VOL_NORM; // 1.0f + volume.range = 0.0f; + + pitch.start = PITCH_NORM; // 100 + pitch.range = 0; + + soundlevel.start = SNDLVL_NORM; // 75dB + soundlevel.range = 0; + delay_msec = 0; + play_to_owner_only = false; + had_missing_wave_files = false; + uses_gender_token = false; + + // TERROR: + m_pGameData = NULL; + +} + +CSoundParametersInternal::CSoundParametersInternal( const CSoundParametersInternal& src ) +{ + m_pSoundNames = NULL; + m_pConvertedNames = NULL; + // TERROR: + m_pGameData = NULL; + // TERROR: + m_pGameData = NULL; + + CopyFrom( src ); +} + +CSoundParametersInternal::~CSoundParametersInternal() +{ + if ( m_nSoundNames > 1 ) + free(m_pSoundNames ); + if ( m_nConvertedNames > 1 ) + free( m_pConvertedNames); + + m_pConvertedNames = NULL; + m_pSoundNames = NULL; + m_nSoundNames = 0; + m_nConvertedNames = 0; +} + +void CSoundParametersInternal::CopyFrom( const CSoundParametersInternal& src ) +{ + if ( m_nSoundNames > 1 ) + free(m_pSoundNames); + if ( m_nConvertedNames > 1 ) + free(m_pConvertedNames); + + channel = src.channel; + volume = src.volume; + pitch = src.pitch; + soundlevel = src.soundlevel; + delay_msec = src.delay_msec; + play_to_owner_only = src.play_to_owner_only; + + m_nSoundNames = src.m_nSoundNames; + if ( m_nSoundNames ) + { + if ( m_nSoundNames > 1 ) + { + m_pSoundNames = (SoundFile*)malloc( sizeof(SoundFile)*m_nSoundNames); + memcpy( m_pSoundNames, src.m_pSoundNames, m_nSoundNames * sizeof(SoundFile) ); + } + else + { + m_pSoundNames = src.m_pSoundNames; + } + } + else + { + m_pSoundNames = NULL; + } + + m_nConvertedNames = src.m_nConvertedNames; + if ( m_nConvertedNames ) + { + if ( m_nConvertedNames > 1 ) + { + m_pConvertedNames = (SoundFile*)malloc( sizeof(SoundFile)*m_nConvertedNames); + memcpy( m_pConvertedNames, src.m_pConvertedNames, m_nConvertedNames * sizeof(SoundFile) ); + } + else + { + m_pConvertedNames = src.m_pConvertedNames; + } + } + else + { + m_pConvertedNames = NULL; + } + + had_missing_wave_files = src.had_missing_wave_files; + uses_gender_token = src.uses_gender_token; +} + +#define CompareInterval( i1, i2 ) ( memcmp( &(i1), &(i2), sizeof(i1) ) == 0 ) + +bool CSoundParametersInternal::operator == ( const CSoundParametersInternal& other ) const +{ + if ( this == &other ) + return true; + + if ( channel != other.channel ) + return false; + if ( !CompareInterval( volume, other.volume ) ) + return false; + if ( !CompareInterval( pitch, other.pitch ) ) + return false; + if ( !CompareInterval( soundlevel, other.soundlevel ) ) + return false; + if ( delay_msec != other.delay_msec ) + return false; + if ( play_to_owner_only != other.play_to_owner_only ) + return false; + + if ( m_nSoundNames != other.m_nSoundNames ) + return false; + + // Compare items + int c = m_nSoundNames; + for ( int i = 0; i < c; i++ ) + { + if ( GetSoundNames()[ i ].symbol != other.GetSoundNames()[ i ].symbol ) + return false; + } + + return true; +} + +float16 ZERO_FLOAT16; + +const char *CSoundParametersInternal::VolumeToString( void ) const +{ + if ( volume.range == ZERO_FLOAT16 ) + { + return _VolumeToString( volume.start ); + } + + static char sz[ 64 ]; + Q_snprintf( sz, sizeof( sz ), "%.3f, %.3f", (float)volume.start, (float)volume.start + (float)volume.range ); + return sz; +} + +const char *CSoundParametersInternal::ChannelToString( void ) const +{ + return _ChannelToString( channel ); +} + +const char *CSoundParametersInternal::SoundLevelToString( void ) const +{ + if ( soundlevel.range == 0 ) + { + return _SoundLevelToString( (soundlevel_t)(int)soundlevel.start ); + } + + static char sz[ 64 ]; + Q_snprintf( sz, sizeof( sz ), "%i, %i", (soundlevel_t)(int)soundlevel.start, (soundlevel_t)(int)(soundlevel.start + soundlevel.range ) ); + return sz; +} + +const char *CSoundParametersInternal::PitchToString( void ) const +{ + if ( pitch.range == 0 ) + { + return _PitchToString( (int)pitch.start ); + } + + static char sz[ 64 ]; + Q_snprintf( sz, sizeof( sz ), "%i, %i", (int)pitch.start, (int)(pitch.start + pitch.range ) ); + return sz; +} + +void CSoundParametersInternal::VolumeFromString( const char *sz ) +{ + if ( !Q_strcasecmp( sz, "VOL_NORM" ) ) + { + volume.start = VOL_NORM; + volume.range = 0.0f; + } + else + { + volume.FromInterval( ReadInterval( sz ) ); + } +} + +void CSoundParametersInternal::ChannelFromString( const char *sz ) +{ + channel = TextToChannel( sz ); +} + +void CSoundParametersInternal::PitchFromString( const char *sz ) +{ + if ( !Q_strcasecmp( sz, "PITCH_NORM" ) ) + { + pitch.start = PITCH_NORM; + pitch.range = 0; + } + else if ( !Q_strcasecmp( sz, "PITCH_LOW" ) ) + { + pitch.start = PITCH_LOW; + pitch.range = 0; + } + else if ( !Q_strcasecmp( sz, "PITCH_HIGH" ) ) + { + pitch.start = PITCH_HIGH; + pitch.range = 0; + } + else + { + pitch.FromInterval( ReadInterval( sz ) ); + } +} + +void CSoundParametersInternal::SoundLevelFromString( const char *sz ) +{ + if ( !Q_strncasecmp( sz, "SNDLVL_", strlen( "SNDLVL_" ) ) ) + { + soundlevel.start = TextToSoundLevel( sz ); + soundlevel.range = 0; + } + else + { + soundlevel.FromInterval( ReadInterval( sz ) ); + } +} + +void CSoundParametersInternal::AddToTail( SoundFile **pDest, uint16 *pDestCount, const SoundFile &source ) +{ + (*pDestCount)++; + if ( *pDestCount == 1 ) + { + // NOTE: when there's only one soundfile in the list, we store it + // packed into the pointer itself, the four bytes for the pointer is just used to store the sound file! + COMPILE_TIME_ASSERT( sizeof(SoundFile) <= sizeof(SoundFile *) ); + *((SoundFile *)(pDest)) = source; + } + else + { + SoundFile temp; + if ( *pDestCount == 2 ) + { + // Copying from a list of one soundfile. Save off the struct + // packed into the pointer field. + temp = *((SoundFile *)(pDest)); + *pDest = NULL; + } + + *pDest = (SoundFile *)realloc( *pDest, (*pDestCount) * sizeof(SoundFile) ); + (*pDest)[ *pDestCount - 1 ] = source; + + if ( *pDestCount == 2 ) + { + (*pDest)[0] = temp; + } + } +} + +#endif // !_STATIC_LINKED || SOUNDEMITTERSYSTEM_DLL diff --git a/public/UnicodeFileHelpers.cpp b/public/UnicodeFileHelpers.cpp new file mode 100644 index 0000000..6f5f864 --- /dev/null +++ b/public/UnicodeFileHelpers.cpp @@ -0,0 +1,240 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include +#include +#include "UtlBuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Advances until non-whitespace hit +//----------------------------------------------------------------------------- +ucs2 *AdvanceOverWhitespace(ucs2 *Start) +{ + while (*Start != 0 && iswspace(*Start)) + { + Start++; + } + + return Start; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ucs2 *ReadUnicodeToken(ucs2 *start, ucs2 *token, int tokenBufferSize, bool "ed) +{ + // skip over any whitespace + start = AdvanceOverWhitespace(start); + quoted = false; + *token = 0; + + if (!*start) + { + return start; + } + + // check to see if it's a quoted string + if (*start == '\"') + { + quoted = true; + // copy out the string until we hit an end quote + start++; + int count = 0; + while (*start && *start != '\"' && count < tokenBufferSize-1) + { + // check for special characters + if (*start == '\\' && *(start+1) == 'n') + { + start++; + *token = '\n'; + } + else if (*start == '\\' && *(start+1) == '\"') + { + start++; + *token = '\"'; + } + else + { + *token = *start; + } + + start++; + token++; + count++; + } + + if (*start == '\"') + { + start++; + } + } + else + { + // copy out the string until we hit a whitespace + int count = 0; + while (*start && !iswspace(*start) && count < tokenBufferSize-1) + { + // no checking for special characters if it's not a quoted string + *token = *start; + + start++; + token++; + count++; + } + } + + *token = 0; + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: Same as above but no translation of \n +//----------------------------------------------------------------------------- +ucs2 *ReadUnicodeTokenNoSpecial(ucs2 *start, ucs2 *token, int tokenBufferSize, bool "ed) +{ + // skip over any whitespace + start = AdvanceOverWhitespace(start); + quoted = false; + *token = 0; + + if (!*start) + { + return start; + } + + // check to see if it's a quoted string + if (*start == '\"') + { + quoted = true; + // copy out the string until we hit an end quote + start++; + int count = 0; + while (*start && *start != '\"' && count < tokenBufferSize-1) + { + // check for special characters + /* + if (*start == '\\' && *(start+1) == 'n') + { + start++; + *token = '\n'; + } + else + */ + if (*start == '\\' && *(start+1) == '\"') + { + start++; + *token = '\"'; + } + else + { + *token = *start; + } + + start++; + token++; + count++; + } + + if (*start == '\"') + { + start++; + } + } + else + { + // copy out the string until we hit a whitespace + int count = 0; + while (*start && !iswspace(*start) && count < tokenBufferSize-1) + { + // no checking for special characters if it's not a quoted string + *token = *start; + + start++; + token++; + count++; + } + } + + *token = 0; + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the first character after the next EOL characters +//----------------------------------------------------------------------------- +ucs2 *ReadToEndOfLine(ucs2 *start) +{ + if (!*start) + return start; + + while (*start) + { + if (*start == 0x0D || *start== 0x0A) + break; + start++; + } + + while (*start == 0x0D || *start== 0x0A) + start++; + + return start; +} + +//----------------------------------------------------------------------------- +// Purpose: file writing +//----------------------------------------------------------------------------- +void WriteUnicodeString(CUtlBuffer &buf, const wchar_t *string, bool addQuotes) +{ + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } + + for (const wchar_t *ws = string; *ws != 0; ws++) + { + // handle special characters + if (addQuotes && *ws == '\"') + { + buf.PutUnsignedShort('\\'); + } + // write the character + buf.PutUnsignedShort(*ws); + } + + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } +} + +//----------------------------------------------------------------------------- +// Purpose: file writing +//----------------------------------------------------------------------------- +void WriteAsciiStringAsUnicode(CUtlBuffer &buf, const char *string, bool addQuotes) +{ + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } + + for (const char *sz = string; *sz != 0; sz++) + { + // handle special characters + if (addQuotes && *sz == '\"') + { + buf.PutUnsignedShort('\\'); + } + buf.PutUnsignedShort(*sz); + } + + if (addQuotes) + { + buf.PutUnsignedShort('\"'); + } +} diff --git a/public/UnicodeFileHelpers.h b/public/UnicodeFileHelpers.h new file mode 100644 index 0000000..48551cb --- /dev/null +++ b/public/UnicodeFileHelpers.h @@ -0,0 +1,28 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef UNICODEFILEHELPERS_H +#define UNICODEFILEHELPERS_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +// helper functions for parsing unicode file buffers +ucs2 *AdvanceOverWhitespace(ucs2 *start); +ucs2 *ReadUnicodeToken(ucs2 *start, ucs2 *token, int tokenBufferSize, bool "ed); +ucs2 *ReadUnicodeTokenNoSpecial(ucs2 *start, ucs2 *token, int tokenBufferSize, bool "ed); +ucs2 *ReadToEndOfLine(ucs2 *start); + +// writing to unicode files via CUtlBuffer +class CUtlBuffer; +void WriteUnicodeString(CUtlBuffer &buffer, const wchar_t *string, bool addQuotes = false); +void WriteAsciiStringAsUnicode(CUtlBuffer &buffer, const char *string, bool addQuotes = false); + + + +#endif // UNICODEFILEHELPERS_H diff --git a/public/UtlCachedFileData.h b/public/UtlCachedFileData.h new file mode 100644 index 0000000..c8e48a2 --- /dev/null +++ b/public/UtlCachedFileData.h @@ -0,0 +1,1084 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef UTLCACHEDFILEDATA_H +#define UTLCACHEDFILEDATA_H +#if defined( WIN32 ) +#pragma once +#endif + +#include "filesystem.h" // FileNameHandle_t +#include "utlrbtree.h" +#include "utlbuffer.h" +#include "UtlSortVector.h" +#include "tier1/strtools.h" + +#include "tier0/memdbgon.h" + +// If you change to serialization protocols, this must be bumped... +#define UTL_CACHE_SYSTEM_VERSION 2 + +#define UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO (long)-2 + +// Cacheable types must derive from this and implement the appropriate methods... +abstract_class IBaseCacheInfo +{ +public: + virtual void Save( CUtlBuffer& buf ) = 0; + virtual void Restore( CUtlBuffer& buf ) = 0; + + virtual void Rebuild( char const *filename ) = 0; +}; + +typedef unsigned int (*PFNCOMPUTECACHEMETACHECKSUM)( void ); + +typedef enum +{ + UTL_CACHED_FILE_USE_TIMESTAMP = 0, + UTL_CACHED_FILE_USE_FILESIZE, +} UtlCachedFileDataType_t; + +template +class CUtlCachedFileData +{ +public: + CUtlCachedFileData + ( + char const *repositoryFileName, + int version, + PFNCOMPUTECACHEMETACHECKSUM checksumfunc = NULL, + UtlCachedFileDataType_t fileCheckType = UTL_CACHED_FILE_USE_TIMESTAMP, + bool nevercheckdisk = false, + bool readonly = false, + bool savemanifest = false + ) + : m_Elements( 0, 0, FileNameHandleLessFunc ), + m_sRepositoryFileName( repositoryFileName ), + m_nVersion( version ), + m_pfnMetaChecksum( checksumfunc ), + m_bDirty( false ), + m_bInitialized( false ), + m_uCurrentMetaChecksum( 0u ), + m_fileCheckType( fileCheckType ), + m_bNeverCheckDisk( nevercheckdisk ), + m_bReadOnly( readonly ), + m_bSaveManifest( savemanifest ) + { + Assert( m_sRepositoryFileName.Length() > 0 ); + } + + virtual ~CUtlCachedFileData() + { + m_Elements.RemoveAll(); + int c = m_Data.Count(); + for ( int i = 0; i < c ; ++i ) + { + delete m_Data[ i ]; + } + m_Data.RemoveAll(); + } + + T* Get( char const *filename ); + const T* Get( char const *filename ) const; + + T* operator[]( int i ); + const T* operator[]( int i ) const; + + int Count() const; + + void GetElementName( int i, char *buf, int buflen ) + { + buf[ 0 ] = 0; + if ( !m_Elements.IsValidIndex( i ) ) + return; + + g_pFullFileSystem->String( m_Elements[ i ].handle, buf, buflen ); + } + + bool EntryExists( char const *filename ) const + { + ElementType_t element; + element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); + int idx = m_Elements.Find( element ); + return idx != m_Elements.InvalidIndex() ? true : false; + } + + void SetElement( char const *name, long fileinfo, T* src ) + { + SetDirty( true ); + + int idx = GetIndex( name ); + + Assert( idx != m_Elements.InvalidIndex() ); + + ElementType_t& e = m_Elements[ idx ]; + + CUtlBuffer buf( 0, 0, 0 ); + + Assert( e.dataIndex != m_Data.InvalidIndex() ); + + T *dest = m_Data[ e.dataIndex ]; + + Assert( dest ); + + // I suppose we could do an assignment operator, but this should save/restore the data element just fine for + // tool purposes + ((IBaseCacheInfo *)src)->Save( buf ); + ((IBaseCacheInfo *)dest)->Restore( buf ); + + e.fileinfo = fileinfo; + if ( ( e.fileinfo == -1 ) && + ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) + { + e.fileinfo = 0; + } + // Force recheck + e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; + } + + // If you create a cache and don't call init/shutdown, you can call this to do a quick check to see if the checksum/version + // will cause a rebuild... + bool IsUpToDate(); + + void Shutdown(); + bool Init(); + + void Save(); + + void Reload(); + + void ForceRecheckDiskInfo(); + // Iterates all entries and gets filesystem info and optionally causes rebuild on any existing items which are out of date + void CheckDiskInfo( bool force_rebuild, long cacheFileTime = 0L ); + + void SaveManifest(); + bool ManifestExists(); + + long GetFileInfo( char const *filename ) + { + ElementType_t element; + element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); + int idx = m_Elements.Find( element ); + if ( idx == m_Elements.InvalidIndex() ) + { + return 0L; + } + + return m_Elements[ idx ].fileinfo; + } + long GetFileInfo( int idx ) + { + if ( !m_Elements.IsValidIndex( idx ) ) + return 0L; + return m_Elements[ idx ].fileinfo; + } + + int GetNumElements() + { + return m_Elements.Count(); + } + + bool IsDirty() const + { + return m_bDirty; + } + + T *RebuildItem( const char *filename ); + + void SetNeverCheckDisk( bool bNeverCheckDisk ); + + void RecheckItem( char const *filename ); + +private: + + void InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile ); + void InitLargeBuffer( FileHandle_t& fh, bool& deleteFile ); + + int GetIndex( const char *filename ) + { + ElementType_t element; + element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); + int idx = m_Elements.Find( element ); + if ( idx == m_Elements.InvalidIndex() ) + { + T *data = new T(); + + int dataIndex = m_Data.AddToTail( data ); + idx = m_Elements.Insert( element ); + m_Elements[ idx ].dataIndex = dataIndex; + } + + return idx; + } + + void CheckInit(); + + void SetDirty( bool dirty ) + { + m_bDirty = dirty; + } + + void RebuildCache( char const *filename, T *data ); + + struct ElementType_t + { + ElementType_t() : + handle( 0 ), + fileinfo( 0 ), + diskfileinfo( UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ), + dataIndex( -1 ) + { + } + + FileNameHandle_t handle; + long fileinfo; + long diskfileinfo; + int dataIndex; + }; + + static bool FileNameHandleLessFunc( ElementType_t const &lhs, ElementType_t const &rhs ) + { + return lhs.handle < rhs.handle; + } + + CUtlRBTree< ElementType_t > m_Elements; + CUtlVector< T * > m_Data; + CUtlString m_sRepositoryFileName; + int m_nVersion; + PFNCOMPUTECACHEMETACHECKSUM m_pfnMetaChecksum; + unsigned int m_uCurrentMetaChecksum; + UtlCachedFileDataType_t m_fileCheckType; + bool m_bNeverCheckDisk : 1; + bool m_bReadOnly : 1; + bool m_bSaveManifest : 1; + bool m_bDirty : 1; + bool m_bInitialized : 1; + +}; + + +template +T* CUtlCachedFileData::Get( char const *filename ) +{ + int idx = GetIndex( filename ); + + ElementType_t& e = m_Elements[ idx ]; + + if ( e.fileinfo == -1 && + m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) + { + e.fileinfo = 0; + } + long cachefileinfo = e.fileinfo; + // Set the disk fileinfo the first time we encounter the filename + if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) + { + if ( m_bNeverCheckDisk ) + { + e.diskfileinfo = cachefileinfo; + } + else + { + if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) + { + e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); + // Missing files get a disk file size of 0 + if ( e.diskfileinfo == -1 ) + { + e.diskfileinfo = 0; + } + } + else + { + e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); + } + } + } + + Assert( e.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ e.dataIndex ]; + + Assert( data ); + + // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct... + if ( cachefileinfo != e.diskfileinfo ) + { + if ( !m_bReadOnly ) + { + RebuildCache( filename, data ); + } + e.fileinfo = e.diskfileinfo; + } + + return data; +} + +template +const T* CUtlCachedFileData::Get( char const *filename ) const +{ + return const_cast< CUtlCachedFileData * >(this)->Get( filename ); +} + +template +T* CUtlCachedFileData::operator[]( int i ) +{ + return m_Data[ m_Elements[ i ].dataIndex ]; +} + +template +const T* CUtlCachedFileData::operator[]( int i ) const +{ + return m_Data[ m_Elements[ i ].dataIndex ]; +} + +template +int CUtlCachedFileData::Count() const +{ + return m_Elements.Count(); +} + +template +void CUtlCachedFileData::Reload() +{ + Shutdown(); + Init(); +} + +template +bool CUtlCachedFileData::IsUpToDate() +{ + // Don't call Init/Shutdown if using this method!!! + Assert( !m_bInitialized ); + + if ( !m_sRepositoryFileName.Length() ) + { + Error( "CUtlCachedFileData: Can't IsUpToDate, no repository file specified." ); + return false; + } + + // Always compute meta checksum + m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0; + + FileHandle_t fh; + + fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" ); + if ( fh == FILESYSTEM_INVALID_HANDLE ) + { + return false; + } + + // Version data is in first 12 bytes of file + byte header[ 12 ]; + g_pFullFileSystem->Read( header, sizeof( header ), fh ); + g_pFullFileSystem->Close( fh ); + + int cacheversion = *( int *)&header[ 0 ]; + + if ( UTL_CACHE_SYSTEM_VERSION != cacheversion ) + { + DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); + Assert( !m_bReadOnly ); + if ( !m_bReadOnly ) + { + g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); + } + return false; + } + + // Now parse data from the buffer + int version = *( int *)&header[ 4 ]; + if ( version != m_nVersion ) + { + DevMsg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); + Assert( !m_bReadOnly ); + if ( !m_bReadOnly ) + { + g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); + } + return false; + } + + // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in + // meta data function + if ( m_pfnMetaChecksum ) + { + unsigned int cache_meta_checksum = *( unsigned int *)&header[ 8 ]; + if ( cache_meta_checksum != m_uCurrentMetaChecksum ) + { + DevMsg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); + Assert( !m_bReadOnly ); + if ( !m_bReadOnly ) + { + g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); + } + return false; + } + } + + // Looks valid + return true; +} + +template +void CUtlCachedFileData::InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile ) +{ + deleteFile = false; + + CUtlBuffer loadBuf; + g_pFullFileSystem->ReadToBuffer( fh, loadBuf ); + g_pFullFileSystem->Close( fh ); + + int cacheversion = 0; + loadBuf.Get( &cacheversion, sizeof( cacheversion ) ); + + if ( UTL_CACHE_SYSTEM_VERSION == cacheversion ) + { + // Now parse data from the buffer + int version = loadBuf.GetInt(); + + if ( version == m_nVersion ) + { + // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in + // meta data function + unsigned int cache_meta_checksum = loadBuf.GetInt(); + + if ( !m_pfnMetaChecksum || + ( cache_meta_checksum == m_uCurrentMetaChecksum ) ) + { + int count = loadBuf.GetInt(); + + Assert( count < 2000000 ); + + CUtlBuffer buf( 0, 0, 0 ); + + for ( int i = 0 ; i < count; ++i ) + { + int bufsize = loadBuf.GetInt(); + Assert( bufsize < 1000000 ); + + buf.Clear(); + buf.EnsureCapacity( bufsize ); + + loadBuf.Get( buf.Base(), bufsize ); + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, bufsize ); + + // Read the element name + char elementFileName[ 512 ]; + buf.GetString( elementFileName, sizeof( elementFileName ) ); + + // Now read the element + int slot = GetIndex( elementFileName ); + + Assert( slot != m_Elements.InvalidIndex() ); + + ElementType_t& element = m_Elements[ slot ]; + + element.fileinfo = buf.GetInt(); + if ( ( element.fileinfo == -1 ) && + ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) + { + element.fileinfo = 0; + } + + Assert( element.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ element.dataIndex ]; + + Assert( data ); + + ((IBaseCacheInfo *)data)->Restore( buf ); + } + } + else + { + Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } + } + else + { + Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } + } + else + { + DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } +} + +template +void CUtlCachedFileData::InitLargeBuffer( FileHandle_t& fh, bool& deleteFile ) +{ + deleteFile = false; + + int cacheversion = 0; + g_pFullFileSystem->Read( &cacheversion, sizeof( cacheversion ), fh ); + + if ( UTL_CACHE_SYSTEM_VERSION == cacheversion ) + { + // Now parse data from the buffer + int version = 0; + g_pFullFileSystem->Read( &version, sizeof( version ), fh ); + + if ( version == m_nVersion ) + { + // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in + // meta data function + unsigned int cache_meta_checksum = 0; + + g_pFullFileSystem->Read( &cache_meta_checksum, sizeof( cache_meta_checksum ), fh ); + + if ( !m_pfnMetaChecksum || + ( cache_meta_checksum == m_uCurrentMetaChecksum ) ) + { + int count = 0; + + g_pFullFileSystem->Read( &count, sizeof( count ), fh ); + + Assert( count < 2000000 ); + + CUtlBuffer buf( 0, 0, 0 ); + + for ( int i = 0 ; i < count; ++i ) + { + int bufsize = 0; + g_pFullFileSystem->Read( &bufsize, sizeof( bufsize ), fh ); + + Assert( bufsize < 1000000 ); + + buf.Clear(); + buf.EnsureCapacity( bufsize ); + + int nBytesRead = g_pFullFileSystem->Read( buf.Base(), bufsize, fh ); + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead ); + + // Read the element name + char elementFileName[ 512 ]; + buf.GetString( elementFileName, sizeof( elementFileName ) ); + + // Now read the element + int slot = GetIndex( elementFileName ); + + Assert( slot != m_Elements.InvalidIndex() ); + + ElementType_t& element = m_Elements[ slot ]; + + element.fileinfo = buf.GetInt(); + if ( ( element.fileinfo == -1 ) && + ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) + { + element.fileinfo = 0; + } + + Assert( element.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ element.dataIndex ]; + + Assert( data ); + + ((IBaseCacheInfo *)data)->Restore( buf ); + } + } + else + { + Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } + } + else + { + Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } + } + else + { + DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); + deleteFile = true; + } + + g_pFullFileSystem->Close( fh ); +} + +template +bool CUtlCachedFileData::Init() +{ + if ( m_bInitialized ) + { + return true; + } + + m_bInitialized = true; + + if ( !m_sRepositoryFileName.Length() ) + { + Error( "CUtlCachedFileData: Can't Init, no repository file specified." ); + return false; + } + + // Always compute meta checksum + m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0; + + FileHandle_t fh; + + fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" ); + if ( fh == FILESYSTEM_INVALID_HANDLE ) + { + // Nothing on disk, we'll recreate everything from scratch... + if ( !m_bReadOnly ) + { + SetDirty( true ); + } + return true; + } + long fileTime = g_pFullFileSystem->GetFileTime( m_sRepositoryFileName.String(), "MOD" ); + int size = g_pFullFileSystem->Size( fh ); + + bool deletefile = false; + + if ( size > 1024 * 1024 ) + { + InitLargeBuffer( fh, deletefile ); + } + else + { + InitSmallBuffer( fh, size, deletefile ); + } + + if ( deletefile ) + { + Assert( !m_bReadOnly ); + if ( !m_bReadOnly ) + { + g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); + SetDirty( true ); + } + } + CheckDiskInfo( false, fileTime ); + return true; +} + +template +void CUtlCachedFileData::Save() +{ + char path[ 512 ]; + Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) ); + Q_StripFilename( path ); + + g_pFullFileSystem->CreateDirHierarchy( path, "MOD" ); + + if ( g_pFullFileSystem->FileExists( m_sRepositoryFileName.String(), "MOD" ) && + !g_pFullFileSystem->IsFileWritable( m_sRepositoryFileName.String(), "MOD" ) ) + { + g_pFullFileSystem->SetFileWritable( m_sRepositoryFileName.String(), true, "MOD" ); + } + + // Now write to file + FileHandle_t fh; + fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "wb" ); + if ( FILESYSTEM_INVALID_HANDLE == fh ) + { + Warning( "Unable to persist cache '%s', check file permissions\n", m_sRepositoryFileName.String() ); + } + else + { + SetDirty( false ); + + int v = UTL_CACHE_SYSTEM_VERSION; + g_pFullFileSystem->Write( &v, sizeof( v ), fh ); + v = m_nVersion; + g_pFullFileSystem->Write( &v, sizeof( v ), fh ); + v = (int)m_uCurrentMetaChecksum; + g_pFullFileSystem->Write( &v, sizeof( v ), fh ); + + // Element count + int c = Count(); + + g_pFullFileSystem->Write( &c, sizeof( c ), fh ); + + // Save repository back out to disk... + CUtlBuffer buf( 0, 0, 0 ); + + // Sort file alphabetically + CUtlSortVector list; + for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) + { + ElementType_t& element = m_Elements[ i ]; + CSortedCacheFile insert; + insert.handle = element.handle; + insert.index = i; + list.InsertNoSort( insert ); + } + list.RedoSort(); + + for ( int i = 0; i < list.Count(); ++i ) + { + ElementType_t &element = m_Elements[ list[ i ].index ]; + + buf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); + + char fn[ 512 ]; + g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); + buf.PutString( fn ); + buf.PutInt( element.fileinfo ); + + Assert( element.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ element.dataIndex ]; + + Assert( data ); + + ((IBaseCacheInfo *)data)->Save( buf ); + + int bufsize = buf.TellPut(); + g_pFullFileSystem->Write( &bufsize, sizeof( bufsize ), fh ); + g_pFullFileSystem->Write( buf.Base(), bufsize, fh ); + } + + g_pFullFileSystem->Close( fh ); + } + + if ( m_bSaveManifest ) + { + SaveManifest(); + } +} + +template +void CUtlCachedFileData::Shutdown() +{ + if ( !m_bInitialized ) + return; + + m_bInitialized = false; + + if ( IsDirty() ) + { + Save(); + } + // No matter what, create the manifest if it doesn't exist on the HD yet + else if ( m_bSaveManifest && !ManifestExists() ) + { + SaveManifest(); + } + + m_Elements.RemoveAll(); +} + +template +bool CUtlCachedFileData::ManifestExists() +{ + char manifest_name[ 512 ]; + Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) ); + Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) ); + return g_pFullFileSystem->FileExists( manifest_name, "MOD" ); +} + +template +void CUtlCachedFileData::SaveManifest() +{ + // Save manifest out to disk... + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + // Sort file alphabetically + CUtlSortVector list; + for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) + { + ElementType_t& element = m_Elements[ i ]; + CSortedCacheFile insert; + insert.handle = element.handle; + insert.index = i; + list.InsertNoSort( insert ); + } + list.RedoSort(); + + for ( int i = 0; i < list.Count(); ++i ) + { + ElementType_t &element = m_Elements[ list[ i ].index ]; + + char fn[ 512 ]; + g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); + + buf.Printf( "\"%s\"\r\n", fn ); + } + + char path[ 512 ]; + Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) ); + Q_StripFilename( path ); + + g_pFullFileSystem->CreateDirHierarchy( path, "MOD" ); + + char manifest_name[ 512 ]; + Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) ); + + Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) ); + + if ( g_pFullFileSystem->FileExists( manifest_name, "MOD" ) && + !g_pFullFileSystem->IsFileWritable( manifest_name, "MOD" ) ) + { + g_pFullFileSystem->SetFileWritable( manifest_name, true, "MOD" ); + } + + // Now write to file + FileHandle_t fh; + fh = g_pFullFileSystem->Open( manifest_name, "wb" ); + if ( FILESYSTEM_INVALID_HANDLE != fh ) + { + g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), fh ); + g_pFullFileSystem->Close( fh ); + + // DevMsg( "Persisting cache manifest '%s' (%d entries)\n", manifest_name, c ); + } + else + { + Warning( "Unable to persist cache manifest '%s', check file permissions\n", manifest_name ); + } +} + +template +void CUtlCachedFileData::RecheckItem( char const *filename ) +{ + int idx = GetIndex( filename ); + ElementType_t& e = m_Elements[ idx ]; + + e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; + + long cachefileinfo = e.fileinfo; + // Set the disk fileinfo the first time we encounter the filename + if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) + { + if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) + { + e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); + // Missing files get a disk file size of 0 + if ( e.diskfileinfo == -1 ) + { + e.diskfileinfo = 0; + } + } + else + { + e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); + } + } + + Assert( e.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ e.dataIndex ]; + + Assert( data ); + + // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct... + if ( cachefileinfo != e.diskfileinfo ) + { + if ( !m_bReadOnly ) + { + RebuildCache( filename, data ); + } + } + e.fileinfo = e.diskfileinfo; +} + +template +T *CUtlCachedFileData::RebuildItem( const char *filename ) +{ + int idx = GetIndex( filename ); + ElementType_t& e = m_Elements[ idx ]; + + e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; + + long cachefileinfo = e.fileinfo; + // Set the disk fileinfo the first time we encounter the filename + if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) + { + if ( m_bNeverCheckDisk ) + { + e.diskfileinfo = cachefileinfo; + } + else + { + if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) + { + e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); + // Missing files get a disk file size of 0 + if ( e.diskfileinfo == -1 ) + { + e.diskfileinfo = 0; + } + } + else + { + e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); + } + } + } + + Assert( e.dataIndex != m_Data.InvalidIndex() ); + + T *data = m_Data[ e.dataIndex ]; + + Assert( data ); + + // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct... + if ( !m_bReadOnly ) + { + RebuildCache( filename, data ); + } + e.fileinfo = e.diskfileinfo; + + return data; +} + +template +void CUtlCachedFileData::RebuildCache( char const *filename, T *data ) +{ + Assert( !m_bReadOnly ); + + // Recache item, mark self as dirty + SetDirty( true ); + + ((IBaseCacheInfo *)data)->Rebuild( filename ); +} + +template +void CUtlCachedFileData::ForceRecheckDiskInfo() +{ + for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) + { + ElementType_t& element = m_Elements[ i ]; + element.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; + } +} + +class CSortedCacheFile +{ +public: + FileNameHandle_t handle; + int index; + + bool Less( const CSortedCacheFile &file0, const CSortedCacheFile &file1, void * ) + { + char name0[ 512 ]; + char name1[ 512 ]; + g_pFullFileSystem->String( file0.handle, name0, sizeof( name0 ) ); + g_pFullFileSystem->String( file1.handle, name1, sizeof( name1 ) ); + return Q_stricmp( name0, name1 ) < 0 ? true : false; + } +}; + +// Iterates all entries and causes rebuild on any existing items which are out of date +template +void CUtlCachedFileData::CheckDiskInfo( bool forcerebuild, long cacheFileTime ) +{ + char fn[ 512 ]; + int i; + if ( forcerebuild ) + { + for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) + { + ElementType_t& element = m_Elements[ i ]; + g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); + Get(fn); + } + return; + } + + CUtlSortVector list; + for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) + { + ElementType_t& element = m_Elements[ i ]; + CSortedCacheFile insert; + insert.handle = element.handle; + insert.index = i; + list.InsertNoSort( insert ); + if ( m_bNeverCheckDisk && + element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) + { + element.diskfileinfo = element.fileinfo; + } + } + + if ( !list.Count() || m_bNeverCheckDisk ) + return; + + // Actually sorting by filename here doesn't appear to be a win since FileNameHandle_t is basically sorted by Path anyway (path is stored in the high part of the DWORD) + // list.RedoSort(); + + bool bSteam = g_pFullFileSystem->IsSteam(); + + for ( int listStart = 0, listEnd = 0; listStart < list.Count(); listStart = listEnd+1 ) + { + int pathIndex = g_pFullFileSystem->GetPathIndex( m_Elements[list[listStart].index].handle ); + for ( listEnd = listStart; listEnd < list.Count(); listEnd++ ) + { + ElementType_t& element = m_Elements[ list[listEnd].index ]; + + int pathTest = g_pFullFileSystem->GetPathIndex( element.handle ); + if ( pathTest != pathIndex ) + break; + } + g_pFullFileSystem->String( m_Elements[list[listStart].index].handle, fn, sizeof( fn ) ); + Q_StripFilename( fn ); + + bool bCheck = true; + + if ( !bSteam ) + { + long pathTime = g_pFullFileSystem->GetPathTime( fn, "GAME" ); + bCheck = (pathTime > cacheFileTime) ? true : false; + } + + for ( i = listStart; i < listEnd; i++ ) + { + ElementType_t& element = m_Elements[ list[i].index ]; + + if ( element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) + { + if ( !bCheck ) + { + element.diskfileinfo = element.fileinfo; + } + else + { + g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); + if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) + { + element.diskfileinfo = g_pFullFileSystem->Size( fn, "GAME" ); + + // Missing files get a disk file size of 0 + if ( element.diskfileinfo == -1 ) + { + element.diskfileinfo = 0; + } + } + else + { + element.diskfileinfo = g_pFullFileSystem->GetFileTime( fn, "GAME" ); + } + } + } + } + } +} + +template +void CUtlCachedFileData::SetNeverCheckDisk( bool bNeverCheckDisk ) +{ + m_bNeverCheckDisk = bNeverCheckDisk; +} + +#include "tier0/memdbgoff.h" + +#endif // UTLCACHEDFILEDATA_H diff --git a/public/VGuiMatSurface/IMatSystemSurface.h b/public/VGuiMatSurface/IMatSystemSurface.h new file mode 100644 index 0000000..59d6545 --- /dev/null +++ b/public/VGuiMatSurface/IMatSystemSurface.h @@ -0,0 +1,113 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: An extra interface implemented by the material system +// implementation of vgui::ISurface +// +// $Revision: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATSYSTEMSURFACE_H +#define IMATSYSTEMSURFACE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "vgui/ISurface.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class VMatrix; +class IMaterial; +struct InputEvent_t; +FORWARD_DECLARE_HANDLE( InputContextHandle_t ); + + +//----------------------------------------------------------------------------- +// Callbacks for mouse getting + setting +//----------------------------------------------------------------------------- +typedef void (*GetMouseCallback_t)(int &x, int &y); +typedef void (*SetMouseCallback_t)(int x, int y); + +//----------------------------------------------------------------------------- +// Callbacks for sound playing +//----------------------------------------------------------------------------- +typedef void (*PlaySoundFunc_t)(const char *pFileName); + + +//----------------------------------------------------------------------------- +// +// An extra interface implemented by the material system implementation of vgui::ISurface +// +//----------------------------------------------------------------------------- +class IMatSystemSurface : public vgui::ISurface +{ +public: + // If the app drives the input (like the engine needs to do for VCR mode), + // it can call this, setting bLetAppDriveInput to true and call + // HandleInputEvent for the input events. + virtual void SetAppDrivesInput( bool bLetAppDriveInput ) = 0; + + // Tells the surface to ignore windows messages + virtual void EnableWindowsMessages( bool bEnable ) = 0; + + // Starts, ends 3D painting + // NOTE: These methods should only be called from within the paint() + // method of a panel. + virtual void Begin3DPaint( int iLeft, int iTop, int iRight, int iBottom ) = 0; + virtual void End3DPaint() = 0; + + // NOTE: This also should only be called from within the paint() + // method of a panel. Use it to disable clipping for the rendering + // of this panel. + virtual void DisableClipping( bool bDisable ) = 0; + + // Prevents vgui from changing the cursor + virtual bool IsCursorLocked() const = 0; + + // Sets the mouse get + set callbacks + virtual void SetMouseCallbacks( GetMouseCallback_t getFunc, SetMouseCallback_t setFunc ) = 0; + + // Installs a function to play sounds + virtual void InstallPlaySoundFunc( PlaySoundFunc_t soundFunc ) = 0; + + // Some drawing methods that cannot be accomplished under Win32 + virtual void DrawColoredCircle( int centerx, int centery, float radius, int r, int g, int b, int a ) = 0; + virtual void DrawColoredText( vgui::HFont font, int x, int y, int r, int g, int b, int a, char *fmt, ... ) = 0; + + // Draws text with current font at position and wordwrapped to the rect using color values specified + virtual void DrawColoredTextRect( vgui::HFont font, int x, int y, int w, int h, int r, int g, int b, int a, char *fmt, ... ) = 0; + virtual void DrawTextHeight( vgui::HFont font, int w, int& h, char *fmt, ... ) = 0; + + // Returns the length of the text string in pixels + virtual int DrawTextLen( vgui::HFont font, char *fmt, ... ) = 0; + + // Draws a panel in 3D space. Assumes view + projection are already set up + // Also assumes the (x,y) coordinates of the panels are defined in 640xN coords + // (N isn't necessary 480 because the panel may not be 4x3) + // The width + height specified are the size of the panel in world coordinates + virtual void DrawPanelIn3DSpace( vgui::VPANEL pRootPanel, const VMatrix &panelCenterToWorld, int nPixelWidth, int nPixelHeight, float flWorldWidth, float flWorldHeight ) = 0; + + // Binds a material to a surface texture ID + virtual void DrawSetTextureMaterial( int id, IMaterial *pMaterial ) = 0; + + // Handles an input event, returns true if the event should be filtered from the rest of the game + virtual bool HandleInputEvent( const InputEvent_t &event ) = 0; + + virtual void Set3DPaintTempRenderTarget( const char *pRenderTargetName ) = 0; + virtual void Reset3DPaintTempRenderTarget( void ) = 0; + + // Gets a material bound to a surface texture ID + virtual IMaterial *DrawGetTextureMaterial( int id ) = 0; + + // Sets the VGui input context + virtual void SetInputContext( InputContextHandle_t hContext ) = 0; +}; + + +#endif // IMATSYSTEMSURFACE_H + diff --git a/public/VGuiMatSurface/IMatSystemSurfaceV5.h b/public/VGuiMatSurface/IMatSystemSurfaceV5.h new file mode 100644 index 0000000..9b8da47 --- /dev/null +++ b/public/VGuiMatSurface/IMatSystemSurfaceV5.h @@ -0,0 +1,88 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef IMATSYSTEMSURFACEV5_H +#define IMATSYSTEMSURFACEV5_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "vgui/isurfacev30.h" + + +namespace MatSystemSurfaceV5 +{ + #define MAT_SYSTEM_SURFACE_INTERFACE_VERSION_5 "MatSystemSurface005" + + + class IMatSystemSurface : public SurfaceV30::ISurface + { + public: + // Hook needed to get input to work. + // If the app drives the input (like the engine needs to do for VCR mode), + // it can set bLetAppDriveInput to true and call HandleWindowMessage for the Windows messages. + virtual void AttachToWindow( void *hwnd, bool bLetAppDriveInput=false ) = 0; + + // If you specified true for bLetAppDriveInput, then call this for each window message that comes in. + virtual void HandleWindowMessage( void *hwnd, unsigned int uMsg, unsigned int wParam, long lParam ) = 0; + + // Tells the surface to ignore windows messages + virtual void EnableWindowsMessages( bool bEnable ) = 0; + + // Starts, ends 3D painting + // NOTE: These methods should only be called from within the paint() + // method of a panel. + virtual void Begin3DPaint( int iLeft, int iTop, int iRight, int iBottom ) = 0; + virtual void End3DPaint() = 0; + + // NOTE: This also should only be called from within the paint() + // method of a panel. Use it to disable clipping for the rendering + // of this panel. + virtual void DisableClipping( bool bDisable ) = 0; + + // Prevents vgui from changing the cursor + virtual bool IsCursorLocked() const = 0; + + // Sets the mouse get + set callbacks + virtual void SetMouseCallbacks( GetMouseCallback_t getFunc, SetMouseCallback_t setFunc ) = 0; + + // Installs a function to play sounds + virtual void InstallPlaySoundFunc( PlaySoundFunc_t soundFunc ) = 0; + + // Some drawing methods that cannot be accomplished under Win32 + virtual void DrawColoredCircle( int centerx, int centery, float radius, int r, int g, int b, int a ) = 0; + virtual int DrawColoredText( vgui::HFont font, int x, int y, int r, int g, int b, int a, char *fmt, ... ) = 0; + + // Draws text with current font at position and wordwrapped to the rect using color values specified + virtual void DrawColoredTextRect( vgui::HFont font, int x, int y, int w, int h, int r, int g, int b, int a, char *fmt, ... ) = 0; + virtual void DrawTextHeight( vgui::HFont font, int w, int& h, char *fmt, ... ) = 0; + + // Returns the length of the text string in pixels + virtual int DrawTextLen( vgui::HFont font, char *fmt, ... ) = 0; + + // Draws a panel in 3D space. Assumes view + projection are already set up + // Also assumes the (x,y) coordinates of the panels are defined in 640xN coords + // (N isn't necessary 480 because the panel may not be 4x3) + // The width + height specified are the size of the panel in world coordinates + virtual void DrawPanelIn3DSpace( vgui::VPANEL pRootPanel, const VMatrix &panelCenterToWorld, int nPixelWidth, int nPixelHeight, float flWorldWidth, float flWorldHeight ) = 0; + + // Binds a material to a surface texture ID + virtual void DrawSetTextureMaterial( int id, IMaterial *pMaterial ) = 0; + }; + +} + +//----------------------------------------------------------------------------- +// FIXME: This works around using scoped interfaces w/ EXPOSE_SINGLE_INTERFACE +//----------------------------------------------------------------------------- +class IMatSystemSurfaceV5 : public MatSystemSurfaceV5::IMatSystemSurface +{ +public: +}; + +#endif // IMATSYSTEMSURFACEV5_H diff --git a/public/XUnzip.cpp b/public/XUnzip.cpp new file mode 100644 index 0000000..3fb6cec --- /dev/null +++ b/public/XUnzip.cpp @@ -0,0 +1,4494 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// XUnzip.cpp Version 1.1 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use 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. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#if defined( PROTECTED_THINGS_ENABLE ) +#undef PROTECTED_THINGS_ENABLE // from protected_things.h +#endif +#include "tier0/platform.h" +#ifdef IS_WINDOWS_PC +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#elif defined(POSIX) +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include "zip/XUnzip.h" + +#if defined(POSIX) +#define _tcslen strlen +#define _tcscpy strcpy +#define _tcscat strcat +#define _tcsstr strstr +#if !defined( _T ) +#define _T( arg ) arg +#endif +#define INVALID_HANDLE_VALUE (void*)-1 +#define CloseHandle( arg ) close( (int) arg ) +#define ZeroMemory( ptr, size ) memset( ptr, 0, size ) +#define FILE_CURRENT SEEK_CUR +#define FILE_BEGIN SEEK_SET +#define FILE_END SEEK_END +#define CreateDirectory( dir, ign ) mkdir( dir, S_IRWXU | S_IRWXG | S_IRWXO ) +#define SetFilePointer( handle, pos, ign, dir ) lseek( (int) handle, pos, dir ) +bool ReadFile( void *handle, void *outbuf, unsigned int toread, unsigned int *nread, void *ignored ) +{ + *nread = read( (int) handle, outbuf, toread ); + return *nread == toread; +} +bool WriteFile( void *handle, void *buf, unsigned int towrite, unsigned int *written, void *ignored ) +{ + *written = write( (int) handle, buf, towrite ); + return *written == towrite; +} + +#define FILE_ATTRIBUTE_NORMAL S_IFREG +#define FILE_ATTRIBUTE_DIRECTORY S_IFDIR +#define FILE_ATTRIBUTE_ARCHIVE 0 +#define FILE_ATTRIBUTE_HIDDEN 0 +#define FILE_ATTRIBUTE_READONLY 0 +#define FILE_ATTRIBUTE_SYSTEM 0 +typedef unsigned char BYTE; +#endif + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// 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. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +#pragma warning(disable : 4702) // unreachable code + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + bool res; +#ifdef _WIN32 + res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS) == TRUE; +#else + h = (void*) dup( (int)hf ); + res = (int) dup >= 0; +#endif + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { +#ifdef _WIN32 + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#else + h = (void*) open( (const TCHAR *)z, O_RDONLY ); +#endif + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } +#ifdef _WIN32 + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); +#else + struct stat buf; + fstat( (int)h, &buf ); + canseek = buf.st_mode & S_IFREG; +#endif + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + { + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ if (fin==NULL) return NULL; + if (unz_copyright[0]!=' ') {lufclose(fin); return NULL;} + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + return err; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +#ifdef _WIN32 +FILETIME timet2filetime(const time_t timer) +{ struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; +#ifdef _WIN32 + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } +#endif + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + return uf ? ZR_OK : ZR_CORRUPT; +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; +#ifdef _WIN32 + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; +#else + ze->atime = 0; + ze->ctime = 0; + ze->mtime = 0; +#endif + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // +#ifdef _WIN32 + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; +#else + ze->atime=ufi.dosDate; ze->ctime=ufi.dosDate; ze->mtime=ufi.dosDate; +#endif + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); +#else + ze->mtime = mtime; +#endif + } + if (hasatime) + { time_t atime = *(time_t*)(extra+epos); epos+=4; +#ifdef _WIN32 + ze->atime = timet2filetime(atime); +#else + ze->atime = atime; +#endif + } + if (hasctime) + { time_t ctime = *(time_t*)(extra+epos); +#ifdef _WIN32 + ze->ctime = timet2filetime(ctime); +#else + ze->ctime = ctime; +#endif + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + _tcscat(cd,dir); + for ( unsigned int iCD = 0; iCD < _tcslen( cd ); iCD++ ) + { + if ( cd[ iCD ] == _T( '/' ) || cd[ iCD ] == _T( '\\' ) ) + { + cd[ iCD ] = 0; + CreateDirectory(cd,NULL); + cd[ iCD ] = _T( '\\' ); + } + } + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + +bool SafeUnzipMemory( const void *pvZipped, int cubZipped, void *pvDest, int cubDest /* should be the exact expected unzipped size */ ) +{ + // unzip + HZIP hZip = OpenZip( (void *)pvZipped, cubZipped, ZIP_MEMORY ); + + // UnzipItem is returning ZR_MORE no matter what size buffer is passed in, we know the real size so just accept + int iRes = ZR_CORRUPT; + if ( hZip ) + { + try + { + iRes = UnzipItem( hZip, 0, pvDest, cubDest, ZIP_MEMORY ); + } + catch ( ... ) + { + // failed to unzip, try to continue + iRes = ZR_CORRUPT; + } + CloseZip( hZip ); + } + + // check for failure + if ( ZR_OK != iRes && ZR_MORE != iRes ) + return false; + + return true; +} + diff --git a/public/XZip.cpp b/public/XZip.cpp new file mode 100644 index 0000000..8144a94 --- /dev/null +++ b/public/XZip.cpp @@ -0,0 +1,3013 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// XZip.cpp Version 1.1 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use 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. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#if defined( PROTECTED_THINGS_ENABLE ) +#undef PROTECTED_THINGS_ENABLE // from protected_things.h +#endif +#include "tier0/platform.h" +#ifdef IS_WINDOWS_PC +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#elif !defined(_X360) +#define far +#define near +#define INVALID_HANDLE_VALUE (void*)-1 +#define _tzset tzset +#endif + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#include +#include "zip/XZip.h" + +#ifdef POSIX +#include +#define _stricmp strcasecmp +#endif + +#ifdef OSX +#define MAP_ANONYMOUS MAP_ANON +#endif + +#ifdef XZIP_NOT_THREAD_SAFE +static ZRESULT lasterrorZ=ZR_OK; +#else +#include "tier0/threadtools.h" +static GenericThreadLocals::CThreadLocalInt lasterrorZ; +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +typedef unsigned char uch; // unsigned 8-bit value +typedef unsigned short ush; // unsigned 16-bit value +typedef unsigned long ulg; // unsigned 32-bit value +typedef size_t extent; // file size +typedef unsigned Pos; // must be at least 32 bits +typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing + +#ifndef EOF +#define EOF (-1) +#endif + + +// Error return values. The values 0..4 and 12..18 follow the conventions +// of PKZIP. The values 4..10 are all assigned to "insufficient memory" +// by PKZIP, so the codes 5..10 are used here for other purposes. +#define ZE_MISS -1 // used by procname(), zipbare() +#define ZE_OK 0 // success +#define ZE_EOF 2 // unexpected end of zip file +#define ZE_FORM 3 // zip file structure error +#define ZE_MEM 4 // out of memory +#define ZE_LOGIC 5 // internal logic error +#define ZE_BIG 6 // entry too large to split +#define ZE_NOTE 7 // invalid comment format +#define ZE_TEST 8 // zip test (-T) failed or out of memory +#define ZE_ABORT 9 // user interrupt or termination +#define ZE_TEMP 10 // error using a temp file +#define ZE_READ 11 // read or seek error +#define ZE_NONE 12 // nothing to do +#define ZE_NAME 13 // missing or empty zip file +#define ZE_WRITE 14 // error writing to a file +#define ZE_CREAT 15 // couldn't open to write +#define ZE_PARMS 16 // bad command line +#define ZE_OPEN 18 // could not open a specified file to read +#define ZE_MAXERR 18 // the highest error number + + +// internal file attribute +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#define BEST -1 // Use best method (deflation or store) +#define STORE 0 // Store method +#define DEFLATE 8 // Deflation method + +#define CRCVAL_INITIAL 0L + +// MSDOS file or directory attributes +#define MSDOS_HIDDEN_ATTR 0x02 +#define MSDOS_DIR_ATTR 0x10 + +// Lengths of headers after signatures in bytes +#define LOCHEAD 26 +#define CENHEAD 42 +#define ENDHEAD 18 + +// Definitions for extra field handling: +#define EB_HEADSIZE 4 /* length of a extra field block header */ +#define EB_LEN 2 /* offset of data length field in header */ +#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */ +#define EB_UT_FLAGS 0 /* byte offset of Flags field */ +#define EB_UT_TIME1 1 /* byte offset of 1st time value */ +#define EB_UT_FL_MTIME (1 << 0) /* mtime present */ +#define EB_UT_FL_ATIME (1 << 1) /* atime present */ +#define EB_UT_FL_CTIME (1 << 2) /* ctime present */ +#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n)) +#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3)) +#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1)) + + +// Macros for writing machine integers to little-endian format +#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);} +#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} + + +// -- Structure of a ZIP file -- +// Signatures for zip file information headers +#define LOCSIG 0x04034b50L +#define CENSIG 0x02014b50L +#define ENDSIG 0x06054b50L +#define EXTLOCSIG 0x08074b50L + + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + + +#define WSIZE (0x8000) +// Maximum window size = 32K. If you are really short of memory, compile +// with a smaller WSIZE but this reduces the compression ratio for files +// of size > WSIZE. WSIZE must be a power of two in the current implementation. +// + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +// Minimum amount of lookahead, except at the end of the input file. +// See deflate.c for comments about the MIN_MATCH+1. +// + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +// In order to simplify the code, particularly on 16 bit machines, match +// distances are limited to MAX_DIST instead of WSIZE. +// + + + + + +// =========================================================================== +// Constants +// + +#define MAX_BITS 15 +// All codes must not exceed MAX_BITS bits + +#define MAX_BL_BITS 7 +// Bit length codes must not exceed MAX_BL_BITS bits + +#define LENGTH_CODES 29 +// number of length codes, not counting the special END_BLOCK code + +#define LITERALS 256 +// number of literal bytes 0..255 + +#define END_BLOCK 256 +// end of block literal code + +#define L_CODES (LITERALS+1+LENGTH_CODES) +// number of Literal or Length codes, including the END_BLOCK code + +#define D_CODES 30 +// number of distance codes + +#define BL_CODES 19 +// number of codes used to transfer the bit lengths + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define LIT_BUFSIZE 0x8000 +#define DIST_BUFSIZE LIT_BUFSIZE +// Sizes of match buffers for literals/lengths and distances. There are +// 4 reasons for limiting LIT_BUFSIZE to 64K: +// - frequencies can be kept in 16 bit counters +// - if compression is not successful for the first block, all input data is +// still in the window so we can still emit a stored block even when input +// comes from standard input. (This can also be done for all blocks if +// LIT_BUFSIZE is not greater than 32K.) +// - if compression is not successful for a file smaller than 64K, we can +// even emit a stored file instead of a stored block (saving 5 bytes). +// - creating new Huffman trees less frequently may not provide fast +// adaptation to changes in the input data statistics. (Take for +// example a binary file with poorly compressible code followed by +// a highly compressible string table.) Smaller buffer sizes give +// fast adaptation but have of course the overhead of transmitting trees +// more frequently. +// - I can't count above 4 +// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save +// memory at the expense of compression). Some optimizations would be possible +// if we rely on DIST_BUFSIZE == LIT_BUFSIZE. +// + +#define REP_3_6 16 +// repeat previous bit length 3-6 times (2 bits of repeat count) + +#define REPZ_3_10 17 +// repeat a zero length 3-10 times (3 bits of repeat count) + +#define REPZ_11_138 18 +// repeat a zero length 11-138 times (7 bits of repeat count) + +#define HEAP_SIZE (2*L_CODES+1) +// maximum heap size + + +// =========================================================================== +// Local data used by the "bit string" routines. +// + +#define Buf_size (8 * 2*sizeof(char)) +// Number of bits used within bi_buf. (bi_buf may be implemented on +// more than 16 bits on some systems.) + +// Output a 16 bit value to the bit stream, lower (oldest) byte first +#define PUTSHORT(state,w) \ +{ if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + /* flush may fail, so only write into the buffer if there's actually room (same below) */ \ + if (state.bs.out_offset < state.bs.out_size-1) { \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ + } \ +} + +#define PUTBYTE(state,b) \ +{ if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + if (state.bs.out_offset < state.bs.out_size) \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} + +// DEFLATE.CPP HEADER + +#define HASH_BITS 15 +// For portability to 16 bit machines, do not use values above 15. + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + +#define max_insert_length max_lazy_match +// Insert new strings in the hash table only if the match length +// is not greater than this length. This saves time but degrades compression. +// max_insert_length is used only for compression levels <= 3. + + + +const int extra_lbits[LENGTH_CODES] // extra bits for each length code + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +const int extra_dbits[D_CODES] // extra bits for each distance code + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +const int extra_blbits[BL_CODES]// extra bits for each bit length code + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +// The lengths of the bit length codes are sent in order of decreasing +// probability, to avoid transmitting the lengths for unused bit length codes. + + +typedef struct config { + ush good_length; // reduce lazy search above this match length + ush max_lazy; // do not perform lazy search above this match length + ush nice_length; // quit search above this match length + ush max_chain; +} config; + +// Values for max_lazy_match, good_match, nice_match and max_chain_length, +// depending on the desired pack level (0..9). The values given below have +// been tuned to exclude worst case performance for pathological files. +// Better values may be found for specific files. +// + +const config configuration_table[10] = { +// good lazy nice chain + {0, 0, 0, 0}, // 0 store only + {4, 4, 8, 4}, // 1 maximum speed, no lazy matches + {4, 5, 16, 8}, // 2 + {4, 6, 32, 32}, // 3 + {4, 4, 16, 16}, // 4 lazy matches */ + {8, 16, 32, 32}, // 5 + {8, 16, 128, 128}, // 6 + {8, 32, 128, 256}, // 7 + {32, 128, 258, 1024}, // 8 + {32, 258, 258, 4096}};// 9 maximum compression */ + +// Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 +// For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning. + + + + + +// Data structure describing a single value and its code string. +typedef struct ct_data { + union { + ush freq; // frequency count + ush code; // bit string + } fc; + union { + ush dad; // father node in Huffman tree + ush len; // length of bit string + } dl; +} ct_data; + +typedef struct tree_desc { + ct_data *dyn_tree; // the dynamic tree + ct_data *static_tree; // corresponding static tree or NULL + const int *extra_bits; // extra bits for each code or NULL + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + int max_code; // largest code with non zero frequency +} tree_desc; + + + + +class TTreeState +{ public: + TTreeState(); + + ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree + ct_data dyn_dtree[2*D_CODES+1]; // distance tree + ct_data static_ltree[L_CODES+2]; // the static literal tree... + // ... Since the bit lengths are imposed, there is no need for the L_CODES + // extra codes used during heap construction. However the codes 286 and 287 + // are needed to build a canonical tree (see ct_init below). + ct_data static_dtree[D_CODES]; // the static distance tree... + // ... (Actually a trivial tree since all codes use 5 bits.) + ct_data bl_tree[2*BL_CODES+1]; // Huffman tree for the bit lengths + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS+1]; // number of codes at each bit length for an optimal tree + + int heap[2*L_CODES+1]; // heap used to build the Huffman trees + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + uch depth[2*L_CODES+1]; + // Depth of each subtree used as tie breaker for trees of equal frequency + + uch length_code[MAX_MATCH-MIN_MATCH+1]; + // length code for each normalized match length (0 == MIN_MATCH) + + uch dist_code[512]; + // distance codes. The first 256 values correspond to the distances + // 3 .. 258, the last 256 values correspond to the top 8 bits of + // the 15 bit distances. + + int base_length[LENGTH_CODES]; + // First normalized length for each code (0 = MIN_MATCH) + + int base_dist[D_CODES]; + // First normalized distance for each code (0 = distance of 1) + + uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths + ush far d_buf[DIST_BUFSIZE]; // buffer for distances + + uch flag_buf[(LIT_BUFSIZE/8)]; + // flag_buf is a bit array distinguishing literals from lengths in + // l_buf, and thus indicating the presence or absence of a distance. + + unsigned last_lit; // running index in l_buf + unsigned last_dist; // running index in d_buf + unsigned last_flags; // running index in flag_buf + uch flags; // current flags not yet saved in flag_buf + uch flag_bit; // current bit used in flags + // bits are filled in flags starting at bit 0 (least significant). + // Note: these flags are overkill in the current code since we don't + // take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + + ulg opt_len; // bit length of current block with optimal trees + ulg static_len; // bit length of current block with static trees + + ulg cmpr_bytelen; // total byte length of compressed file + ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen' + + ulg input_len; // total byte length of input file + // input_len is for debugging only since we can get it by other means. + + ush *file_type; // pointer to UNKNOWN, BINARY or ASCII +// int *file_method; // pointer to DEFLATE or STORE +}; + +TTreeState::TTreeState() +{ tree_desc a = {dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; l_desc = a; + tree_desc b = {dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; d_desc = b; + tree_desc c = {bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; bl_desc = c; + last_lit=0; + last_dist=0; + last_flags=0; +} + + + +class TBitState +{ public: + + int flush_flg; + // + unsigned bi_buf; + // Output buffer. bits are inserted starting at the bottom (least significant + // bits). The width of bi_buf must be at least 16 bits. + int bi_valid; + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + char *out_buf; + // Current output buffer. + unsigned out_offset; + // Current offset in output buffer. + // On 16 bit machines, the buffer is limited to 64K. + unsigned out_size; + // Size of current output buffer + ulg bits_sent; // bit length of the compressed data only needed for debugging??? +}; + + + + + + + +class TDeflateState +{ public: + TDeflateState() {window_size=0;} + + uch window[2L*WSIZE]; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least WSIZE + // bytes. With this organization, matches are limited to a distance of + // WSIZE-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would + // be less efficient since the data would have to be copied WSIZE/CBSZ times) + Pos prev[WSIZE]; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + Pos head[HASH_SIZE]; + // Heads of the hash chains or NIL. If your compiler thinks that + // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC. + + ulg window_size; + // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + // input file length plus MIN_LOOKAHEAD. + + long block_start; + // window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int sliding; + // Set to false when the input file is already in memory + + unsigned ins_h; // hash index of string to be inserted + + unsigned int prev_length; + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + + unsigned strstart; // start of string to insert + unsigned match_start; // start of matching string + int eofile; // flag set at end of input file + unsigned lookahead; // number of valid bytes ahead in window + + unsigned max_chain_length; + // To speed up deflation, hash chains are never searched beyond this length. + // A higher limit improves compression ratio but degrades the speed. + + unsigned int max_lazy_match; + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + + unsigned good_match; + // Use a faster search when the previous match is longer than this + + int nice_match; // Stop searching when current match exceeds this +}; + + +typedef struct iztimes { + time_t atime,mtime,ctime; +} iztimes; // access, modify, create times + +typedef struct zlist { + ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are + ulg tim, crc, siz, len; + extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD + ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD + ulg atx, off; + char name[MAX_PATH]; // File name in zip file + char *extra; // Extra field (set only if ext != 0) + char *cextra; // Extra in central (set only if cext != 0) + char *comment; // Comment (set only if com != 0) + char iname[MAX_PATH]; // Internal file name after cleanup + char zname[MAX_PATH]; // External version of internal name + int mark; // Marker for files to operate on + int trash; // Marker for files to delete + int dosflag; // Set to force MSDOS file attributes + struct zlist far *nxt; // Pointer to next header in list +} TZipFileInfo; + + +class TState; +typedef unsigned (*READFUNC)(TState &state, char *buf,unsigned size); +typedef unsigned (*FLUSHFUNC)(void *param, const char *buf, unsigned *size); +typedef unsigned (*WRITEFUNC)(void *param, const char *buf, unsigned size); +class TState +{ public: TState() {err=0;} + // + void *param; + int level; bool seekable; + READFUNC readfunc; FLUSHFUNC flush_outbuf; + TTreeState ts; TBitState bs; TDeflateState ds; + const char *err; +}; + + + + + + + + +#undef Assert +void AssertXZip(TState &state,bool cond, const char *msg) +{ + if (cond) return; + state.err=msg; +} +void __cdecl Trace(const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} +void __cdecl Tracec(bool ,const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} + + + +// =========================================================================== +// Local (static) routines in this file. +// + +void init_block (TState &); +void pqdownheap (TState &,ct_data *tree, int k); +void gen_bitlen (TState &,tree_desc *desc); +void gen_codes (TState &state,ct_data *tree, int max_code); +void build_tree (TState &,tree_desc *desc); +void scan_tree (TState &,ct_data *tree, int max_code); +void send_tree (TState &state,ct_data *tree, int max_code); +int build_bl_tree (TState &); +void send_all_trees (TState &state,int lcodes, int dcodes, int blcodes); +void compress_block (TState &state,ct_data *ltree, ct_data *dtree); +void set_file_type (TState &); +void send_bits (TState &state, int value, int length); +unsigned bi_reverse (unsigned code, int len); +void bi_windup (TState &state); +void copy_block (TState &state,char *buf, unsigned len, int header); + + +#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len) +// Send a code of the given tree. c and tree must not have side effects + +// alternatively... +//#define send_code(state, c, tree) +// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c)); +// send_bits(state, tree[c].fc.code, tree[c].dl.len); } + +#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)]) +// Mapping from a distance to a distance code. dist is the distance - 1 and +// must not have side effects. dist_code[256] and dist_code[257] are never used. + +#define Max(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(TState &state, ush *attr) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + state.ts.file_type = attr; + //state.ts.file_method = method; + state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L; + state.ts.input_len = 0L; + + if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + state.ts.base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + state.ts.base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + state.ts.base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + state.ts.dist_code[256 + dist++] = (uch)code; + } + } + AssertXZip(state,dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + n = 0; + while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++; + while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++; + while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + /* fc.codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(state,(ct_data *)state.ts.static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + state.ts.static_dtree[n].dl.len = 5; + state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(state); +} + +/* =========================================================================== + * Initialize a new block. + */ +void init_block(TState &state) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0; + for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0; + for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0; + + state.ts.dyn_ltree[END_BLOCK].fc.freq = 1; + state.ts.opt_len = state.ts.static_len = 0L; + state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0; + state.ts.flags = 0; state.ts.flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = state.ts.heap[SMALLEST]; \ + state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \ + pqdownheap(state,tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].fc.freq < tree[m].fc.freq || \ + (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +void pqdownheap(TState &state,ct_data *tree, int k) +{ + int v = state.ts.heap[k]; + int j = k << 1; /* left son of k */ + int htemp; /* required because of bug in SASC compiler */ + + while (j <= state.ts.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j+1], state.ts.heap[j])) j++; + + /* Exit if v is smaller than both sons */ + htemp = state.ts.heap[j]; + if (smaller(tree, v, htemp)) break; + + /* Exchange v with the smallest son */ + state.ts.heap[k] = htemp; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + state.ts.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +void gen_bitlen(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + const int *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */ + + for (h = state.ts.heap_max+1; h < HEAP_SIZE; h++) { + n = state.ts.heap[h]; + bits = tree[tree[n].dl.dad].dl.len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].dl.len = (ush)bits; + /* We overwrite tree[n].dl.dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + state.ts.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].fc.freq; + state.ts.opt_len += (ulg)f * (bits + xbits); + if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits); + } + if (overflow == 0) return; + + Trace("\nbit length overflow\n"); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (state.ts.bl_count[bits] == 0) bits--; + state.ts.bl_count[bits]--; /* move one leaf down the tree */ + state.ts.bl_count[bits+1] += (ush)2; /* move one overflow item as its brother */ + state.ts.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = state.ts.bl_count[bits]; + while (n != 0) { + m = state.ts.heap[--h]; + if (m > max_code) continue; + if (tree[m].dl.len != (ush)bits) { + Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits); + state.ts.opt_len += ((long)bits-(long)tree[m].dl.len)*(long)tree[m].fc.freq; + tree[m].dl.len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +void gen_codes (TState &state, ct_data *tree, int max_code) +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (ush)((code + state.ts.bl_count[bits-1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + AssertXZip(state,code + state.ts.bl_count[MAX_BITS]-1 == (1<< ((ush) MAX_BITS)) - 1, + "inconsistent bit counts"); + Trace("\ngen_codes: max_code %d ", max_code); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].dl.len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len); + + //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +void build_tree(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc.freq != 0) { + state.ts.heap[++state.ts.heap_len] = max_code = n; + state.ts.depth[n] = 0; + } else { + tree[n].dl.len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (state.ts.heap_len < 2) { + int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[newcp].fc.freq = 1; + state.ts.depth[newcp] = 0; + state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = state.ts.heap_len/2; n >= 1; n--) pqdownheap(state,tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */ + + state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */ + state.ts.heap[--state.ts.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq); + state.ts.depth[node] = (uch) (Max(state.ts.depth[n], state.ts.depth[m]) + 1); + tree[n].dl.dad = tree[m].dl.dad = (ush)node; + /* and insert the new node in the heap */ + state.ts.heap[SMALLEST] = node++; + pqdownheap(state,tree, SMALLEST); + + } while (state.ts.heap_len >= 2); + + state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(state,(tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes (state,(ct_data *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +void scan_tree (TState &state,ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].dl.len = (ush)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count); + } else if (curlen != 0) { + if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++; + state.ts.bl_tree[REP_3_6].fc.freq++; + } else if (count <= 10) { + state.ts.bl_tree[REPZ_3_10].fc.freq++; + } else { + state.ts.bl_tree[REPZ_11_138].fc.freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +void send_tree (TState &state, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].dl.len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(state, curlen, state.ts.bl_tree); count--; + } + AssertXZip(state,count >= 3 && count <= 6, " 3_6?"); + send_code(state,REP_3_6, state.ts.bl_tree); send_bits(state,count-3, 2); + + } else if (count <= 10) { + send_code(state,REPZ_3_10, state.ts.bl_tree); send_bits(state,count-3, 3); + + } else { + send_code(state,REPZ_11_138, state.ts.bl_tree); send_bits(state,count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +int build_bl_tree(TState &state) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(state,(ct_data *)state.ts.dyn_ltree, state.ts.l_desc.max_code); + scan_tree(state,(ct_data *)state.ts.dyn_dtree, state.ts.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(state,(tree_desc *)(&state.ts.bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + state.ts.opt_len += 3*(max_blindex+1) + 5+5+4; + Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +void send_all_trees(TState &state,int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + AssertXZip(state,lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + AssertXZip(state,lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Trace("\nbl counts: "); + send_bits(state,lcodes-257, 5); + /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */ + send_bits(state,dcodes-1, 5); + send_bits(state,blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Trace("\nbl code %2d ", bl_order[rank]); + send_bits(state,state.ts.bl_tree[bl_order[rank]].dl.len, 3); + } + Trace("\nbl tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_ltree, lcodes-1); /* send the literal tree */ + Trace("\nlit tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_dtree, dcodes-1); /* send the distance tree */ + Trace("\ndist tree: sent %ld", state.bs.bits_sent); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length (in bytes) for the file so far. + */ +ulg flush_block(TState &state,char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state); + + /* Construct the literal and distance trees */ + build_tree(state,(tree_desc *)(&state.ts.l_desc)); + Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + build_tree(state,(tree_desc *)(&state.ts.d_desc)); + Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(state); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (state.ts.opt_len+3+7)>>3; + static_lenb = (state.ts.static_len+3+7)>>3; + state.ts.input_len += stored_len; /* for debugging only */ + + Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len, + state.ts.last_lit, state.ts.last_dist); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + // Originally, zip allowed the file to be transformed from a compressed + // into a stored file in the case where compression failed, there + // was only one block, and it was allowed to change. I've removed this + // possibility since the code's cleaner if no changes are allowed. + //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L + // && state.ts.cmpr_len_bits == 0L && state.seekable) + //{ // && state.ts.file_method != NULL + // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: + // AssertXZip(state,buf!=NULL,"block vanished"); + // copy_block(state,buf, (unsigned)stored_len, 0); // without header + // state.ts.cmpr_bytelen = stored_len; + // AssertXZip(state,false,"unimplemented *state.ts.file_method = STORE;"); + // //*state.ts.file_method = STORE; + //} + //else + if (stored_len+4 <= opt_lenb && buf != (char*)NULL) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(state,(STORED_BLOCK<<1)+eof, 3); /* send block type */ + state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4; + state.ts.cmpr_len_bits = 0L; + + copy_block(state,buf, (unsigned)stored_len, 1); /* with header */ + } + else if (static_lenb == opt_lenb) { + send_bits(state,(STATIC_TREES<<1)+eof, 3); + compress_block(state,(ct_data *)state.ts.static_ltree, (ct_data *)state.ts.static_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.static_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + else { + send_bits(state,(DYN_TREES<<1)+eof, 3); + send_all_trees(state,state.ts.l_desc.max_code+1, state.ts.d_desc.max_code+1, max_blindex+1); + compress_block(state,(ct_data *)state.ts.dyn_ltree, (ct_data *)state.ts.dyn_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.opt_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + AssertXZip(state,((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size"); + init_block(state); + + if (eof) { + // AssertXZip(state,input_len == isize, "bad input size"); + bi_windup(state); + state.ts.cmpr_len_bits += 7; /* align on byte boundary */ + } + Trace("\n"); + + return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (TState &state,int dist, int lc) +{ + state.ts.l_buf[state.ts.last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + state.ts.dyn_ltree[lc].fc.freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + AssertXZip(state,(ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + state.ts.dyn_ltree[state.ts.length_code[lc]+LITERALS+1].fc.freq++; + state.ts.dyn_dtree[d_code(dist)].fc.freq++; + + state.ts.d_buf[state.ts.last_dist++] = (ush)dist; + state.ts.flags |= state.ts.flag_bit; + } + state.ts.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((state.ts.last_lit & 7) == 0) { + state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags; + state.ts.flags = 0, state.ts.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)state.ts.last_lit*8L; + ulg in_length = (ulg)state.ds.strstart-state.ds.block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + state.ts.last_lit, state.ts.last_dist, in_length, out_length, + 100L - out_length*100L/in_length); + if (state.ts.last_dist < state.ts.last_lit/2 && out_length < in_length/2) return 1; + } + return (state.ts.last_lit == LIT_BUFSIZE-1 || state.ts.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +void compress_block(TState &state,ct_data *ltree, ct_data *dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (state.ts.last_lit != 0) do { + if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++]; + lc = state.ts.l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(state,lc, ltree); /* send a literal byte */ + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = state.ts.length_code[lc]; + send_code(state,code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= state.ts.base_length[code]; + send_bits(state,lc, extra); /* send the extra length bits */ + } + dist = state.ts.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + AssertXZip(state,code < D_CODES, "bad d_code"); + + send_code(state,code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= state.ts.base_dist[code]; + send_bits(state,dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < state.ts.last_lit); + + send_code(state,END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +void set_file_type(TState &state) +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (TState &state,char *tgt_buf, unsigned tgt_size, int flsh_allowed) +{ + state.bs.out_buf = tgt_buf; + state.bs.out_size = tgt_size; + state.bs.out_offset = 0; + state.bs.flush_flg = flsh_allowed; + + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = 0L; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(TState &state,int value, int length) +{ + AssertXZip(state,length > 0 && length <= 15, "invalid length"); + state.bs.bits_sent += (ulg)length; + /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and + * (Buf_size - bi_valid) bits from value to flush the filled bi_buf, + * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid)) + * unused bits in bi_buf. + */ + state.bs.bi_buf |= (value << state.bs.bi_valid); + state.bs.bi_valid += length; + if (state.bs.bi_valid > (int)Buf_size) { + PUTSHORT(state,state.bs.bi_buf); + state.bs.bi_valid -= Buf_size; + state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid); + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup(TState &state) +{ + if (state.bs.bi_valid > 8) { + PUTSHORT(state,state.bs.bi_buf); + } else if (state.bs.bi_valid > 0) { + PUTBYTE(state,state.bs.bi_buf); + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param, state.bs.out_buf, &state.bs.out_offset); + } + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = (state.bs.bits_sent+7) & ~7; +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(TState &state, char *block, unsigned len, int header) +{ + bi_windup(state); /* align on byte boundary */ + + if (header) { + PUTSHORT(state,(ush)len); + PUTSHORT(state,(ush)~len); + state.bs.bits_sent += 2*16; + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param, state.bs.out_buf, &state.bs.out_offset); + state.bs.out_offset = len; + state.flush_outbuf(state.param, block, &state.bs.out_offset); + } else if (state.bs.out_offset + len > state.bs.out_size) { + AssertXZip(state,false,"output buffer too small for in-memory compression"); + } else { + memcpy(state.bs.out_buf + state.bs.out_offset, block, len); + state.bs.out_offset += len; + } + state.bs.bits_sent += (ulg)len<<3; +} + + + + + + + + +/* =========================================================================== + * Prototypes for functions. + */ + +void fill_window (TState &state); +ulg deflate_fast (TState &state); + +int longest_match (TState &state,IPos cur_match); + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)< 0 if the input file is already read or + * mmap'ed in the window[] array, 0 otherwise. In the first case, + * window_size is sufficient to contain the whole input file plus + * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end + * of window[] when looking for matches towards the end). + */ +void lm_init (TState &state, int pack_level, ush *flags) +{ + register unsigned j; + + AssertXZip(state,pack_level>=1 && pack_level<=8,"bad pack level"); + + /* Do not slide the window if the whole input is already in memory + * (window_size > 0) + */ + state.ds.sliding = 0; + if (state.ds.window_size == 0L) { + state.ds.sliding = 1; + state.ds.window_size = (ulg)2L*WSIZE; + } + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + state.ds.head[HASH_SIZE-1] = NIL; + memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE-1)*sizeof(*state.ds.head)); + + /* Set the default configuration parameters: + */ + state.ds.max_lazy_match = configuration_table[pack_level].max_lazy; + state.ds.good_match = configuration_table[pack_level].good_length; + state.ds.nice_match = configuration_table[pack_level].nice_length; + state.ds.max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level <= 2) { + *flags |= FAST; + } else if (pack_level >= 8) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + state.ds.strstart = 0; + state.ds.block_start = 0L; + + j = WSIZE; + j <<= 1; // Can read 64K in one step + state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j); + + if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) { + state.ds.eofile = 1, state.ds.lookahead = 0; + return; + } + state.ds.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + + state.ds.ins_h = 0; + for (j=0; j= 1 + */ +// For 80x86 and 680x0 and ARM, an optimized version is in match.asm or +// match.S. The code is functionally equivalent, so you can use the C version +// if desired. Which I do so desire! +int longest_match(TState &state,IPos cur_match) +{ + unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */ + register uch far *scan = state.ds.window + state.ds.strstart; /* current string */ + register uch far *match; /* matched string */ + register int len; /* length of current match */ + int best_len = state.ds.prev_length; /* best match length so far */ + IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + AssertXZip(state,HASH_BITS>=8 && MAX_MATCH==258,"Code too clever"); + + + + register uch far *strend = state.ds.window + state.ds.strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (state.ds.prev_length >= state.ds.good_match) { + chain_length >>= 2; + } + + AssertXZip(state,state.ds.strstart <= state.ds.window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + AssertXZip(state,cur_match < state.ds.strstart, "no future"); + match = state.ds.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + AssertXZip(state,scan <= state.ds.window+(unsigned)(state.ds.window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + + if (len > best_len) { + state.ds.match_start = cur_match; + best_len = len; + if (len >= state.ds.nice_match) break; + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; + } + } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + + +#define check_match(state,start, match, length) +// or alternatively... +//void check_match(TState &state,IPos start, IPos match, int length) +//{ // check that the match is indeed a match +// if (memcmp((char*)state.ds.window + match, +// (char*)state.ds.window + start, length) != EQUAL) { +// fprintf(stderr, +// " start %d, match %d, length %d\n", +// start, match, length); +// error("invalid match"); +// } +// if (state.verbose > 1) { +// fprintf(stderr,"\\[%d,%d]", start-match, length); +// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0); +// } +//} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or eofile is set; file reads are + * performed for at least two bytes (required for the translate_eol option). + */ +void fill_window(TState &state) +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart); + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* For MMAP or BIG_MEM, the whole input file is already in memory so + * we must not perform sliding. We must however call (*read_buf)() in + * order to compute the crc, update lookahead and possibly set eofile. + */ + } else if (state.ds.strstart >= WSIZE+MAX_DIST && state.ds.sliding) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)state.ds.window, (char*)state.ds.window+WSIZE, (unsigned)WSIZE); + state.ds.match_start -= WSIZE; + state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + state.ds.block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = state.ds.head[n]; + state.ds.head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = state.ds.prev[n]; + state.ds.prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + if (state.ds.eofile) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the MMAP or BIG_MEM case (not yet supported in gzip), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + AssertXZip(state,more >= 2, "more < 2"); + + n = state.readfunc(state, (char*)state.ds.window+state.ds.strstart+state.ds.lookahead, more); + + if (n == 0 || n == (unsigned)EOF) { + state.ds.eofile = 1; + } else { + state.ds.lookahead += n; + } + } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(state,eof) \ + flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \ + (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof)) + +/* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +ulg deflate_fast(TState &state) +{ + IPos hash_head = NIL; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + state.ds.prev_length = MIN_MATCH-1; + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(state,state.ds.strstart, state.ds.match_start, match_length); + + flush = ct_tally(state,state.ds.strstart-state.ds.match_start, match_length - MIN_MATCH); + + state.ds.lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= state.ds.max_insert_length + && state.ds.lookahead >= MIN_MATCH) { + match_length--; /* string at strstart already in hash table */ + do { + state.ds.strstart++; + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--match_length != 0); + state.ds.strstart++; + } else { + state.ds.strstart += match_length; + match_length = 0; + state.ds.ins_h = state.ds.window[state.ds.strstart]; + UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart+1]); + AssertXZip(state,MIN_MATCH==3,"Call UPDATE_HASH() MIN_MATCH-3 more times"); + } + } else { + /* No match, output a literal byte */ + flush = ct_tally (state,0, state.ds.window[state.ds.strstart]); + state.ds.lookahead--; + state.ds.strstart++; + } + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + return FLUSH_BLOCK(state,1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate(TState &state) +{ + IPos hash_head = NIL; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ + + if (state.level <= 3) return deflate_fast(state); /* optimized for speed */ + + /* Process the input block. */ + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + state.ds.prev_length = match_length, prev_match = state.ds.match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match && + state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && state.ds.strstart-state.ds.match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) { + unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH; + check_match(state,state.ds.strstart-1, prev_match, state.ds.prev_length); + flush = ct_tally(state,state.ds.strstart-1-prev_match, state.ds.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + state.ds.lookahead -= state.ds.prev_length-1; + state.ds.prev_length -= 2; + do { + if (++state.ds.strstart <= max_insert) { + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } + } while (--state.ds.prev_length != 0); + state.ds.strstart++; + match_available = 0; + match_length = MIN_MATCH-1; + + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally (state,0, state.ds.window[state.ds.strstart-1])) { + FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + } + state.ds.strstart++; + state.ds.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + state.ds.strstart++; + state.ds.lookahead--; + } +// AssertXZip(state,strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + if (match_available) ct_tally (state,0, state.ds.window[state.ds.strstart-1]); + + return FLUSH_BLOCK(state,1); /* eof */ +} + + + + + + + + + + + + +int putlocal(struct zlist far *z, WRITEFUNC wfunc,void *param) +{ // Write a local header described by *z to file *f. Return a ZE_ error code. + PUTLG(LOCSIG, f); + PUTSH(z->ver, f); + PUTSH(z->lflg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->ext, f); + size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam); + if (res!=z->nam) return ZE_TEMP; + if (z->ext) + { res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext); + if (res!=z->ext) return ZE_TEMP; + } + return ZE_OK; +} + +int putextended(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write an extended local header described by *z to file *f. Returns a ZE_ code + PUTLG(EXTLOCSIG, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + return ZE_OK; +} + +int putcentral(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write a central header entry of *z to file *f. Returns a ZE_ code. + PUTLG(CENSIG, f); + PUTSH(z->vem, f); + PUTSH(z->ver, f); + PUTSH(z->flg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->cext, f); + PUTSH(z->com, f); + PUTSH(z->dsk, f); + PUTSH(z->att, f); + PUTLG(z->atx, f); + PUTLG(z->off, f); + if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam || + (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) || + (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com)) + return ZE_TEMP; + return ZE_OK; +} + + +int putend(int n, ulg s, ulg c, extent m, char *z, WRITEFUNC wfunc, void *param) +{ // write the end of the central-directory-data to file *f. + PUTLG(ENDSIG, f); + PUTSH(0, f); + PUTSH(0, f); + PUTSH(n, f); + PUTSH(n, f); + PUTLG(s, f); + PUTLG(c, f); + PUTSH(m, f); + // Write the comment, if any + if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP; + return ZE_OK; +} + + + + + + +const ulg crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) +#define DO1(buf) crc = CRC32(crc, *buf++) +#define DO2(buf) DO1(buf); DO1(buf) +#define DO4(buf) DO2(buf); DO2(buf) +#define DO8(buf) DO4(buf); DO4(buf) + +ulg crc32(ulg crc, const uch *buf, extent len) +{ if (buf==NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {DO8(buf); len -= 8;} + if (len) do {DO1(buf);} while (--len); + return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines) +} + + + + + + + + +bool HasZipSuffix(const char *fn) +{ const char *ext = fn+strlen(fn); + while (ext>fn && *ext!='.') ext--; + if (ext==fn && *ext!='.') return false; + if (_stricmp(ext,".Z")==0) return true; + if (_stricmp(ext,".zip")==0) return true; + if (_stricmp(ext,".zoo")==0) return true; + if (_stricmp(ext,".arc")==0) return true; + if (_stricmp(ext,".lzh")==0) return true; + if (_stricmp(ext,".arj")==0) return true; + if (_stricmp(ext,".gz")==0) return true; + if (_stricmp(ext,".tgz")==0) return true; + return false; +} + +#ifdef _WIN32 +time_t filetime2timet(const FILETIME ft) +{ SYSTEMTIME st; FileTimeToSystemTime(&ft,&st); + if (st.wYear<1970) {st.wYear=1970; st.wMonth=1; st.wDay=1;} + if (st.wYear>=2038) {st.wYear=2037; st.wMonth=12; st.wDay=31;} + struct tm tm; + tm.tm_sec = st.wSecond; + tm.tm_min = st.wMinute; + tm.tm_hour = st.wHour; + tm.tm_mday = st.wDay; + tm.tm_mon = st.wMonth-1; + tm.tm_year = st.wYear-1900; + tm.tm_isdst = 0; + time_t t = mktime(&tm); + return t; +} + +ZRESULT GetFileInfo(HANDLE hf, ulg *attr, long *size, iztimes *times, ulg *timestamp) +{ + DWORD type=GetFileType(hf); + if (type!=FILE_TYPE_DISK) + return ZR_NOTINITED; + // The handle must be a handle to a file + // The date and time is returned in a long with the date most significant to allow + // unsigned integer comparison of absolute times. The attributes have two + // high bytes unix attr, and two low bytes a mapping of that to DOS attr. + //struct stat s; int res=stat(fn,&s); if (res!=0) return false; + // translate windows file attributes into zip ones. + BY_HANDLE_FILE_INFORMATION bhi; + BOOL res=GetFileInformationByHandle(hf,&bhi); + if (!res) + return ZR_NOFILE; + FileTimeToLocalFileTime( &bhi.ftLastAccessTime, &bhi.ftLastAccessTime ); + FileTimeToLocalFileTime( &bhi.ftLastWriteTime, &bhi.ftLastWriteTime ); + FileTimeToLocalFileTime( &bhi.ftCreationTime, &bhi.ftCreationTime ); + DWORD fa=bhi.dwFileAttributes; + ulg a=0; + // Zip uses the lower word for its interpretation of windows stuff + if (fa&FILE_ATTRIBUTE_READONLY) a|=0x01; + if (fa&FILE_ATTRIBUTE_HIDDEN) a|=0x02; + if (fa&FILE_ATTRIBUTE_SYSTEM) a|=0x04; + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x10; + if (fa&FILE_ATTRIBUTE_ARCHIVE) a|=0x20; + // It uses the upper word for standard unix attr, which we must manually construct + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x40000000; // directory + else a|=0x80000000; // normal file + a|=0x01000000; // readable + if (fa&FILE_ATTRIBUTE_READONLY) {} + else a|=0x00800000; // writeable + // now just a small heuristic to check if it's an executable: + DWORD red, hsize=GetFileSize(hf,NULL); if (hsize>40) + { SetFilePointer(hf,0,NULL,FILE_BEGIN); unsigned short magic; ReadFile(hf,&magic,sizeof(magic),&red,NULL); + SetFilePointer(hf,36,NULL,FILE_BEGIN); unsigned long hpos; ReadFile(hf,&hpos,sizeof(hpos),&red,NULL); + if (magic==0x54AD && hsize>hpos+4+20+28) + { SetFilePointer(hf,hpos,NULL,FILE_BEGIN); unsigned long signature; ReadFile(hf,&signature,sizeof(signature),&red,NULL); + if (signature==IMAGE_DOS_SIGNATURE || signature==IMAGE_OS2_SIGNATURE + || signature==IMAGE_OS2_SIGNATURE_LE || signature==IMAGE_NT_SIGNATURE) + { a |= 0x00400000; // executable + } + } + } + // + if (attr!=NULL) *attr = a; + if (size!=NULL) *size = hsize; + if (times!=NULL) + { // time_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970. + // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601 + times->atime = filetime2timet(bhi.ftLastAccessTime); + times->mtime = filetime2timet(bhi.ftLastWriteTime); + times->ctime = filetime2timet(bhi.ftCreationTime); + } + if (timestamp!=NULL) + { WORD dosdate,dostime; + FileTimeToDosDateTime(&bhi.ftLastWriteTime,&dosdate,&dostime); + *timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + } + return ZR_OK; +} +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class TZip +{ public: + TZip() : hfout(0),hmapout(0),zfis(0),obuf(0),hfin(0),writ(0),oerr(false),hasputcen(false),ooffset(0) {} + ~TZip() {} + + // These variables say about the file we're writing into + // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile + HANDLE hfout; // if valid, we'll write here (for files or pipes) + HANDLE hmapout; // otherwise, we'll write here (for memmap) + unsigned ooffset; // for hfout, this is where the pointer was initially + ZRESULT oerr; // did a write operation give rise to an error? + unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks + bool ocanseek; // can we seek? + char *obuf; // this is where we've locked mmap to view. + unsigned int size; // how big is the buffer (needed for munmap on *nix) + unsigned int opos; // current pos in the mmap + unsigned int mapsize; // the size of the map we created + bool hasputcen; // have we yet placed the central directory? + // + TZipFileInfo *zfis; // each file gets added onto this list, for writing the table at the end + + ZRESULT Create(void *z,unsigned int len,DWORD flags); + static unsigned sflush(void *param,const char *buf, unsigned *size); + static unsigned swrite(void *param,const char *buf, unsigned size); + unsigned int write(const char *buf,unsigned int size); + bool oseek(unsigned int pos); + ZRESULT GetMemory(void **pbuf, unsigned long *plen); + ZRESULT Close(); + + // some variables to do with the file currently being read: + // I haven't done it object-orientedly here, just put them all + // together, since OO didn't seem to make the design any clearer. + ulg attr; iztimes times; ulg timestamp; // all open_* methods set these + bool iseekable; long isize,ired; // size is not set until close() on pips + ulg crc; // crc is not set until close(). iwrit is cumulative + HANDLE hfin; bool selfclosehf; // for input files and pipes + const char *bufin; unsigned int lenin,posin; // for memory + // and a variable for what we've done with the input: (i.e. compressed it!) + ulg csize; // compressed size, set by the compression routines + // and this is used by some of the compression routines + char buf[16384]; + + + ZRESULT open_file(const TCHAR *fn); + ZRESULT open_handle(HANDLE hf,unsigned int len); + ZRESULT open_mem(void *src,unsigned int len); + ZRESULT open_dir(); + static unsigned sread(TState &s,char *buf,unsigned size); + unsigned read(char *buf, unsigned size); + ZRESULT iclose(); + + ZRESULT ideflate(TZipFileInfo *zfi); + ZRESULT istore(); + + ZRESULT Add(const char *odstzn, void *src,unsigned int len, DWORD flags); + ZRESULT AddCentral(); + +}; + +ZRESULT TZip::Create(void *z,unsigned int len,DWORD flags) +{ + if (hfout!=0 || hmapout!=0 || obuf!=0 || writ!=0 || oerr!=ZR_OK || hasputcen) + return ZR_NOTINITED; + // + if (flags==ZIP_MEMORY) + { + size = len; + if (size==0) + return ZR_MEMSIZE; + if (z!=0) + obuf=(char*)z; + else + { +#ifdef _WIN32 + hmapout = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,size,NULL); + if (hmapout==NULL) + return ZR_NOALLOC; + obuf = (char*)MapViewOfFile(hmapout,FILE_MAP_ALL_ACCESS,0,0,size); + if (obuf==0) + { + CloseHandle(hmapout); + hmapout=0; + return ZR_NOALLOC; + } +#endif +#ifdef POSIX + obuf = (char*) mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0 ); + if (obuf==NULL) + return ZR_NOALLOC; +#endif + } + ocanseek=true; + opos=0; + mapsize=size; + return ZR_OK; + } +#ifdef _WIN32 + else if (flags==ZIP_HANDLE) + { + HANDLE hf = (HANDLE)z; + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&hfout,0,FALSE,DUPLICATE_SAME_ACCESS); + if (!res) + return ZR_NODUPH; + // now we have our own hfout, which we must close. And the caller will close hf + DWORD type = GetFileType(hfout); + ocanseek = (type==FILE_TYPE_DISK); + if (type==FILE_TYPE_DISK) + ooffset=SetFilePointer(hfout,0,NULL,FILE_CURRENT); + else + ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_FILENAME) + { +#ifdef _UNICODE + const TCHAR *fn = (const TCHAR*)z; + hfout = CreateFileW(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#else + const char *fn = (const char*)z; + hfout = CreateFileA(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#endif + + if (hfout==INVALID_HANDLE_VALUE) + { + hfout=0; + return ZR_NOFILE; + } + ocanseek=true; + ooffset=0; + return ZR_OK; + } +#endif + else + return ZR_ARGS; +} + + +unsigned TZip::sflush(void *param,const char *buf, unsigned *size) +{ // static + if (*size==0) return 0; + TZip *zip = (TZip*)param; + unsigned int writ = zip->write(buf,*size); + if (writ!=0) *size=0; + return writ; +} +unsigned TZip::swrite(void *param,const char *buf, unsigned size) +{ // static + if (size==0) return 0; + TZip *zip=(TZip*)param; return zip->write(buf,size); +} +unsigned int TZip::write(const char *buf,unsigned int size) +{ if (obuf!=0) + { if (opos+size>=mapsize) {oerr=ZR_MEMSIZE; return 0;} + memcpy(obuf+opos, buf, size); + opos+=size; + return size; + } +#ifdef _WIN32 + else if (hfout!=0) + { DWORD writ; WriteFile(hfout,buf,size,&writ,NULL); + return writ; + } +#endif + oerr=ZR_NOTINITED; return 0; +} + +bool TZip::oseek(unsigned int pos) +{ if (!ocanseek) {oerr=ZR_SEEK; return false;} + if (obuf!=0) + { if (pos>=mapsize) {oerr=ZR_MEMSIZE; return false;} + opos=pos; + return true; + } +#ifdef _WIN32 + else if (hfout!=0) + { SetFilePointer(hfout,pos+ooffset,NULL,FILE_BEGIN); + return true; + } +#endif + oerr=ZR_NOTINITED; return 0; +} + +ZRESULT TZip::GetMemory(void **pbuf, unsigned long *plen) +{ // When the user calls GetMemory, they're presumably at the end + // of all their adding. In any case, we have to add the central + // directory now, otherwise the memory we tell them won't be complete. + if (!hasputcen) AddCentral(); hasputcen=true; + if (pbuf!=NULL) *pbuf=(void*)obuf; + if (plen!=NULL) *plen=writ; + if (obuf==NULL) return ZR_NOTMMAP; + return ZR_OK; +} + +ZRESULT TZip::Close() +{ // if the directory hadn't already been added through a call to GetMemory, + // then we do it now + ZRESULT res=ZR_OK; if (!hasputcen) res=AddCentral(); hasputcen=true; + if (obuf!=0 && hmapout!=0) +#ifdef _WIN32 + UnmapViewOfFile(obuf); +#endif +#ifdef POSIX + munmap(obuf, size); +#endif + size=0; + obuf=0; +#ifdef _WIN32 + if (hmapout!=0) CloseHandle(hmapout); hmapout=0; + if (hfout!=0) CloseHandle(hfout); hfout=0; +#endif + return res; +} + + + + +ZRESULT TZip::open_file(const TCHAR *fn) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (fn==0) return ZR_ARGS; + HANDLE hf = INVALID_HANDLE_VALUE; +#ifdef _WIN32 + hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); +#endif + if (hf==INVALID_HANDLE_VALUE) return ZR_NOFILE; + ZRESULT res = open_handle(hf,0); + if (res!=ZR_OK) { +#ifdef _WIN32 + CloseHandle(hf); +#endif + return res; + } + selfclosehf=true; + return ZR_OK; +} +ZRESULT TZip::open_handle(HANDLE hf,unsigned int len) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (hf==0 || hf==INVALID_HANDLE_VALUE) return ZR_ARGS; +#ifdef _WIN32 + DWORD type = GetFileType(hf); + if (type==FILE_TYPE_DISK) + { ZRESULT res = GetFileInfo(hf,&attr,&isize,×,×tamp); + if (res!=ZR_OK) return res; + SetFilePointer(hf,0,NULL,FILE_BEGIN); // because GetFileInfo will have screwed it up + iseekable=true; hfin=hf; + return ZR_OK; + } + else + { attr= 0x80000000; // just a normal file + isize = -1; // can't know size until at the end + if (len!=0) isize=len; // unless we were told explicitly! + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + hfin=hf; + return ZR_OK; + } +#else + return ZR_FAILED; +#endif +} + +ZRESULT TZip::open_mem(void *src,unsigned int len) +{ hfin=0; bufin=(const char*)src; selfclosehf=false; crc=CRCVAL_INITIAL; ired=0; csize=0; ired=0; + lenin=len; posin=0; + if (src==0 || len==0) return ZR_ARGS; +#ifdef _WIN32 + attr= 0x80000000; // just a normal file + isize = len; + iseekable=true; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +#else + return ZR_FAILED; +#endif +} + +ZRESULT TZip::open_dir() +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; +#ifdef _WIN32 + attr= 0x41C00010; // a readable writable directory, and again directory + isize = 0; + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +#else + return ZR_FAILED; +#endif +} + +unsigned TZip::sread(TState &s,char *buf,unsigned size) +{ // static + TZip *zip = (TZip*)s.param; + return zip->read(buf,size); +} + +unsigned TZip::read(char *buf, unsigned size) +{ if (bufin!=0) + { if (posin>=lenin) return 0; // end of input + ulg red = lenin-posin; + if (red>size) red=size; + memcpy(buf, bufin+posin, red); + posin += red; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } +#ifdef _WIN32 + else if (hfin!=0) + { DWORD red; + BOOL ok = ReadFile(hfin,buf,size,&red,NULL); + if (!ok) return 0; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } +#endif + else {oerr=ZR_NOTINITED; return 0;} +} + +ZRESULT TZip::iclose() +{ +#ifdef _WIN32 + if (selfclosehf && hfin!=0) CloseHandle(hfin); +#endif + hfin=0; + bool mismatch = (isize!=-1 && isize!=ired); + isize=ired; // and crc has been being updated anyway + if (mismatch) return ZR_MISSIZE; + else return ZR_OK; +} + + + +ZRESULT TZip::ideflate(TZipFileInfo *zfi) +{ TState state; + state.readfunc=sread; state.flush_outbuf=sflush; + state.param=this; state.level=8; state.seekable=iseekable; state.err=NULL; + // the following line will make ct_init realise it has to perform the init + state.ts.static_dtree[0].dl.len = 0; + // It would be nicer if I could figure out precisely which data had to + // be initted each time, and which didn't, but that's kind of difficult. + // Maybe for the next version... + // + bi_init(state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(state,&zfi->att); + lm_init(state,state.level, &zfi->flg); + ulg sz = deflate(state); + csize=sz; + if (state.err!=NULL) return ZR_FLATE; + else return ZR_OK; +} + +ZRESULT TZip::istore() +{ ulg size=0; + for (;;) + { unsigned int cin=read(buf,16384); if (cin<=0 || cin==(unsigned int)EOF) break; + unsigned int cout = write(buf,cin); if (cout!=cin) return ZR_MISSIZE; + size += cin; + } + csize=size; + return ZR_OK; +} + + + + +ZRESULT TZip::Add(const char *odstzn, void *src,unsigned int len, DWORD flags) +{ + if (oerr) + return ZR_FAILED; + if (hasputcen) + return ZR_ENDED; + + // zip has its own notion of what its names should look like: i.e. dir/file.stuff + char dstzn[MAX_PATH]; + strcpy(dstzn, odstzn); + if (*dstzn == 0) + return ZR_ARGS; + char *d=dstzn; + while (*d != 0) + { + if (*d == '\\') + *d = '/'; d++; + } + bool isdir = (flags==ZIP_FOLDER); + bool needs_trailing_slash = (isdir && dstzn[strlen(dstzn)-1]!='/'); + int method=DEFLATE; + if (isdir || HasZipSuffix(dstzn)) + method=STORE; + + // now open whatever was our input source: + ZRESULT openres; + if (flags==ZIP_FILENAME) + openres=open_file((const TCHAR*)src); + else if (flags==ZIP_HANDLE) + openres=open_handle((HANDLE)src,len); + else if (flags==ZIP_MEMORY) + openres=open_mem(src,len); + else if (flags==ZIP_FOLDER) + openres=open_dir(); + else return ZR_ARGS; + if (openres!=ZR_OK) + return openres; + + // A zip "entry" consists of a local header (which includes the file name), + // then the compressed data, and possibly an extended local header. + + // Initialize the local header + TZipFileInfo zfi; zfi.nxt=NULL; + strcpy(zfi.name,""); + strcpy(zfi.iname,dstzn); + zfi.nam=strlen(zfi.iname); + if (needs_trailing_slash) + { + strcat(zfi.iname,"/"); + zfi.nam++; + } + strcpy(zfi.zname,""); + zfi.extra=NULL; zfi.ext=0; // extra header to go after this compressed data, and its length + zfi.cextra=NULL; zfi.cext=0; // extra header to go in the central end-of-zip directory, and its length + zfi.comment=NULL; zfi.com=0; // comment, and its length + zfi.mark = 1; + zfi.dosflag = 0; + zfi.att = (ush)BINARY; + zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3 + zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it + zfi.tim = timestamp; + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfi.crc = 0; // to be updated later + zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it. + zfi.lflg = zfi.flg; // to be updated later + zfi.how = (ush)method; // to be updated later + zfi.siz = (ulg)(method==STORE && isize>=0 ? isize : 0); // to be updated later + zfi.len = (ulg)(isize); // to be updated later + zfi.dsk = 0; + zfi.atx = attr; + zfi.off = writ+ooffset; // offset within file of the start of this local record + // stuff the 'times' structure into zfi.extra + char xloc[EB_L_UT_SIZE]; + zfi.extra=xloc; + zfi.ext=EB_L_UT_SIZE; + char xcen[EB_C_UT_SIZE]; + zfi.cextra=xcen; + zfi.cext=EB_C_UT_SIZE; + xloc[0] = 'U'; + xloc[1] = 'T'; + xloc[2] = EB_UT_LEN(3); // length of data part of e.f. + xloc[3] = 0; + xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME; + xloc[5] = (char)(times.mtime); + xloc[6] = (char)(times.mtime >> 8); + xloc[7] = (char)(times.mtime >> 16); + xloc[8] = (char)(times.mtime >> 24); + xloc[9] = (char)(times.atime); + xloc[10] = (char)(times.atime >> 8); + xloc[11] = (char)(times.atime >> 16); + xloc[12] = (char)(times.atime >> 24); + xloc[13] = (char)(times.ctime); + xloc[14] = (char)(times.ctime >> 8); + xloc[15] = (char)(times.ctime >> 16); + xloc[16] = (char)(times.ctime >> 24); + memcpy(zfi.cextra,zfi.extra,EB_C_UT_SIZE); + zfi.cextra[EB_LEN] = EB_UT_LEN(1); + + + // (1) Start by writing the local header: + int r = putlocal(&zfi,swrite,this); + if (r!=ZE_OK) + { + iclose(); + return ZR_WRITE; + } + writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext; + if (oerr!=ZR_OK) + { + iclose(); + return oerr; + } + + //(2) Write deflated/stored file to zip file + ZRESULT writeres=ZR_OK; + if (!isdir && method==DEFLATE) + writeres=ideflate(&zfi); + else if (!isdir && method==STORE) + writeres=istore(); + else if (isdir) + csize=0; + iclose(); + writ += csize; + if (oerr!=ZR_OK) + return oerr; + if (writeres!=ZR_OK) + return ZR_WRITE; + + // (3) Either rewrite the local header with correct information... + bool first_header_has_size_right = (zfi.siz==csize); + zfi.crc = crc; + zfi.siz = csize; + zfi.len = isize; + if (ocanseek) + { + zfi.how = (ush)method; + if ((zfi.flg & 1) == 0) + zfi.flg &= ~8; // clear the extended local header flag + zfi.lflg = zfi.flg; + // rewrite the local header: + if (!oseek(zfi.off-ooffset)) + return ZR_SEEK; + if ((r = putlocal(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + if (!oseek(writ)) + return ZR_SEEK; + } + else + { + // (4) ... or put an updated header at the end + if (zfi.how != (ush) method) + return ZR_NOCHANGE; + if (method==STORE && !first_header_has_size_right) + return ZR_NOCHANGE; + if ((r = putextended(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + writ += 16L; + zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index + } + if (oerr!=ZR_OK) + return oerr; + + // Keep a copy of the zipfileinfo, for our end-of-zip directory + char *cextra = new char[zfi.cext]; + memcpy(cextra,zfi.cextra,zfi.cext); zfi.cextra=cextra; + TZipFileInfo *pzfi = new TZipFileInfo; + memcpy(pzfi,&zfi,sizeof(zfi)); + if (zfis==NULL) + zfis=pzfi; + else + { + TZipFileInfo *z=zfis; + while (z->nxt!=NULL) + z=z->nxt; + z->nxt=pzfi; + } + return ZR_OK; +} + +ZRESULT TZip::AddCentral() +{ // write central directory + int numentries = 0; + ulg pos_at_start_of_central = writ; + //ulg tot_unc_size=0, tot_compressed_size=0; + bool okay=true; + for (TZipFileInfo *zfi=zfis; zfi!=NULL; ) + { if (okay) + { int res = putcentral(zfi, swrite,this); + if (res!=ZE_OK) okay=false; + } + writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com; + //tot_unc_size += zfi->len; + //tot_compressed_size += zfi->siz; + numentries++; + // + TZipFileInfo *zfinext = zfi->nxt; + if (zfi->cextra!=0) delete[] zfi->cextra; + delete zfi; + zfi = zfinext; + } + ulg center_size = writ - pos_at_start_of_central; + if (okay) + { int res = putend(numentries, center_size, pos_at_start_of_central+ooffset, 0, NULL, swrite,this); + if (res!=ZE_OK) okay=false; + writ += 4 + ENDHEAD + 0; + } + if (!okay) return ZR_WRITE; + return ZR_OK; +} + + +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorZ; + const char *msg="unknown zip result code"; + switch (code) + { case ZR_OK: msg="Success"; break; + case ZR_NODUPH: msg="Culdn't duplicate handle"; break; + case ZR_NOFILE: msg="Couldn't create/open file"; break; + case ZR_NOALLOC: msg="Failed to allocate memory"; break; + case ZR_WRITE: msg="Error writing to file"; break; + case ZR_NOTFOUND: msg="File not found in the zipfile"; break; + case ZR_MORE: msg="Still more data to unzip"; break; + case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg="Error reading file"; break; + case ZR_ARGS: msg="Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg="Caller: there was a previous error"; break; + case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break; + } + unsigned int mlen=(unsigned int)strlen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + + +typedef struct +{ DWORD flag; + TZip *zip; +} TZipHandleData; + + +HZIP CreateZipZ(void *z,unsigned int len,DWORD flags) +{ + _tzset(); + TZip *zip = new TZip(); + lasterrorZ = zip->Create(z,len,flags); + if (lasterrorZ != ZR_OK) + { + delete zip; + return 0; + } + TZipHandleData *han = new TZipHandleData; + han->flag = 2; + han->zip = zip; + return (HZIP)han; +} + +ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags) +{ + if (hz == 0) + { + lasterrorZ = ZR_ARGS; + return ZR_ARGS; + } + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag != 2) + { + lasterrorZ = ZR_ZMODE; + return ZR_ZMODE; + } + TZip *zip = han->zip; + + + if (flags == ZIP_FILENAME) + { + char szDest[MAX_PATH*2]; + memset(szDest, 0, sizeof(szDest)); + +#ifdef _UNICODE + // need to convert Unicode dest to ANSI + int nActualChars = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + (LPCWSTR) dstzn, // wide-character string + -1, // number of chars in string + szDest, // buffer for new string + MAX_PATH*2-2, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + if (nActualChars == 0) + return ZR_ARGS; +#else + strcpy(szDest, dstzn); +#endif + + lasterrorZ = zip->Add(szDest, src, len, flags); + } + else + { + lasterrorZ = zip->Add((char *)dstzn, src, len, flags); + } + + return lasterrorZ; +} + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len) +{ if (hz==0) {if (buf!=0) *buf=0; if (len!=0) *len=0; lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->GetMemory(buf,len); + return lasterrorZ; +} + +ZRESULT CloseZipZ(HZIP hz) +{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->Close(); + delete zip; + delete han; + return lasterrorZ; +} + +bool IsZipHandleZ(HZIP hz) +{ if (hz==0) return true; + TZipHandleData *han = (TZipHandleData*)hz; + return (han->flag==2); +} + + diff --git a/public/appframework/AppFramework.h b/public/appframework/AppFramework.h new file mode 100644 index 0000000..f6056c1 --- /dev/null +++ b/public/appframework/AppFramework.h @@ -0,0 +1,228 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: An application framework +// +// $Revision: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef APPFRAMEWORK_H +#define APPFRAMEWORK_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystemGroup.h" +#include "ilaunchabledll.h" + +//----------------------------------------------------------------------------- +// Gets the application instance.. +//----------------------------------------------------------------------------- +void *GetAppInstance(); + + +//----------------------------------------------------------------------------- +// Sets the application instance, should only be used if you're not calling AppMain. +//----------------------------------------------------------------------------- +void SetAppInstance( void* hInstance ); + + +//----------------------------------------------------------------------------- +// Main entry point for the application +//----------------------------------------------------------------------------- +int AppMain( void* hInstance, void* hPrevInstance, const char* lpCmdLine, int nCmdShow, CAppSystemGroup *pAppSystemGroup ); +int AppMain( int argc, char **argv, CAppSystemGroup *pAppSystemGroup ); + + +//----------------------------------------------------------------------------- +// Used to startup/shutdown the application +//----------------------------------------------------------------------------- +int AppStartup( void* hInstance, void* hPrevInstance, const char* lpCmdLine, int nCmdShow, CAppSystemGroup *pAppSystemGroup ); +int AppStartup( int argc, char **argv, CAppSystemGroup *pAppSystemGroup ); +void AppShutdown( CAppSystemGroup *pAppSystemGroup ); + + +//----------------------------------------------------------------------------- +// Macros to create singleton application objects for windowed + console apps +//----------------------------------------------------------------------------- + +// This assumes you've used one of the +#define DEFINE_LAUNCHABLE_DLL_STEAM_APP() \ + class CAppLaunchableDLL : public ILaunchableDLL \ + { \ + public: \ + virtual int main( int argc, char **argv ) \ + { \ + return AppMain( argc, argv, &__s_SteamApplicationObject ); \ + } \ + }; \ + static CAppLaunchableDLL __g_AppLaunchableDLL; \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CAppLaunchableDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION, __g_AppLaunchableDLL ); + + +#if !defined( _X360 ) +#if defined( _OSX ) +#define DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + int main( int argc, char **argv ) \ + { \ + extern int ValveCocoaMain( int argc, char **argv, CAppSystemGroup *pAppSystemGroup ); \ + return ValveCocoaMain( argc, argv, &_globalVarName ); \ + } +#elif defined( PLATFORM_LINUX ) +#define DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + int main( int argc, char **argv ) \ + { \ + extern int ValveLinuxWindowedMain( int argc, char **argv, CAppSystemGroup *pAppSystemGroup ); \ + return ValveLinuxWindowedMain( argc, argv, &_globalVarName ); \ + } +#else +#define DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + int __stdcall WinMain( struct HINSTANCE__* hInstance, struct HINSTANCE__* hPrevInstance, NULLTERMINATED char *lpCmdLine, int nCmdShow ) \ + { \ + return AppMain( hInstance, hPrevInstance, lpCmdLine, nCmdShow, &_globalVarName ); \ + } +#endif +#else +#define DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + DLL_EXPORT int AppMain360( struct HINSTANCE__* hInstance, struct HINSTANCE__* hPrevInstance, NULLTERMINATED char *lpCmdLine, int nCmdShow ) \ + { \ + return AppMain( hInstance, hPrevInstance, lpCmdLine, nCmdShow, &_globalVarName ); \ + } +#endif + +#if !defined( _X360 ) +#define DEFINE_CONSOLE_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + int main( int argc, char **argv ) \ + { \ + return AppMain( argc, argv, &_globalVarName ); \ + } +#else +#define DEFINE_CONSOLE_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + DLL_EXPORT int AppMain360( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) \ + { \ + return AppMain( hInstance, hPrevInstance, lpCmdLine, nCmdShow, &_globalVarName ); \ + } +#endif + +#define DEFINE_BINLAUNCHABLE_APPLICATION_OBJECT_GLOBALVAR( _globalVarName ) \ + class CApplicationDLL : public ILaunchableDLL \ + { \ + public: \ + virtual int main( int argc, char **argv ) \ + { \ + return AppMain( argc, argv, &_globalVarName ); \ + } \ + }; \ + EXPOSE_SINGLE_INTERFACE( CApplicationDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION ) + +#define DEFINE_WINDOWED_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( __s_ApplicationObject ) + +#define DEFINE_CONSOLE_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + DEFINE_CONSOLE_APPLICATION_OBJECT_GLOBALVAR( __s_ApplicationObject ) + +#define DEFINE_BINLAUNCHABLE_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + DEFINE_BINLAUNCHABLE_APPLICATION_OBJECT_GLOBALVAR( __s_ApplicationObject ) + + +//----------------------------------------------------------------------------- +// This class is a helper class used for steam-based applications. +// It loads up the file system in preparation for using it to load other +// required modules from steam. +//----------------------------------------------------------------------------- +class CSteamApplication : public CAppSystemGroup +{ + typedef CAppSystemGroup BaseClass; + +public: + CSteamApplication( CSteamAppSystemGroup *pAppSystemGroup ); + + // Implementation of IAppSystemGroup + virtual bool Create( ); + virtual bool PreInit( ); + virtual int Main( ); + virtual void PostShutdown(); + virtual void Destroy(); + + // Use this version in cases where you can't control the main loop and + // expect to be ticked + virtual int Startup(); + virtual void Shutdown(); + +public: + // Here's a hook to override the filesystem DLL that it tries to load. + // By default, it uses FileSystem_GetFileSystemDLLName to figure this out. + virtual bool GetFileSystemDLLName( char *pOut, int nMaxBytes, bool &bIsSteam ); + +protected: + IFileSystem *m_pFileSystem; + CSteamAppSystemGroup *m_pChildAppSystemGroup; + bool m_bSteam; +}; + + +class CBinLaunchableSteamApp : public CSteamApplication +{ +public: + CBinLaunchableSteamApp( CSteamAppSystemGroup *pAppSystemGroup ) : CSteamApplication( pAppSystemGroup ) + { + } + + virtual bool GetFileSystemDLLName( char *pOut, int nMaxChars, bool &bIsSteam ) + { + // Our path should already include game\bin, so just use the filename directly + // and don't try to figure out an absolute path to it as CSteamApplication does. + V_strncpy( pOut, "filesystem_stdio", nMaxChars ); + bIsSteam = false; + return true; + } +}; + + +//----------------------------------------------------------------------------- +// Macros to help create singleton application objects for windowed + console steam apps +//----------------------------------------------------------------------------- +#define DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT_GLOBALVAR( _className, _varName ) \ + static CSteamApplication __s_SteamApplicationObject( &_varName ); \ + DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + +#define DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + static CSteamApplication __s_SteamApplicationObject( &__s_ApplicationObject ); \ + DEFINE_WINDOWED_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + +#define DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT_GLOBALVAR( _className, _varName ) \ + static CSteamApplication __s_SteamApplicationObject( &_varName ); \ + DEFINE_CONSOLE_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + +#define DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + static CSteamApplication __s_SteamApplicationObject( &__s_ApplicationObject ); \ + DEFINE_CONSOLE_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + +#define DEFINE_BINLAUNCHABLE_STEAM_APPLICATION_OBJECT_GLOBALVAR( _className, _varName ) \ + static CBinLaunchableSteamApp __s_SteamApplicationObject( &_varName ); \ + DEFINE_BINLAUNCHABLE_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + +#define DEFINE_BINLAUNCHABLE_STEAM_APPLICATION_OBJECT( _className ) \ + static _className __s_ApplicationObject; \ + static CBinLaunchableSteamApp __s_SteamApplicationObject( &__s_ApplicationObject ); \ + DEFINE_BINLAUNCHABLE_APPLICATION_OBJECT_GLOBALVAR( __s_SteamApplicationObject ) + + +// This defines your steam application object and ties it to your appsystemgroup. +// This does NOT hookup its AppMain to get called. You'll have to call that from startup code +// or use something like DEFINE_LAUNCHABLE_DLL_STEAM_APP() to call it. +// +// _steamApplicationClass derives from CSteamApplication. +// _appClass derives from CAppSystemGroup (.. can derive from - or be - CTier2SteamApp for example). +// +#define DEFINE_CUSTOM_STEAM_APPLICATION_OBJECT( _steamApplicationClassName, _appClassName ) \ + static _appClassName __s_ApplicationObject; \ + static _steamApplicationClassName __s_SteamApplicationObject( &__s_ApplicationObject ); + +#endif // APPFRAMEWORK_H diff --git a/public/appframework/IAppSystem.h b/public/appframework/IAppSystem.h new file mode 100644 index 0000000..5375fb0 --- /dev/null +++ b/public/appframework/IAppSystem.h @@ -0,0 +1,121 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: An application framework +// +// $Revision: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IAPPSYSTEM_H +#define IAPPSYSTEM_H + +#ifdef COMPILER_MSVC +#pragma once +#endif + +#include "tier1/interface.h" +#include "interfaces/interfaces.h" + + +//----------------------------------------------------------------------------- +// Specifies a module + interface name for initialization +//----------------------------------------------------------------------------- +struct AppSystemInfo_t +{ + const char *m_pModuleName; + const char *m_pInterfaceName; +}; + + +//----------------------------------------------------------------------------- +// Client systems are singleton objects in the client codebase responsible for +// various tasks +// The order in which the client systems appear in this list are the +// order in which they are initialized and updated. They are shut down in +// reverse order from which they are initialized. +//----------------------------------------------------------------------------- +enum InitReturnVal_t +{ + INIT_FAILED = 0, + INIT_OK, + + INIT_LAST_VAL, +}; + +enum AppSystemTier_t +{ + APP_SYSTEM_TIER0 = 0, + APP_SYSTEM_TIER1, + APP_SYSTEM_TIER2, + APP_SYSTEM_TIER3, + + APP_SYSTEM_TIER_OTHER, +}; + + +abstract_class IAppSystem +{ +public: + // Here's where the app systems get to learn about each other + virtual bool Connect( CreateInterfaceFn factory ) = 0; + virtual void Disconnect() = 0; + + // Here's where systems can access other interfaces implemented by this object + // Returns NULL if it doesn't implement the requested interface + virtual void *QueryInterface( const char *pInterfaceName ) = 0; + + // Init, shutdown + virtual InitReturnVal_t Init() = 0; + virtual void Shutdown() = 0; + + // Returns all dependent libraries + virtual const AppSystemInfo_t* GetDependencies() = 0; + + // Returns the tier + virtual AppSystemTier_t GetTier() = 0; + + // Reconnect to a particular interface + virtual void Reconnect( CreateInterfaceFn factory, const char *pInterfaceName ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem +//----------------------------------------------------------------------------- +template< class IInterface > +class CBaseAppSystem : public IInterface +{ +public: + // Here's where the app systems get to learn about each other + virtual bool Connect( CreateInterfaceFn factory ) { return true; } + virtual void Disconnect() {} + + // Here's where systems can access other interfaces implemented by this object + // Returns NULL if it doesn't implement the requested interface + virtual void *QueryInterface( const char *pInterfaceName ) { return NULL; } + + // Init, shutdown + virtual InitReturnVal_t Init() { return INIT_OK; } + virtual void Shutdown() {} + + virtual const AppSystemInfo_t* GetDependencies() { return NULL; } + virtual AppSystemTier_t GetTier() { return APP_SYSTEM_TIER_OTHER; } + + virtual void Reconnect( CreateInterfaceFn factory, const char *pInterfaceName ) + { + ReconnectInterface( factory, pInterfaceName ); + } +}; + + +//----------------------------------------------------------------------------- +// Helper implementation of an IAppSystem for tier0 +//----------------------------------------------------------------------------- +template< class IInterface > +class CTier0AppSystem : public CBaseAppSystem< IInterface > +{ +}; + + +#endif // IAPPSYSTEM_H + diff --git a/public/appframework/IAppSystemGroup.h b/public/appframework/IAppSystemGroup.h new file mode 100644 index 0000000..bd9a7f3 --- /dev/null +++ b/public/appframework/IAppSystemGroup.h @@ -0,0 +1,299 @@ +//======== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ======== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: Defines a group of app systems that all have the same lifetime +// that need to be connected/initialized, etc. in a well-defined order +// +// $Revision: $ +// $NoKeywords: $ +//============================================================================= + +#ifndef IAPPSYSTEMGROUP_H +#define IAPPSYSTEMGROUP_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/interface.h" +#include "tier1/utlvector.h" +#include "tier1/utldict.h" +#include "tier1/utlstringmap.h" +#include "IAppSystem.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IAppSystem; +class CSysModule; +class IBaseInterface; +class IFileSystem; + +//----------------------------------------------------------------------------- +// Handle to a DLL +//----------------------------------------------------------------------------- +typedef int AppModule_t; + +enum +{ + APP_MODULE_INVALID = (AppModule_t)~0 +}; + + +//----------------------------------------------------------------------------- +// NOTE: The following methods must be implemented in your application +// although they can be empty implementations if you like... +//----------------------------------------------------------------------------- +abstract_class IAppSystemGroup +{ +public: + // An installed application creation function, you should tell the group + // the DLLs and the singleton interfaces you want to instantiate. + // Return false if there's any problems and the app will abort + virtual bool Create( ) = 0; + + // Allow the application to do some work after AppSystems are connected but + // they are all Initialized. + // Return false if there's any problems and the app will abort + virtual bool PreInit() = 0; + + // Allow the application to do some work after AppSystems are initialized but + // before main is run + // Return false if there's any problems and the app will abort + virtual bool PostInit() = 0; + + // Main loop implemented by the application + virtual int Main( ) = 0; + + // Allow the application to do some work after all AppSystems are shut down + virtual void PreShutdown() = 0; + + // Allow the application to do some work after all AppSystems are shut down + virtual void PostShutdown() = 0; + + // Call an installed application destroy function, occurring after all modules + // are unloaded + virtual void Destroy() = 0; +}; + + +//----------------------------------------------------------------------------- +// This class represents a group of app systems that all have the same lifetime +// that need to be connected/initialized, etc. in a well-defined order +//----------------------------------------------------------------------------- +class CAppSystemGroup : public IAppSystemGroup +{ +public: + // Used to determine where we exited out from the system + enum AppSystemGroupStage_t + { + CREATION = 0, + DEPENDENCIES, + CONNECTION, + PREINITIALIZATION, + INITIALIZATION, + POSTINITIALIZATION, + RUNNING, + PRESHUTDOWN, + SHUTDOWN, + POSTSHUTDOWN, + DISCONNECTION, + DESTRUCTION, + + APPSYSTEM_GROUP_STAGE_COUNT, + }; + +public: + // constructor + CAppSystemGroup( CAppSystemGroup *pParentAppSystem = NULL ); + + // Runs the app system group. + // First, modules are loaded, next they are connected, followed by initialization + // Then Main() is run + // Then modules are shut down, disconnected, and unloaded + int Run( ); + + // Use this version in cases where you can't control the main loop and + // expect to be ticked + virtual int Startup(); + virtual void Shutdown(); + + // Default implementations... + virtual bool PostInit() { return true; } + virtual void PreShutdown() { } + + // Returns the stage at which the app system group ran into an error + AppSystemGroupStage_t GetCurrentStage() const; + +protected: + // These methods are meant to be called by derived classes of CAppSystemGroup + + // Methods to load + unload DLLs + AppModule_t LoadModule( const char *pDLLName ); + AppModule_t LoadModule( CreateInterfaceFn factory ); + + // Method to add various global singleton systems + IAppSystem *AddSystem( AppModule_t module, const char *pInterfaceName ); + void AddSystem( IAppSystem *pAppSystem, const char *pInterfaceName ); + + // Simpler method of doing the LoadModule/AddSystem thing. + // Make sure the last AppSystemInfo has a NULL module name + bool AddSystems( AppSystemInfo_t *pSystems ); + + // Adds a factory to the system so other stuff can query it. Triggers a connect systems + void AddNonAppSystemFactory( CreateInterfaceFn fn ); + + // Removes a factory, triggers a disconnect call if it succeeds + void RemoveNonAppSystemFactory( CreateInterfaceFn fn ); + + // Causes the systems to reconnect to an interface + void ReconnectSystems( const char *pInterfaceName ); + + // Method to look up a particular named system... + void *FindSystem( const char *pInterfaceName ); + + // Creates the app window (windowed app only) + virtual void *CreateAppWindow( void *hInstance, const char *pTitle, bool bWindowed, int w, int h, bool bResizing ); + void SetAppWindowTitle( void* hWnd, const char *pTitle ); + + // Gets at a class factory for the topmost appsystem group in an appsystem stack + static CreateInterfaceFn GetFactory(); + +private: + struct Module_t + { + CSysModule *m_pModule; + CreateInterfaceFn m_Factory; + char *m_pModuleName; + }; + + typedef CUtlStringMap< CUtlSymbolTable > LibraryDependencies_t; + + int OnStartup(); + void OnShutdown(); + + void UnloadAllModules( ); + void RemoveAllSystems(); + + // Method to load all dependent systems + bool LoadDependentSystems(); + + // Method to connect/disconnect all systems + bool ConnectSystems( ); + void DisconnectSystems(); + + // Method to initialize/shutdown all systems + InitReturnVal_t InitSystems(); + void ShutdownSystems(); + + // Gets at the parent appsystem group + CAppSystemGroup *GetParent(); + + // Loads a module the standard way + virtual CSysModule *LoadModuleDLL( const char *pDLLName ); + + void ComputeDependencies( LibraryDependencies_t &depend ); + void SortDependentLibraries( LibraryDependencies_t &depend ); + const char *FindSystemName( int nIndex ); + static bool SortLessFunc( const int &left, const int &right ); + + void ReportStartupFailure( int nErrorStage, int nSysIndex ); + + CUtlVector m_Modules; + CUtlVector m_Systems; + CUtlVector m_NonAppSystemFactories; + CUtlDict m_SystemDict; + CAppSystemGroup *m_pParentAppSystem; + AppSystemGroupStage_t m_nCurrentStage; + + static LibraryDependencies_t *sm_pSortDependencies; + friend void *AppSystemCreateInterfaceFn(const char *pName, int *pReturnCode); + friend class CSteamAppSystemGroup; +}; + + +//----------------------------------------------------------------------------- +// This class represents a group of app systems that are loaded through steam +//----------------------------------------------------------------------------- +class CSteamAppSystemGroup : public CAppSystemGroup +{ +public: + CSteamAppSystemGroup( IFileSystem *pFileSystem = NULL, CAppSystemGroup *pParentAppSystem = NULL ); + + // Used by CSteamApplication to set up necessary pointers if we can't do it in the constructor + void Setup( IFileSystem *pFileSystem, CAppSystemGroup *pParentAppSystem ); + +protected: + // Sets up the search paths + bool SetupSearchPaths( const char *pStartingDir, bool bOnlyUseStartingDir, bool bIsTool ); + + // Returns the game info path. Only works if you've called SetupSearchPaths first + const char *GetGameInfoPath() const; + +private: + virtual CSysModule *LoadModuleDLL( const char *pDLLName ); + + IFileSystem *m_pFileSystem; + char m_pGameInfoPath[ MAX_PATH ]; +}; + + +//----------------------------------------------------------------------------- +// Helper empty decorator implementation of an IAppSystemGroup +//----------------------------------------------------------------------------- +template< class CBaseClass > +class CDefaultAppSystemGroup : public CBaseClass +{ +public: + virtual bool Create( ) { return true; } + virtual bool PreInit() { return true; } + virtual bool PostInit() { return true; } + virtual void PreShutdown() { } + virtual void PostShutdown() {} + virtual void Destroy() {} +}; + + +//----------------------------------------------------------------------------- +// Special helper for game info directory suggestion +//----------------------------------------------------------------------------- + +class CFSSteamSetupInfo; // Forward declaration + +// +// SuggestGameInfoDirFn_t +// Game info suggestion function. +// Provided by the application to possibly detect the suggested game info +// directory and initialize all the game-info-related systems appropriately. +// Parameters: +// pFsSteamSetupInfo steam file system setup information if available. +// pchPathBuffer buffer to hold game info directory path on return. +// nBufferLength length of the provided buffer to hold game info directory path. +// pbBubbleDirectories should contain "true" on return to bubble the directories up searching for game info file. +// Return values: +// Returns "true" if the game info directory path suggestion is available and +// was successfully copied into the provided buffer. +// Returns "false" otherwise, interpreted that no suggestion will be used. +// +typedef bool ( * SuggestGameInfoDirFn_t ) ( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories ); + +// +// SetSuggestGameInfoDirFn +// Installs the supplied game info directory suggestion function. +// Parameters: +// pfnNewFn the new game info directory suggestion function. +// Returns: +// The previously installed suggestion function or NULL if none was installed before. +// This function never fails. +// +SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn ); + + +#endif // APPSYSTEMGROUP_H + + diff --git a/public/appframework/VguiMatSysApp.h b/public/appframework/VguiMatSysApp.h new file mode 100644 index 0000000..a68135d --- /dev/null +++ b/public/appframework/VguiMatSysApp.h @@ -0,0 +1,51 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// Material editor +//============================================================================= + +#ifndef VGUIMATSYSAPP_H +#define VGUIMATSYSAPP_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "appframework/matsysapp.h" + +FORWARD_DECLARE_HANDLE( InputContextHandle_t ); + +//----------------------------------------------------------------------------- +// The application object +//----------------------------------------------------------------------------- +class CVguiMatSysApp : public CMatSysApp +{ + typedef CMatSysApp BaseClass; + +public: + CVguiMatSysApp(); + + // Methods of IApplication + virtual bool Create(); + virtual bool PreInit(); + virtual bool PostInit(); + virtual void PreShutdown(); + virtual void PostShutdown(); + virtual void Destroy(); + + InputContextHandle_t GetAppInputContext(); + +private: + InputContextHandle_t m_hAppInputContext; +}; + + +#endif // VGUIMATSYSAPP_H diff --git a/public/appframework/tier2app.h b/public/appframework/tier2app.h new file mode 100644 index 0000000..3bae40c --- /dev/null +++ b/public/appframework/tier2app.h @@ -0,0 +1,89 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// The application object for apps that use tier2 +//============================================================================= + +#ifndef TIER2APP_H +#define TIER2APP_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "appframework/AppFramework.h" +#include "tier2/tier2dm.h" +#include "tier1/convar.h" + + +//----------------------------------------------------------------------------- +// The application object for apps that use tier2 +//----------------------------------------------------------------------------- +class CTier2SteamApp : public CSteamAppSystemGroup +{ + typedef CSteamAppSystemGroup BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + CreateInterfaceFn factory = GetFactory(); + ConnectTier1Libraries( &factory, 1 ); + ConVar_Register( 0 ); + ConnectTier2Libraries( &factory, 1 ); + return true; + } + + virtual void PostShutdown() + { + DisconnectTier2Libraries(); + ConVar_Unregister(); + DisconnectTier1Libraries(); + } + + virtual void Destroy() + { + } +}; + + +//----------------------------------------------------------------------------- +// The application object for apps that use tier2 and datamodel +//----------------------------------------------------------------------------- +class CTier2DmSteamApp : public CTier2SteamApp +{ + typedef CTier2SteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + if ( !BaseClass::PreInit() ) + return false; + + CreateInterfaceFn factory = GetFactory(); + if ( !ConnectDataModel( factory ) ) + return false; + + InitReturnVal_t nRetVal = InitDataModel(); + return ( nRetVal == INIT_OK ); + } + + virtual void PostShutdown() + { + ShutdownDataModel(); + DisconnectDataModel(); + BaseClass::PostShutdown(); + } +}; + + +#endif // TIER2APP_H diff --git a/public/appframework/tier3app.h b/public/appframework/tier3app.h new file mode 100644 index 0000000..caa6bfc --- /dev/null +++ b/public/appframework/tier3app.h @@ -0,0 +1,121 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// The application objects for apps that use tier3 +//============================================================================= + +#ifndef TIER3APP_H +#define TIER3APP_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "appframework/tier2app.h" +#include "tier3/tier3.h" +#include "vgui_controls/Controls.h" + + +//----------------------------------------------------------------------------- +// The application object for apps that use tier3 +//----------------------------------------------------------------------------- +class CTier3SteamApp : public CTier2SteamApp +{ + typedef CTier2SteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + if ( !BaseClass::PreInit() ) + return false; + + CreateInterfaceFn factory = GetFactory(); + ConnectTier3Libraries( &factory, 1 ); + return true; + } + + virtual void PostShutdown() + { + DisconnectTier3Libraries(); + BaseClass::PostShutdown(); + } +}; + + +//----------------------------------------------------------------------------- +// The application object for apps that use tier3 +//----------------------------------------------------------------------------- +class CTier3DmSteamApp : public CTier2DmSteamApp +{ + typedef CTier2DmSteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + if ( !BaseClass::PreInit() ) + return false; + + CreateInterfaceFn factory = GetFactory(); + ConnectTier3Libraries( &factory, 1 ); + return true; + } + + virtual void PostShutdown() + { + DisconnectTier3Libraries(); + BaseClass::PostShutdown(); + } +}; + + +//----------------------------------------------------------------------------- +// The application object for apps that use vgui +//----------------------------------------------------------------------------- +class CVguiSteamApp : public CTier3SteamApp +{ + typedef CTier3SteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + if ( !BaseClass::PreInit() ) + return false; + + CreateInterfaceFn factory = GetFactory(); + return vgui::VGui_InitInterfacesList( "CVguiSteamApp", &factory, 1 ); + } +}; + + +//----------------------------------------------------------------------------- +// The application object for apps that use vgui +//----------------------------------------------------------------------------- +class CVguiDmSteamApp : public CTier3DmSteamApp +{ + typedef CTier3DmSteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool PreInit() + { + if ( !BaseClass::PreInit() ) + return false; + + CreateInterfaceFn factory = GetFactory(); + return vgui::VGui_InitInterfacesList( "CVguiSteamApp", &factory, 1 ); + } +}; + + +#endif // TIER3APP_H diff --git a/public/arraystack.h b/public/arraystack.h new file mode 100644 index 0000000..7bc5671 --- /dev/null +++ b/public/arraystack.h @@ -0,0 +1,69 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ARRAYSTACK_H +#define ARRAYSTACK_H +#pragma once + +#include +#include "List.h" + +template class ArrayStack +{ +protected: + T *data; + int m_stackDepth; + int m_maxNumElements; + +public: + ArrayStack( int maxNumElements ) + { + data = new T[maxNumElements]; + m_maxNumElements = maxNumElements; + m_stackDepth = 0; + assert( data ); + } + + void Push( T elem ) + { + data[m_stackDepth++] = elem; + if( m_stackDepth > m_maxNumElements ) + { + printf( "ArrayStack overflow\n" ); + assert( 0 ); + } + } + + T Pop( void ) + { + if( m_stackDepth == 0 ) + { + printf( "ArrayStack underflow\n" ); + assert( 0 ); + } + return data[--m_stackDepth]; + } + + bool IsEmpty() + { + return ( m_stackDepth == 0 ); + } + + int GetDepth() + { + return m_stackDepth; + } +}; + + +#endif // ARRAYSTACK_H diff --git a/public/avi/iavi.h b/public/avi/iavi.h new file mode 100644 index 0000000..4c3aaba --- /dev/null +++ b/public/avi/iavi.h @@ -0,0 +1,124 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +//============================================================================= + +#ifndef IAVI_H +#define IAVI_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/iappsystem.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct BGR888_t; +class IMaterial; + + +//----------------------------------------------------------------------------- +// Parameters for creating a new AVI +//----------------------------------------------------------------------------- +struct AVIParams_t +{ + AVIParams_t() : + m_nFrameRate( 0 ), m_nFrameScale( 1 ), m_nWidth( 0 ), m_nHeight( 0 ), + m_nSampleRate( 0 ), m_nSampleBits( 0 ), m_nNumChannels( 0 ) + { + m_pFileName[ 0 ] = 0; + } + + char m_pFileName[ 256 ]; + char m_pPathID[ 256 ]; + + // fps = m_nFrameRate / m_nFrameScale + // for integer framerates, set framerate to the fps, and framescale to 1 + // for ntsc-style framerates like 29.97 (or 23.976 or 59.94), + // set framerate to 30,000 (or 24,000 or 60,000) and framescale to 1001 + // yes, framescale is an odd naming choice, but it matching MS's AVI api + int m_nFrameRate; + int m_nFrameScale; + + int m_nWidth; + int m_nHeight; + + // Sound/.wav info + int m_nSampleRate; + int m_nSampleBits; + int m_nNumChannels; +}; + + +//----------------------------------------------------------------------------- +// Handle to an AVI +//----------------------------------------------------------------------------- +typedef unsigned short AVIHandle_t; +enum +{ + AVIHANDLE_INVALID = (AVIHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Handle to an AVI material +//----------------------------------------------------------------------------- +typedef unsigned short AVIMaterial_t; +enum +{ + AVIMATERIAL_INVALID = (AVIMaterial_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Main AVI interface +//----------------------------------------------------------------------------- +class IAvi : public IAppSystem +{ +public: + // Necessary to call this before any other AVI interface methods + virtual void SetMainWindow( void* hWnd ) = 0; + + // Start/stop recording an AVI + virtual AVIHandle_t StartAVI( const AVIParams_t& params ) = 0; + virtual void FinishAVI( AVIHandle_t handle ) = 0; + + // Add frames to an AVI + virtual void AppendMovieSound( AVIHandle_t h, short *buf, size_t bufsize ) = 0; + virtual void AppendMovieFrame( AVIHandle_t h, const BGR888_t *pRGBData ) = 0; + + // Create/destroy an AVI material (a materialsystem IMaterial) + virtual AVIMaterial_t CreateAVIMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID ) = 0; + virtual void DestroyAVIMaterial( AVIMaterial_t hMaterial ) = 0; + + // Sets the time for an AVI material + virtual void SetTime( AVIMaterial_t hMaterial, float flTime ) = 0; + + // Gets the IMaterial associated with an AVI material + virtual IMaterial* GetMaterial( AVIMaterial_t hMaterial ) = 0; + + // Returns the max texture coordinate of the AVI + virtual void GetTexCoordRange( AVIMaterial_t hMaterial, float *pMaxU, float *pMaxV ) = 0; + + // Returns the frame size of the AVI (stored in a subrect of the material itself) + virtual void GetFrameSize( AVIMaterial_t hMaterial, int *pWidth, int *pHeight ) = 0; + + // Returns the frame rate of the AVI + virtual int GetFrameRate( AVIMaterial_t hMaterial ) = 0; + + // Returns the total frame count of the AVI + virtual int GetFrameCount( AVIMaterial_t hMaterial ) = 0; + + // Sets the frame for an AVI material (use instead of SetTime) + virtual void SetFrame( AVIMaterial_t hMaterial, float flFrame ) = 0; +}; + +extern IAvi *g_pAVI; + +#endif // IAVI_H diff --git a/public/avi/ibik.h b/public/avi/ibik.h new file mode 100644 index 0000000..077b7e4 --- /dev/null +++ b/public/avi/ibik.h @@ -0,0 +1,139 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. ======= +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +//============================================================================= + +#ifndef IBIK_H +#define IBIK_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/iappsystem.h" + +#define BIK_LOOP 0x00000001 // play endlessly +#define BIK_PRELOAD 0x00000002 // causes the entire move to load into memory +#define BIK_NO_AUDIO 0x00000004 // video doesn't have audio + +#define ENABLE_BIK_PERF_SPEW 0 + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct BGR888_t; +class IMaterial; + +//----------------------------------------------------------------------------- +// Parameters for creating a new BINK +//----------------------------------------------------------------------------- +struct BIKParams_t +{ + BIKParams_t() : + m_nFrameRate( 0 ), m_nFrameScale( 1 ), m_nWidth( 0 ), m_nHeight( 0 ), + m_nSampleRate( 0 ), m_nSampleBits( 0 ), m_nNumChannels( 0 ) + { + m_pFileName[ 0 ] = 0; + } + + char m_pFileName[ 256 ]; + char m_pPathID[ 256 ]; + + // fps = m_nFrameRate / m_nFrameScale + // for integer framerates, set framerate to the fps, and framescale to 1 + // for ntsc-style framerates like 29.97 (or 23.976 or 59.94), + // set framerate to 30,000 (or 24,000 or 60,000) and framescale to 1001 + // yes, framescale is an odd naming choice, but it matching MS's AVI api + int m_nFrameRate; + int m_nFrameScale; + + int m_nWidth; + int m_nHeight; + + // Sound/.wav info + int m_nSampleRate; + int m_nSampleBits; + int m_nNumChannels; +}; + +//----------------------------------------------------------------------------- +// Handle to an BINK +//----------------------------------------------------------------------------- +typedef unsigned short BIKHandle_t; +enum +{ + BIKHANDLE_INVALID = (BIKHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Handle to an BINK material +//----------------------------------------------------------------------------- +typedef unsigned short BIKMaterial_t; +enum +{ + BIKMATERIAL_INVALID = (BIKMaterial_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Main AVI interface +//----------------------------------------------------------------------------- +class IBik : public IAppSystem +{ +public: + // Create/destroy a BINK material (a materialsystem IMaterial) + virtual BIKMaterial_t CreateMaterial( const char *pMaterialName, const char *pFileName, const char *pPathID, int flags = 0 ) = 0; + virtual void DestroyMaterial( BIKMaterial_t hMaterial ) = 0; + + // Update the frame (if necessary) + virtual bool Update( BIKMaterial_t hMaterial ) = 0; + + virtual bool ReadyForSwap( BIKMaterial_t hMaterial ) = 0; + + // Gets the IMaterial associated with an BINK material + virtual IMaterial* GetMaterial( BIKMaterial_t hMaterial ) = 0; + + // Returns the max texture coordinate of the BINK + virtual void GetTexCoordRange( BIKMaterial_t hMaterial, float *pMaxU, float *pMaxV ) = 0; + + // Returns the frame size of the BINK (stored in a subrect of the material itself) + virtual void GetFrameSize( BIKMaterial_t hMaterial, int *pWidth, int *pHeight ) = 0; + + // Returns the frame rate of the BINK + virtual int GetFrameRate( BIKMaterial_t hMaterial ) = 0; + + // Returns the total frame count of the BINK + virtual int GetFrameCount( BIKMaterial_t hMaterial ) = 0; + + // Get our current frame + virtual int GetFrame( BIKMaterial_t hMaterial ) = 0; + + // Sets the frame for an BINK material (use instead of SetTime) + virtual void SetFrame( BIKMaterial_t hMaterial, float flFrame ) = 0; + +#ifdef WIN32 +#if !defined( _X360 ) + // Sets the direct sound device that Bink will decode to + virtual bool SetDirectSoundDevice( void *pDevice ) = 0; + virtual bool SetMilesSoundDevice( void *pDevice ) = 0; +#else + //needs to be called after xaudio is initialized + virtual bool HookXAudio( void ) = 0; +#endif +#endif + // Pause and unpause the movie playback + virtual void Pause( BIKMaterial_t hMaterial ) = 0; + virtual void Unpause( BIKMaterial_t hMaterial ) = 0; + + // Number for appending the current material name + virtual int GetGlobalMaterialAllocationNumber( void ) = 0; +}; + +extern IBik *g_pBIK; + +#endif // IBIK_H diff --git a/public/basehandle.h b/public/basehandle.h new file mode 100644 index 0000000..9c30d30 --- /dev/null +++ b/public/basehandle.h @@ -0,0 +1,191 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BASEHANDLE_H +#define BASEHANDLE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "const.h" +#include "tier0/dbg.h" + + +class IHandleEntity; + + +// -------------------------------------------------------------------------------------------------- // +// CBaseHandle. +// -------------------------------------------------------------------------------------------------- // + +class CBaseHandle +{ +friend class CBaseEntityList; + +public: + + CBaseHandle(); + CBaseHandle( const CBaseHandle &other ); + CBaseHandle( unsigned long value ); + CBaseHandle( int iEntry, int iSerialNumber ); + + void Init( int iEntry, int iSerialNumber ); + void Term(); + + // Even if this returns true, Get() still can return return a non-null value. + // This just tells if the handle has been initted with any values. + bool IsValid() const; + + int GetEntryIndex() const; + int GetSerialNumber() const; + + int ToInt() const; + bool operator !=( const CBaseHandle &other ) const; + bool operator ==( const CBaseHandle &other ) const; + bool operator ==( const IHandleEntity* pEnt ) const; + bool operator !=( const IHandleEntity* pEnt ) const; + bool operator <( const CBaseHandle &other ) const; + bool operator <( const IHandleEntity* pEnt ) const; + + // Assign a value to the handle. + const CBaseHandle& operator=( const IHandleEntity *pEntity ); + const CBaseHandle& Set( const IHandleEntity *pEntity ); + + // Use this to dereference the handle. + // Note: this is implemented in game code (ehandle.h) + IHandleEntity* Get() const; + + +protected: + // The low NUM_SERIAL_BITS hold the index. If this value is less than MAX_EDICTS, then the entity is networkable. + // The high NUM_SERIAL_NUM_BITS bits are the serial number. + unsigned long m_Index; +}; + + +#include "ihandleentity.h" + + +inline CBaseHandle::CBaseHandle() +{ + m_Index = INVALID_EHANDLE_INDEX; +} + +inline CBaseHandle::CBaseHandle( const CBaseHandle &other ) +{ + m_Index = other.m_Index; +} + +inline CBaseHandle::CBaseHandle( unsigned long value ) +{ + m_Index = value; +} + +inline CBaseHandle::CBaseHandle( int iEntry, int iSerialNumber ) +{ + Init( iEntry, iSerialNumber ); +} + +inline void CBaseHandle::Init( int iEntry, int iSerialNumber ) +{ + Assert( iEntry >= 0 && (iEntry & ENT_ENTRY_MASK) == iEntry); + Assert( iSerialNumber >= 0 && iSerialNumber < (1 << NUM_SERIAL_NUM_BITS) ); + + m_Index = iEntry | (iSerialNumber << NUM_SERIAL_NUM_SHIFT_BITS); +} + +inline void CBaseHandle::Term() +{ + m_Index = INVALID_EHANDLE_INDEX; +} + +inline bool CBaseHandle::IsValid() const +{ + return m_Index != INVALID_EHANDLE_INDEX; +} + +inline int CBaseHandle::GetEntryIndex() const +{ + // There is a hack here: due to a bug in the original implementation of the + // entity handle system, an attempt to look up an invalid entity index in + // certain cirumstances might fall through to the the mask operation below. + // This would mask an invalid index to be in fact a lookup of entity number + // NUM_ENT_ENTRIES, so invalid ent indexes end up actually looking up the + // last slot in the entities array. Since this slot is always empty, the + // lookup returns NULL and the expected behavior occurs through this unexpected + // route. + // A lot of code actually depends on this behavior, and the bug was only exposed + // after a change to NUM_SERIAL_NUM_BITS increased the number of allowable + // static props in the world. So the if-stanza below detects this case and + // retains the prior (bug-submarining) behavior. + if ( !IsValid() ) + return NUM_ENT_ENTRIES-1; + return m_Index & ENT_ENTRY_MASK; +} + +inline int CBaseHandle::GetSerialNumber() const +{ + return m_Index >> NUM_SERIAL_NUM_SHIFT_BITS; +} + +inline int CBaseHandle::ToInt() const +{ + return (int)m_Index; +} + +inline bool CBaseHandle::operator !=( const CBaseHandle &other ) const +{ + return m_Index != other.m_Index; +} + +inline bool CBaseHandle::operator ==( const CBaseHandle &other ) const +{ + return m_Index == other.m_Index; +} + +inline bool CBaseHandle::operator ==( const IHandleEntity* pEnt ) const +{ + return Get() == pEnt; +} + +inline bool CBaseHandle::operator !=( const IHandleEntity* pEnt ) const +{ + return Get() != pEnt; +} + +inline bool CBaseHandle::operator <( const CBaseHandle &other ) const +{ + return m_Index < other.m_Index; +} + +inline bool CBaseHandle::operator <( const IHandleEntity *pEntity ) const +{ + unsigned long otherIndex = (pEntity) ? pEntity->GetRefEHandle().m_Index : INVALID_EHANDLE_INDEX; + return m_Index < otherIndex; +} + +inline const CBaseHandle& CBaseHandle::operator=( const IHandleEntity *pEntity ) +{ + return Set( pEntity ); +} + +inline const CBaseHandle& CBaseHandle::Set( const IHandleEntity *pEntity ) +{ + if ( pEntity ) + { + *this = pEntity->GetRefEHandle(); + } + else + { + m_Index = INVALID_EHANDLE_INDEX; + } + + return *this; +} + + +#endif // BASEHANDLE_H diff --git a/public/bitmap/bitmap.h b/public/bitmap/bitmap.h new file mode 100644 index 0000000..32d1e7f --- /dev/null +++ b/public/bitmap/bitmap.h @@ -0,0 +1,107 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef BITMAP_H +#define BITMAP_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "bitmap/imageformat.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; + + +//----------------------------------------------------------------------------- +// A Bitmap +//----------------------------------------------------------------------------- +struct Bitmap_t +{ + Bitmap_t(); + ~Bitmap_t(); + void Init( int nWidth, int nHeight, ImageFormat imageFormat ); + unsigned char *GetPixel( int x, int y ); + + int m_nWidth; + int m_nHeight; + ImageFormat m_ImageFormat; + unsigned char *m_pBits; +}; + +inline Bitmap_t::Bitmap_t() +{ + m_nWidth = 0; + m_nHeight = 0; + m_ImageFormat = IMAGE_FORMAT_UNKNOWN; + m_pBits = NULL; +} + +inline Bitmap_t::~Bitmap_t() +{ + if ( m_pBits ) + { + delete[] m_pBits; + m_pBits = NULL; + } +} + +inline void Bitmap_t::Init( int nWidth, int nHeight, ImageFormat imageFormat ) +{ + if ( m_pBits ) + { + delete[] m_pBits; + m_pBits = NULL; + } + + m_nWidth = nWidth; + m_nHeight = nHeight; + m_ImageFormat = imageFormat; + m_pBits = new unsigned char[ nWidth * nHeight * ImageLoader::SizeInBytes( m_ImageFormat ) ]; +} + +inline unsigned char *Bitmap_t::GetPixel( int x, int y ) +{ + if ( !m_pBits ) + return NULL; + + int nPixelSize = ImageLoader::SizeInBytes( m_ImageFormat ); + return &m_pBits[ ( m_nWidth * y + x ) * nPixelSize ]; +} + + +//----------------------------------------------------------------------------- +// Loads a bitmap from an arbitrary file: could be a TGA, PSD, or PFM. +// LoadBitmap autodetects which type, and returns it +//----------------------------------------------------------------------------- +enum BitmapFileType_t +{ + BITMAP_FILE_TYPE_UNKNOWN = -1, + BITMAP_FILE_TYPE_PSD = 0, + BITMAP_FILE_TYPE_TGA, + BITMAP_FILE_TYPE_PFM, +}; + +BitmapFileType_t LoadBitmapFile( CUtlBuffer &buf, Bitmap_t *pBitmap ); + + +//----------------------------------------------------------------------------- +// PFM file loading related methods +//----------------------------------------------------------------------------- +bool PFMReadFileR32F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale ); +bool PFMReadFileRGB323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale ); +bool PFMReadFileRGBA32323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale ); +bool PFMGetInfo_AndAdvanceToTextureBits( CUtlBuffer &pfmBuffer, int &nWidth, int &nHeight, ImageFormat &imageFormat ); + + +#endif // BITMAP_H diff --git a/public/bitmap/colorformat.h b/public/bitmap/colorformat.h new file mode 100644 index 0000000..cfa23b4 --- /dev/null +++ b/public/bitmap/colorformat.h @@ -0,0 +1,146 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef COLORFORMAT_H +#define COLORFORMAT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "mathlib/vector.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector4d.h" +#include "mathlib/vertexcolor.h" + +enum ColorFormat_t +{ + COLOR_FORMAT_UNKNOWN = 0, + COLOR_FORMAT_R32G32B32A32_TYPELESS = 1, + COLOR_FORMAT_R32G32B32A32_FLOAT = 2, + COLOR_FORMAT_R32G32B32A32_UINT = 3, + COLOR_FORMAT_R32G32B32A32_SINT = 4, + COLOR_FORMAT_R32G32B32_TYPELESS = 5, + COLOR_FORMAT_R32G32B32_FLOAT = 6, + COLOR_FORMAT_R32G32B32_UINT = 7, + COLOR_FORMAT_R32G32B32_SINT = 8, + COLOR_FORMAT_R16G16B16A16_TYPELESS = 9, + COLOR_FORMAT_R16G16B16A16_FLOAT = 10, + COLOR_FORMAT_R16G16B16A16_UNORM = 11, + COLOR_FORMAT_R16G16B16A16_UINT = 12, + COLOR_FORMAT_R16G16B16A16_SNORM = 13, + COLOR_FORMAT_R16G16B16A16_SINT = 14, + COLOR_FORMAT_R32G32_TYPELESS = 15, + COLOR_FORMAT_R32G32_FLOAT = 16, + COLOR_FORMAT_R32G32_UINT = 17, + COLOR_FORMAT_R32G32_SINT = 18, + COLOR_FORMAT_R32G8X24_TYPELESS = 19, + COLOR_FORMAT_D32_FLOAT_S8X24_UINT = 20, + COLOR_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + COLOR_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + COLOR_FORMAT_R10G10B10A2_TYPELESS = 23, + COLOR_FORMAT_R10G10B10A2_UNORM = 24, + COLOR_FORMAT_R10G10B10A2_UINT = 25, + COLOR_FORMAT_R11G11B10_FLOAT = 26, + COLOR_FORMAT_R8G8B8A8_TYPELESS = 27, + COLOR_FORMAT_R8G8B8A8_UNORM = 28, + COLOR_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + COLOR_FORMAT_R8G8B8A8_UINT = 30, + COLOR_FORMAT_R8G8B8A8_SNORM = 31, + COLOR_FORMAT_R8G8B8A8_SINT = 32, + COLOR_FORMAT_R16G16_TYPELESS = 33, + COLOR_FORMAT_R16G16_FLOAT = 34, + COLOR_FORMAT_R16G16_UNORM = 35, + COLOR_FORMAT_R16G16_UINT = 36, + COLOR_FORMAT_R16G16_SNORM = 37, + COLOR_FORMAT_R16G16_SINT = 38, + COLOR_FORMAT_R32_TYPELESS = 39, + COLOR_FORMAT_D32_FLOAT = 40, + COLOR_FORMAT_R32_FLOAT = 41, + COLOR_FORMAT_R32_UINT = 42, + COLOR_FORMAT_R32_SINT = 43, + COLOR_FORMAT_R24G8_TYPELESS = 44, + COLOR_FORMAT_D24_UNORM_S8_UINT = 45, + COLOR_FORMAT_R24_UNORM_X8_TYPELESS = 46, + COLOR_FORMAT_X24_TYPELESS_G8_UINT = 47, + COLOR_FORMAT_R8G8_TYPELESS = 48, + COLOR_FORMAT_R8G8_UNORM = 49, + COLOR_FORMAT_R8G8_UINT = 50, + COLOR_FORMAT_R8G8_SNORM = 51, + COLOR_FORMAT_R8G8_SINT = 52, + COLOR_FORMAT_R16_TYPELESS = 53, + COLOR_FORMAT_R16_FLOAT = 54, + COLOR_FORMAT_D16_UNORM = 55, + COLOR_FORMAT_R16_UNORM = 56, + COLOR_FORMAT_R16_UINT = 57, + COLOR_FORMAT_R16_SNORM = 58, + COLOR_FORMAT_R16_SINT = 59, + COLOR_FORMAT_R8_TYPELESS = 60, + COLOR_FORMAT_R8_UNORM = 61, + COLOR_FORMAT_R8_UINT = 62, + COLOR_FORMAT_R8_SNORM = 63, + COLOR_FORMAT_R8_SINT = 64, + COLOR_FORMAT_A8_UNORM = 65, + COLOR_FORMAT_R1_UNORM = 66, + COLOR_FORMAT_R9G9B9E5_SHAREDEXP = 67, + COLOR_FORMAT_R8G8_B8G8_UNORM = 68, + COLOR_FORMAT_G8R8_G8B8_UNORM = 69, + COLOR_FORMAT_BC1_TYPELESS = 70, + COLOR_FORMAT_BC1_UNORM = 71, + COLOR_FORMAT_BC1_UNORM_SRGB = 72, + COLOR_FORMAT_BC2_TYPELESS = 73, + COLOR_FORMAT_BC2_UNORM = 74, + COLOR_FORMAT_BC2_UNORM_SRGB = 75, + COLOR_FORMAT_BC3_TYPELESS = 76, + COLOR_FORMAT_BC3_UNORM = 77, + COLOR_FORMAT_BC3_UNORM_SRGB = 78, + COLOR_FORMAT_BC4_TYPELESS = 79, + COLOR_FORMAT_BC4_UNORM = 80, + COLOR_FORMAT_BC4_SNORM = 81, + COLOR_FORMAT_BC5_TYPELESS = 82, + COLOR_FORMAT_BC5_UNORM = 83, + COLOR_FORMAT_BC5_SNORM = 84, + COLOR_FORMAT_B5G6R5_UNORM = 85, + COLOR_FORMAT_B5G5R5A1_UNORM = 86, + COLOR_FORMAT_B8G8R8A8_UNORM = 87, + COLOR_FORMAT_B8G8R8X8_UNORM = 88, + COLOR_FORMAT_FORCE_UINT = 0xffffffffUL, +}; + +template < class T > inline ColorFormat_t ComputeColorFormat( T* ) +{ + return COLOR_FORMAT_UNKNOWN; +} + +template <> inline ColorFormat_t ComputeColorFormat( float32* ) +{ + return COLOR_FORMAT_R32_FLOAT; +} + +template <> inline ColorFormat_t ComputeColorFormat( Vector2D* ) +{ + return COLOR_FORMAT_R32G32_FLOAT; +} + +template <> inline ColorFormat_t ComputeColorFormat( Vector* ) +{ + return COLOR_FORMAT_R32G32B32_FLOAT; +} + +template <> inline ColorFormat_t ComputeColorFormat( Vector4D* ) +{ + return COLOR_FORMAT_R32G32B32A32_FLOAT; +} + +template <> inline ColorFormat_t ComputeColorFormat( VertexColor_t* ) +{ + // FIXME: How to get at SRGB formats? + return COLOR_FORMAT_R8G8B8A8_UNORM; +} + + +#endif // COLORFORMAT_H diff --git a/public/bitmap/cubemap.h b/public/bitmap/cubemap.h new file mode 100644 index 0000000..1d67253 --- /dev/null +++ b/public/bitmap/cubemap.h @@ -0,0 +1,68 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: a class for performing cube-mapped spherical sample lookups. +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef CUBEMAP_H +#define CUBEMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier1/utlmemory.h" +#include "mathlib/mathlib.h" + +template struct CCubeMap +{ + T m_Samples[6][RES][RES]; + +public: + FORCEINLINE void GetCoords( Vector const &vecNormalizedDirection, int &nX, int &nY, int &nFace ) + { + // find largest magnitude component + int nLargest = 0; + int nAxis0 = 1; + int nAxis1 = 2; + if ( fabs( vecNormalizedDirection[1] ) > fabs( vecNormalizedDirection[0] ) ) + { + nLargest = 1; + nAxis0 = 0; + nAxis1 = 2; + } + if ( fabs( vecNormalizedDirection[2] ) > fabs( vecNormalizedDirection[nLargest] ) ) + { + nLargest = 2; + nAxis0 = 0; + nAxis1 = 1; + } + float flZ = vecNormalizedDirection[nLargest]; + if ( flZ < 0 ) + { + flZ = - flZ; + nLargest += 3; + } + nFace = nLargest; + flZ = 1.0 / flZ; + nX = RemapValClamped( vecNormalizedDirection[nAxis0] * flZ, -1, 1, 0, RES - 1 ); + nY = RemapValClamped( vecNormalizedDirection[nAxis1] * flZ, -1, 1, 0, RES - 1 ); + } + + FORCEINLINE T & GetSample( Vector const &vecNormalizedDirection ) + { + int nX, nY, nFace; + GetCoords( vecNormalizedDirection, nX, nY, nFace ); + return m_Samples[nFace][nX][nY]; + } +}; + + + + + +#endif // CUBEMAP_H diff --git a/public/bitmap/imageformat.h b/public/bitmap/imageformat.h new file mode 100644 index 0000000..f957248 --- /dev/null +++ b/public/bitmap/imageformat.h @@ -0,0 +1,783 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IMAGEFORMAT_H +#define IMAGEFORMAT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier0/platform.h" +#include "tier0/dbg.h" + +enum NormalDecodeMode_t +{ + NORMAL_DECODE_NONE = 0, + NORMAL_DECODE_ATI2N = 1, + NORMAL_DECODE_ATI2N_ALPHA = 2 +}; + +// Forward declaration +#ifdef _WIN32 +typedef enum _D3DFORMAT D3DFORMAT; +typedef enum DXGI_FORMAT; +#endif + +//----------------------------------------------------------------------------- +// The various image format types +//----------------------------------------------------------------------------- + +// don't bitch that inline functions aren't used!!!! +#pragma warning(disable : 4514) + +enum ImageFormat +{ + IMAGE_FORMAT_UNKNOWN = -1, + IMAGE_FORMAT_RGBA8888 = 0, + IMAGE_FORMAT_ABGR8888, + IMAGE_FORMAT_RGB888, + IMAGE_FORMAT_BGR888, + IMAGE_FORMAT_RGB565, + IMAGE_FORMAT_I8, + IMAGE_FORMAT_IA88, + IMAGE_FORMAT_P8, + IMAGE_FORMAT_A8, + IMAGE_FORMAT_RGB888_BLUESCREEN, + IMAGE_FORMAT_BGR888_BLUESCREEN, + IMAGE_FORMAT_ARGB8888, + IMAGE_FORMAT_BGRA8888, + IMAGE_FORMAT_DXT1, + IMAGE_FORMAT_DXT3, + IMAGE_FORMAT_DXT5, + IMAGE_FORMAT_BGRX8888, + IMAGE_FORMAT_BGR565, + IMAGE_FORMAT_BGRX5551, + IMAGE_FORMAT_BGRA4444, + IMAGE_FORMAT_DXT1_ONEBITALPHA, + IMAGE_FORMAT_BGRA5551, + IMAGE_FORMAT_UV88, + IMAGE_FORMAT_UVWQ8888, + IMAGE_FORMAT_RGBA16161616F, + IMAGE_FORMAT_RGBA16161616, + IMAGE_FORMAT_UVLX8888, + IMAGE_FORMAT_R32F, // Single-channel 32-bit floating point + IMAGE_FORMAT_RGB323232F, // NOTE: D3D9 does not have this format + IMAGE_FORMAT_RGBA32323232F, + IMAGE_FORMAT_RG1616F, + IMAGE_FORMAT_RG3232F, + IMAGE_FORMAT_RGBX8888, + + IMAGE_FORMAT_NULL, // Dummy format which takes no video memory + + // Compressed normal map formats + IMAGE_FORMAT_ATI2N, // One-surface ATI2N / DXN format + IMAGE_FORMAT_ATI1N, // Two-surface ATI1N format + + IMAGE_FORMAT_RGBA1010102, // 10 bit-per component render targets + IMAGE_FORMAT_BGRA1010102, + IMAGE_FORMAT_R16F, // 16 bit FP format + + // Depth-stencil texture formats + IMAGE_FORMAT_D16, + IMAGE_FORMAT_D15S1, + IMAGE_FORMAT_D32, + IMAGE_FORMAT_D24S8, + IMAGE_FORMAT_LINEAR_D24S8, + IMAGE_FORMAT_D24X8, + IMAGE_FORMAT_D24X4S4, + IMAGE_FORMAT_D24FS8, + IMAGE_FORMAT_D16_SHADOW, // Specific formats for shadow mapping + IMAGE_FORMAT_D24X8_SHADOW, // Specific formats for shadow mapping + + // supporting these specific formats as non-tiled for procedural cpu access (360-specific) + IMAGE_FORMAT_LINEAR_BGRX8888, + IMAGE_FORMAT_LINEAR_RGBA8888, + IMAGE_FORMAT_LINEAR_ABGR8888, + IMAGE_FORMAT_LINEAR_ARGB8888, + IMAGE_FORMAT_LINEAR_BGRA8888, + IMAGE_FORMAT_LINEAR_RGB888, + IMAGE_FORMAT_LINEAR_BGR888, + IMAGE_FORMAT_LINEAR_BGRX5551, + IMAGE_FORMAT_LINEAR_I8, + IMAGE_FORMAT_LINEAR_RGBA16161616, + + IMAGE_FORMAT_LE_BGRX8888, + IMAGE_FORMAT_LE_BGRA8888, + + NUM_IMAGE_FORMATS +}; + +#if defined( POSIX ) || defined( DX_TO_GL_ABSTRACTION ) +typedef enum _D3DFORMAT + { + D3DFMT_INDEX16, + D3DFMT_D16, + D3DFMT_D24S8, + D3DFMT_A8R8G8B8, + D3DFMT_A4R4G4B4, + D3DFMT_X8R8G8B8, + D3DFMT_R5G6R5, + D3DFMT_X1R5G5B5, + D3DFMT_A1R5G5B5, + D3DFMT_L8, + D3DFMT_A8L8, + D3DFMT_A, + D3DFMT_DXT1, + D3DFMT_DXT3, + D3DFMT_DXT5, + D3DFMT_V8U8, + D3DFMT_Q8W8V8U8, + D3DFMT_X8L8V8U8, + D3DFMT_A16B16G16R16F, + D3DFMT_A16B16G16R16, + D3DFMT_R32F, + D3DFMT_A32B32G32R32F, + D3DFMT_R8G8B8, + D3DFMT_D24X4S4, + D3DFMT_A8, + D3DFMT_R5G6B5, + D3DFMT_D15S1, + D3DFMT_D24X8, + D3DFMT_VERTEXDATA, + D3DFMT_INDEX32, + + // adding fake D3D format names for the vendor specific ones (eases debugging/logging) + + // NV shadow depth tex + D3DFMT_NV_INTZ = 0x5a544e49, // MAKEFOURCC('I','N','T','Z') + D3DFMT_NV_RAWZ = 0x5a574152, // MAKEFOURCC('R','A','W','Z') + + // NV null tex + D3DFMT_NV_NULL = 0x4c4c554e, // MAKEFOURCC('N','U','L','L') + + // ATI shadow depth tex + D3DFMT_ATI_D16 = 0x36314644, // MAKEFOURCC('D','F','1','6') + D3DFMT_ATI_D24S8 = 0x34324644, // MAKEFOURCC('D','F','2','4') + + // ATI 1N and 2N compressed tex + D3DFMT_ATI_2N = 0x32495441, // MAKEFOURCC('A', 'T', 'I', '2') + D3DFMT_ATI_1N = 0x31495441, // MAKEFOURCC('A', 'T', 'I', '1') + + D3DFMT_UNKNOWN + } D3DFORMAT; +#endif + +//----------------------------------------------------------------------------- +// Color structures +//----------------------------------------------------------------------------- +#pragma warning ( disable : 4293 ) +template< int nBitCount > FORCEINLINE int ConvertTo10Bit( int x ) +{ + switch ( nBitCount ) + { + case 1: + return ( x & 0x1 ) ? 0x3FF : 0; + case 2: + return ( x << 8 ) | ( x << 6 ) | ( x << 4 ) | ( x << 2 ) | x; + case 3: + return ( x << 7 ) | ( x << 4 ) | ( x << 1 ) | ( x >> 2 ); + case 4: + return ( x << 6 ) | ( x << 2 ) | ( x >> 2 ); + default: + return ( x << ( 10 - nBitCount ) ) | ( x >> ( nBitCount - ( 10 - nBitCount ) ) ); + } +} +#pragma warning ( default : 4293 ) + +template< int nBitCount > FORCEINLINE int ConvertFrom10Bit( int x ) +{ + return ( x >> ( 10 - nBitCount ) ); +} + +struct R32F_t +{ + float32 r; +}; + +struct RG3232F_t +{ + float32 r; + float32 g; +}; + +struct RGB323232F_t +{ + float32 r; + float32 g; + float32 b; +}; + +struct RGBA32323232F_t +{ + float32 r; + float32 g; + float32 b; + float32 a; +}; + +struct RGBA1010102_t +{ + uint32 r : 10; + uint32 g : 10; + uint32 b : 10; + uint32 a : 2; + inline int RTo10Bit( ) const { return ConvertTo10Bit<10>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<10>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<10>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<2>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<10>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<10>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<10>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<10>( a10 ); } +}; + +struct BGRA1010102_t +{ + uint32 b : 10; + uint32 g : 10; + uint32 r : 10; + uint32 a : 2; + inline int RTo10Bit( ) const { return ConvertTo10Bit<10>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<10>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<10>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<2>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<10>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<10>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<10>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<10>( a10 ); } +}; + +struct BGRA8888_t +{ + uint8 b; // change the order of names to change the + uint8 g; // order of the output ARGB or BGRA, etc... + uint8 r; // Last one is MSB, 1st is LSB. + uint8 a; + inline BGRA8888_t& operator=( const BGRA8888_t& in ) + { + *( unsigned int * )this = *( unsigned int * )∈ + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<8>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<8>( a10 ); } +}; + +struct ABGR8888_t +{ + uint8 a; + uint8 b; // change the order of names to change the + uint8 g; // order of the output ARGB or BGRA, etc... + uint8 r; // Last one is MSB, 1st is LSB. + inline ABGR8888_t& operator=( const BGRA8888_t& in ) + { + r = in.r; + g = in.g; + b = in.b; + a = in.a; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<8>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<8>( a10 ); } +}; + + +struct RGBA8888_t +{ + uint8 r; // change the order of names to change the + uint8 g; // order of the output ARGB or BGRA, etc... + uint8 b; // Last one is MSB, 1st is LSB. + uint8 a; + inline RGBA8888_t& operator=( const BGRA8888_t& in ) + { + r = in.r; + g = in.g; + b = in.b; + a = in.a; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<8>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<8>( a10 ); } +}; + +struct RGB888_t +{ + uint8 r; + uint8 g; + uint8 b; + inline RGB888_t& operator=( const BGRA8888_t& in ) + { + r = in.r; + g = in.g; + b = in.b; + return *this; + } + inline bool operator==( const RGB888_t& in ) const + { + return ( r == in.r ) && ( g == in.g ) && ( b == in.b ); + } + inline bool operator!=( const RGB888_t& in ) const + { + return ( r != in.r ) || ( g != in.g ) || ( b != in.b ); + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } +}; + +struct BGR888_t +{ + uint8 b; + uint8 g; + uint8 r; + inline BGR888_t& operator=( const BGRA8888_t& in ) + { + r = in.r; + g = in.g; + b = in.b; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } +}; + +struct BGRX8888_t +{ + uint8 b; + uint8 g; + uint8 r; + uint8 x; + inline BGRX8888_t& operator=( const BGRA8888_t& in ) + { + r = in.r; + g = in.g; + b = in.b; + x = 255; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<8>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<8>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<8>( b ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<8>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<8>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<8>( b10 ); } +}; + + +// 360 uses this structure for x86 dxt decoding +#if defined( _X360 ) +#pragma bitfield_order( push, lsb_to_msb ) +#endif +struct BGR565_t +{ + uint16 b : 5; // order of names changes + uint16 g : 6; // byte order of output to 32 bit + uint16 r : 5; + inline BGR565_t& operator=( const BGRA8888_t& in ) + { + r = in.r >> 3; + g = in.g >> 2; + b = in.b >> 3; + return *this; + } + inline BGR565_t &Set( int red, int green, int blue ) + { + r = red >> 3; + g = green >> 2; + b = blue >> 3; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<5>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<6>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<5>( b ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<5>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<6>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<5>( b10 ); } +}; +#if defined( _X360 ) +#pragma bitfield_order( pop ) +#endif + +struct BGRA5551_t +{ + uint16 b : 5; // order of names changes + uint16 g : 5; // byte order of output to 32 bit + uint16 r : 5; + uint16 a : 1; + inline BGRA5551_t& operator=( const BGRA8888_t& in ) + { + r = in.r >> 3; + g = in.g >> 3; + b = in.b >> 3; + a = in.a >> 7; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<5>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<5>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<5>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<1>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<5>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<5>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<5>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<1>( a10 ); } +}; + +struct BGRA4444_t +{ + uint16 b : 4; // order of names changes + uint16 g : 4; // byte order of output to 32 bit + uint16 r : 4; + uint16 a : 4; + inline BGRA4444_t& operator=( const BGRA8888_t& in ) + { + r = in.r >> 4; + g = in.g >> 4; + b = in.b >> 4; + a = in.a >> 4; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<4>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<4>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<4>( b ); } + inline int ATo10Bit( ) const { return ConvertTo10Bit<4>( a ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<4>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<4>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<4>( b10 ); } + inline void AFrom10Bit( int a10 ) { a = ConvertFrom10Bit<4>( a10 ); } +}; + +struct RGBX5551_t +{ + uint16 r : 5; + uint16 g : 5; + uint16 b : 5; + uint16 x : 1; + inline RGBX5551_t& operator=( const BGRA8888_t& in ) + { + r = in.r >> 3; + g = in.g >> 3; + b = in.b >> 3; + return *this; + } + inline int RTo10Bit( ) const { return ConvertTo10Bit<5>( r ); } + inline int GTo10Bit( ) const { return ConvertTo10Bit<5>( g ); } + inline int BTo10Bit( ) const { return ConvertTo10Bit<5>( b ); } + inline void RFrom10Bit( int r10 ) { r = ConvertFrom10Bit<5>( r10 ); } + inline void GFrom10Bit( int g10 ) { g = ConvertFrom10Bit<5>( g10 ); } + inline void BFrom10Bit( int b10 ) { b = ConvertFrom10Bit<5>( b10 ); } +}; + +struct UV88_t +{ + int8 u; + int8 v; +}; + +struct UVWQ8888_t +{ + int8 u; + int8 v; + int8 w; + int8 q; +}; + +struct UVLX8888_t +{ + int8 u; + int8 v; + uint8 l; + uint8 x; +}; + +//----------------------------------------------------------------------------- +// some important constants +//----------------------------------------------------------------------------- +#define ARTWORK_GAMMA ( 2.2f ) +#define IMAGE_MAX_DIM ( 2048 ) + + +//----------------------------------------------------------------------------- +// information about each image format +//----------------------------------------------------------------------------- +struct ImageFormatInfo_t +{ + const char* m_pName; + uint8 m_nNumBytes; + uint8 m_nNumRedBits; + uint8 m_nNumGreenBits; + uint8 m_nNumBlueBits; + uint8 m_nNumAlphaBits; + uint8 m_nNumDepthBits; + uint8 m_nNumStencilBits; + bool m_bIsCompressed:1; + bool m_bIsFloat:1; + bool m_bIsDepthFormat : 1; +}; + + +//----------------------------------------------------------------------------- +// Computes nice filters for a compression ratio +//----------------------------------------------------------------------------- +struct KernelInfo_t +{ + float *m_pKernel; + float *m_pInvKernel; + int m_nWidth; + int m_nHeight; + int m_nDepth; + int m_nDiameter; +}; + + +//----------------------------------------------------------------------------- +// Various methods related to pixelmaps and color formats +//----------------------------------------------------------------------------- +namespace ImageLoader +{ + bool GetInfo( const char *fileName, int *width, int *height, enum ImageFormat *imageFormat, float *sourceGamma ); + + // Generates a nice filter kernel + void ComputeNiceFilterKernel( float flWRatio, float flHRatio, float flDRatio, KernelInfo_t *pKernel ); + void CleanupNiceFilterKernel( KernelInfo_t *pKernel ); + + // adjustedheight received the height adjusted for compression (/4 for dxt) + int GetMemRequired( int width, int height, int depth, ImageFormat imageFormat, bool mipmap, int *pAdjustedHeightOut = NULL ); + int GetMemRequired( int width, int height, int depth, int nMipmapCount, ImageFormat imageFormat, int *pAdjustedHeightOut = NULL ); + + // This version is for mipmaps which are stored biggest level to smallest level in memory + int GetMipMapLevelByteOffset( int width, int height, ImageFormat imageFormat, int skipMipLevels, int nDepth = 1 ); + + // This version is for mipmaps which are stored smallest level to largest level in memory + int GetMipMapLevelByteOffsetReverse( int nWidth, int nHeight, int nDepth, int nTotalMipCount, ImageFormat imageFormat, int nMipLevel ); + + void GetMipMapLevelDimensions( int *width, int *height, int skipMipLevels ); + void GetMipMapLevelDimensions( int &nWidth, int &nHeight, int &nDepth, int nMipLevel ); + int GetNumMipMapLevels( int width, int height, int depth = 1 ); + bool Load( unsigned char *imageData, const char *fileName, int width, int height, ImageFormat imageFormat, float targetGamma, bool mipmap ); + bool Load( unsigned char *imageData, FILE *fp, int width, int height, + enum ImageFormat imageFormat, float targetGamma, bool mipmap ); + + // convert from any image format to any other image format. + // return false if the conversion cannot be performed. + // Strides denote the number of bytes per each line, + // by default assumes width * # of bytes per pixel + bool ConvertImageFormat( const unsigned char *src, enum ImageFormat srcImageFormat, + unsigned char *dst, enum ImageFormat dstImageFormat, + int width, int height, int srcStride = 0, int dstStride = 0 ); + + // must be used in conjunction with ConvertImageFormat() to pre-swap and post-swap + void PreConvertSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width = 0, int stride = 0 ); + void PostConvertSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width = 0, int stride = 0 ); + void ByteSwapImageData( unsigned char *pImageData, int nImageSize, ImageFormat imageFormat, int width = 0, int stride = 0 ); + bool IsFormatValidForConversion( ImageFormat fmt ); + + //----------------------------------------------------------------------------- + // convert back and forth from D3D format to ImageFormat, regardless of + // whether it's supported or not + // FIXME: Move into shaderapidx8/rendersystemdx9 + //----------------------------------------------------------------------------- + ImageFormat D3DFormatToImageFormat( D3DFORMAT format ); + D3DFORMAT ImageFormatToD3DFormat( ImageFormat format ); + + // Flags for ResampleRGBA8888 + enum + { + RESAMPLE_NORMALMAP = 0x1, + RESAMPLE_ALPHATEST = 0x2, + RESAMPLE_NICE_FILTER = 0x4, + RESAMPLE_CLAMPS = 0x8, + RESAMPLE_CLAMPT = 0x10, + RESAMPLE_CLAMPU = 0x20, + }; + + struct ResampleInfo_t + { + + ResampleInfo_t() : m_nFlags(0), m_flAlphaThreshhold(0.4f), m_flAlphaHiFreqThreshhold(0.4f), m_nSrcDepth(1), m_nDestDepth(1) + { + m_flColorScale[0] = 1.0f, m_flColorScale[1] = 1.0f, m_flColorScale[2] = 1.0f, m_flColorScale[3] = 1.0f; + m_flColorGoal[0] = 0.0f, m_flColorGoal[1] = 0.0f, m_flColorGoal[2] = 0.0f, m_flColorGoal[3] = 0.0f; + } + + unsigned char *m_pSrc; + unsigned char *m_pDest; + + int m_nSrcWidth; + int m_nSrcHeight; + int m_nSrcDepth; + + int m_nDestWidth; + int m_nDestHeight; + int m_nDestDepth; + + float m_flSrcGamma; + float m_flDestGamma; + + float m_flColorScale[4]; // Color scale factors RGBA + float m_flColorGoal[4]; // Color goal values RGBA DestColor = ColorGoal + scale * (SrcColor - ColorGoal) + + float m_flAlphaThreshhold; + float m_flAlphaHiFreqThreshhold; + + int m_nFlags; + }; + + bool ResampleRGBA8888( const ResampleInfo_t &info ); + bool ResampleRGBA16161616( const ResampleInfo_t &info ); + bool ResampleRGB323232F( const ResampleInfo_t &info ); + bool ResampleRGBA32323232F( const ResampleInfo_t &info ); + + void ConvertNormalMapRGBA8888ToDUDVMapUVLX8888( const unsigned char *src, int width, int height, unsigned char *dst_ ); + void ConvertNormalMapRGBA8888ToDUDVMapUVWQ8888( const unsigned char *src, int width, int height, unsigned char *dst_ ); + void ConvertNormalMapRGBA8888ToDUDVMapUV88( const unsigned char *src, int width, int height, unsigned char *dst_ ); + void ConvertNormalMapARGB8888ToDXT5GA( const unsigned char *src, unsigned char *dst, int width, int height ); + + void ConvertIA88ImageToNormalMapRGBA8888( const unsigned char *src, int width, + int height, unsigned char *dst, + float bumpScale ); + + void NormalizeNormalMapRGBA8888( unsigned char *src, int numTexels ); + + //----------------------------------------------------------------------------- + // Gamma correction + //----------------------------------------------------------------------------- + void GammaCorrectRGBA8888( unsigned char *src, unsigned char* dst, + int width, int height, int depth, float srcGamma, float dstGamma ); + + + //----------------------------------------------------------------------------- + // Makes a gamma table + //----------------------------------------------------------------------------- + void ConstructGammaTable( unsigned char* pTable, float srcGamma, float dstGamma ); + + + //----------------------------------------------------------------------------- + // Gamma corrects using a previously constructed gamma table + //----------------------------------------------------------------------------- + void GammaCorrectRGBA8888( unsigned char* pSrc, unsigned char* pDst, + int width, int height, int depth, unsigned char* pGammaTable ); + + + //----------------------------------------------------------------------------- + // Generates a number of mipmap levels + //----------------------------------------------------------------------------- + void GenerateMipmapLevels( unsigned char* pSrc, unsigned char* pDst, int width, + int height, int depth, ImageFormat imageFormat, float srcGamma, float dstGamma, + int numLevels = 0 ); + + + //----------------------------------------------------------------------------- + // operations on square images (src and dst can be the same) + //----------------------------------------------------------------------------- + bool RotateImageLeft( const unsigned char *src, unsigned char *dst, + int widthHeight, ImageFormat imageFormat ); + bool RotateImage180( const unsigned char *src, unsigned char *dst, + int widthHeight, ImageFormat imageFormat ); + bool FlipImageVertically( void *pSrc, void *pDst, int nWidth, int nHeight, ImageFormat imageFormat, int nDstStride = 0 ); + bool FlipImageHorizontally( void *pSrc, void *pDst, int nWidth, int nHeight, ImageFormat imageFormat, int nDstStride = 0 ); + bool SwapAxes( unsigned char *src, + int widthHeight, ImageFormat imageFormat ); + + + //----------------------------------------------------------------------------- + // Returns info about each image format + //----------------------------------------------------------------------------- + const ImageFormatInfo_t &ImageFormatInfo( ImageFormat fmt ); + + + //----------------------------------------------------------------------------- + // Gets the name of the image format + //----------------------------------------------------------------------------- + inline char const* GetName( ImageFormat fmt ) + { + return ImageFormatInfo(fmt).m_pName; + } + + + //----------------------------------------------------------------------------- + // Gets the size of the image format in bytes + //----------------------------------------------------------------------------- + inline int SizeInBytes( ImageFormat fmt ) + { + return ImageFormatInfo(fmt).m_nNumBytes; + } + + //----------------------------------------------------------------------------- + // Does the image format support transparency? + //----------------------------------------------------------------------------- + inline bool IsTransparent( ImageFormat fmt ) + { + return ImageFormatInfo(fmt).m_nNumAlphaBits > 0; + } + + + //----------------------------------------------------------------------------- + // Is the image format compressed? + //----------------------------------------------------------------------------- + inline bool IsCompressed( ImageFormat fmt ) + { + return ImageFormatInfo(fmt).m_bIsCompressed; + } + + + //----------------------------------------------------------------------------- + // Is the image format compressed? + //----------------------------------------------------------------------------- + inline bool IsDepthFormat( ImageFormat fmt ) + { + return ImageFormatInfo(fmt).m_bIsDepthFormat; + } + + + //----------------------------------------------------------------------------- + // Is any channel > 8 bits? + //----------------------------------------------------------------------------- + inline bool HasChannelLargerThan8Bits( ImageFormat fmt ) + { + ImageFormatInfo_t info = ImageFormatInfo(fmt); + return ( info.m_nNumRedBits > 8 || info.m_nNumGreenBits > 8 || info.m_nNumBlueBits > 8 || info.m_nNumAlphaBits > 8 ); + } + + inline bool IsFloatFormat( ImageFormat fmt ) + { + return ( fmt == IMAGE_FORMAT_RGBA16161616F ) || + ( fmt == IMAGE_FORMAT_RG1616F ) || + ( fmt == IMAGE_FORMAT_R32F ) || + ( fmt == IMAGE_FORMAT_RG3232F ) || + ( fmt == IMAGE_FORMAT_RGB323232F ) || + ( fmt == IMAGE_FORMAT_RGBA32323232F ); + } + +} // end namespace ImageLoader + + +#endif // IMAGEFORMAT_H diff --git a/public/bitmap/psd.h b/public/bitmap/psd.h new file mode 100644 index 0000000..ec56f84 --- /dev/null +++ b/public/bitmap/psd.h @@ -0,0 +1,105 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Methods relating to saving + loading PSD files (photoshop) +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PSD_H +#define PSD_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "bitmap/imageformat.h" //ImageFormat enum definition + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; +struct Bitmap_t; + +class PSDImageResources +{ +public: + enum Resource { + eResFileInfo = 0x0404 + }; + + struct ResElement { + Resource m_eType; + // unsigned char m_pReserved[4]; + unsigned short m_numBytes; + unsigned char const *m_pvData; + }; + +public: + explicit PSDImageResources( unsigned int numBytes, unsigned char const *pvBuffer ) : m_numBytes( numBytes ), m_pvBuffer( pvBuffer ) {} + +public: + ResElement FindElement( Resource eType ) const; + +protected: + unsigned int m_numBytes; + unsigned char const * m_pvBuffer; +}; + +class PSDResFileInfo +{ +public: + enum ResFileInfo { + eTitle = 0x05, + eAuthor = 0x50, + eAuthorTitle = 0x55, + eDescription = 0x78, + eDescriptionWriter = 0x7A, + eKeywords = 0x19, + eCopyrightNotice = 0x74 + }; + + struct ResFileInfoElement { + ResFileInfo m_eType; + unsigned short m_numBytes; + unsigned char const *m_pvData; + }; + +public: + explicit PSDResFileInfo( PSDImageResources::ResElement res ) : m_res( res ) {} + +public: + ResFileInfoElement FindElement( ResFileInfo eType ) const; + +protected: + PSDImageResources::ResElement m_res; +}; + + +//----------------------------------------------------------------------------- +// Is a file a PSD file? +//----------------------------------------------------------------------------- +bool IsPSDFile( const char *pFileName, const char *pPathID ); +bool IsPSDFile( CUtlBuffer &buf ); + + +//----------------------------------------------------------------------------- +// Returns information about the PSD file +//----------------------------------------------------------------------------- +bool PSDGetInfo( const char *pFileName, const char *pPathID, int *pWidth, int *pHeight, ImageFormat *pImageFormat, float *pSourceGamma ); +bool PSDGetInfo( CUtlBuffer &buf, int *pWidth, int *pHeight, ImageFormat *pImageFormat, float *pSourceGamma ); + + +//----------------------------------------------------------------------------- +// Get PSD file image resources, pointers refer into the utlbuffer +//----------------------------------------------------------------------------- +PSDImageResources PSDGetImageResources( CUtlBuffer &buf ); + + +//----------------------------------------------------------------------------- +// Reads the PSD file into the specified buffer +//----------------------------------------------------------------------------- +bool PSDReadFileRGBA8888( CUtlBuffer &buf, Bitmap_t &bitmap ); +bool PSDReadFileRGBA8888( const char *pFileName, const char *pPathID, Bitmap_t &bitmap ); + + +#endif // PSD_H diff --git a/public/bitmap/psheet.h b/public/bitmap/psheet.h new file mode 100644 index 0000000..ccbe188 --- /dev/null +++ b/public/bitmap/psheet.h @@ -0,0 +1,136 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: sheet definitions for particles and other sprite functions +// +//===========================================================================// + +#ifndef PSHEET_H +#define PSHEET_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlobjectreference.h" +#include "materialsystem/MaterialSystemUtil.h" +#include "tier1/utlvector.h" + +class CUtlBuffer; + +// classes for keeping a dictionary of sheet files in memory. A sheet is a bunch of frames packewd +// within one texture. Each sheet has 1 or more frame sequences stored for it. + + +// for fast lookups to retrieve sequence data, we store the sequence information discretized into +// a fixed # of frames. If this discretenesss is a visual problem, you can lerp the blend values to get it +// perfect. +#define SEQUENCE_SAMPLE_COUNT 1024 + +#define MAX_IMAGES_PER_FRAME_ON_DISK 4 +#define MAX_IMAGES_PER_FRAME_IN_MEMORY 2 + +struct SequenceSampleTextureCoords_t +{ + float m_fLeft_U0; + float m_fTop_V0; + float m_fRight_U0; + float m_fBottom_V0; + + float m_fLeft_U1; + float m_fTop_V1; + float m_fRight_U1; + float m_fBottom_V1; +}; + +struct SheetSequenceSample_t +{ + // coordinates of two rectangles (old and next frame coords) + + SequenceSampleTextureCoords_t m_TextureCoordData[MAX_IMAGES_PER_FRAME_IN_MEMORY]; + + float m_fBlendFactor; + + void CopyFirstFrameToOthers(void) + { + // for old format files only supporting one image per frame + for(int i=1; i < MAX_IMAGES_PER_FRAME_IN_MEMORY; i++) + { + m_TextureCoordData[i] = m_TextureCoordData[0]; + } + } + +}; + +enum SheetSequenceFlags_t +{ + SEQ_FLAG_CLAMP = 0x1, // as opposed to loop + SEQ_FLAG_NO_ALPHA = 0x2, // packed as sequence-rgb (alpha channel should be ignored) + SEQ_FLAG_NO_COLOR = 0x4, // packed as sequence-a (color channels should be ignored) +}; + +class CSheet +{ +public: + // read form a .sht file. This is the usual thing to do + CSheet( CUtlBuffer &buf ); + CSheet( void ); + ~CSheet( void ); + + const SheetSequenceSample_t *GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop ); + + // references for smart ptrs + CUtlReferenceList m_References; + + struct SheetInfo_t + { + SheetSequenceSample_t *m_pSamples; + uint8 m_SeqFlags; + bool m_bSequenceIsCopyOfAnotherSequence; + int16 m_nNumFrames; + float m_flFrameSpan; + }; + + CUtlVector< SheetInfo_t > m_SheetInfo; +}; + +////////////////////////////////////////////////////////////////////////// + +class IMesh; +class IMaterial; + +// A heavier-weight version of CSheet with more bells and whistles + +class CSheetExtended +{ +public: + explicit CSheetExtended( IMaterial* pMaterial ); + ~CSheetExtended(); + + int GetSheetSequenceCount(); + int GetNthSequenceIndex( int nSequenceNumber ); + const SheetSequenceSample_t *GetSampleForSequence( float flAge, float flAgeScale, int nSequence, bool bForceLoop ); + float GetSequenceTimeSpan( int nSequenceIndex ); + + void DrawSheet( IMesh *pMesh, const Vector &vCenter, float flRadius, int nSheetSequence, float flAge, float flSheetPreviewSpeed, bool bLoopSheetPreview, int nSecondarySequence=-1, bool bOverrideSpriteCard=false ); + + bool ValidSheetData(); + + bool SequenceHasAlphaData( int nSequenceIndex ); + bool SequenceHasColorData( int nSequenceIndex ); + + // Helper + static bool IsMaterialSeparateAlphaColorMaterial( IMaterial* pMat ); + static bool IsMaterialDualSequence( IMaterial* pMat ); + +private: + void LoadFromBuffer( CUtlBuffer& buf ); + void LoadFromMaterial( IMaterial* pMaterial ); + + // TEMP: Store in a CSheet for now - eventually we'll want more data + CSheet* m_pSheetData; + CMaterialReference m_Material; +}; + + +#endif // PSHEET_H + diff --git a/public/bitmap/tgaloader.h b/public/bitmap/tgaloader.h new file mode 100644 index 0000000..8a9947b --- /dev/null +++ b/public/bitmap/tgaloader.h @@ -0,0 +1,45 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef TGALOADER_H +#define TGALOADER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "bitmap/imageformat.h" +#include "tier1/utlmemory.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; + + +namespace TGALoader +{ + +int TGAHeaderSize(); + +bool GetInfo( const char *fileName, int *width, int *height, ImageFormat *imageFormat, float *sourceGamma ); +bool GetInfo( CUtlBuffer &buf, int *width, int *height, ImageFormat *imageFormat, float *sourceGamma ); + +bool Load( unsigned char *imageData, const char *fileName, int width, int height, + ImageFormat imageFormat, float targetGamma, bool mipmap ); +bool Load( unsigned char *imageData, CUtlBuffer &buf, int width, int height, + ImageFormat imageFormat, float targetGamma, bool mipmap ); + +bool LoadRGBA8888( const char *pFileName, CUtlMemory &outputData, int &outWidth, int &outHeight ); +bool LoadRGBA8888( CUtlBuffer &buf, CUtlMemory &outputData, int &outWidth, int &outHeight ); + +} // end namespace TGALoader + +#endif // TGALOADER_H diff --git a/public/bitmap/tgawriter.h b/public/bitmap/tgawriter.h new file mode 100644 index 0000000..965fb5a --- /dev/null +++ b/public/bitmap/tgawriter.h @@ -0,0 +1,35 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef TGAWRITER_H +#define TGAWRITER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "bitmap/imageformat.h" //ImageFormat enum definition + +class CUtlBuffer; + +namespace TGAWriter +{ + bool WriteToBuffer( unsigned char *pImageData, CUtlBuffer &buffer, int width, int height, ImageFormat srcFormat, ImageFormat dstFormat ); + + // Write out a simple tga file from a memory buffer. + bool WriteTGAFile( const char *fileName, int width, int height, enum ImageFormat srcFormat, uint8 const *srcData, int nStride ); + + // Routines for writing to files without allocating any memory in the TGA writer + // Useful for very large files such as posters, which are rendered as sub-rects + bool WriteDummyFileNoAlloc( const char *fileName, int width, int height, ImageFormat dstFormat ); + bool WriteRectNoAlloc( unsigned char *pImageData, const char *fileName, int nXOrigin, int nYOrigin, int width, int height, int nStride, ImageFormat srcFormat ); + bool WriteRectNoAllocFeather( unsigned char *pImageData, const char *fileName, int nXOrigin, int nYOrigin, int width, int height, int nGuardBandWidth, int nGuardBandHeight, int nStride, enum ImageFormat srcFormat ); + +} // end namespace TGAWriter + +#endif // TGAWRITER_H diff --git a/public/bittools.h b/public/bittools.h new file mode 100644 index 0000000..b812fe1 --- /dev/null +++ b/public/bittools.h @@ -0,0 +1,46 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef BITTOOLS_H +#define BITTOOLS_H +#ifdef _WIN32 +#pragma once +#endif + +namespace bittools +{ + template + struct RecurseBit + { + enum {result = RecurseBit::result}; + }; + + template + struct RecurseBit<0, C> + { + enum {result = C}; + }; + + template + struct RecursePow2 + { + enum {result = RecursePow2::result}; + }; + + template + struct RecursePow2<0, C> + { + enum {result = C}; + }; + +} + +#define ROUND_TO_POWER_OF_2( n ) ( bittools::RecursePow2< (n) - 1 >::result ) +#define MINIMUM_BITS_NEEDED( n ) ( bittools::RecurseBit< (n) - 1 >::result ) + +#endif //BITTOOLS_H + diff --git a/public/bitvec.h b/public/bitvec.h new file mode 100644 index 0000000..8a9307b --- /dev/null +++ b/public/bitvec.h @@ -0,0 +1,1417 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef BITVEC_H +#define BITVEC_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier0/dbg.h" +#include "tier0/basetypes.h" + + +class CBitVecAccessor +{ +public: + CBitVecAccessor(uint32 *pDWords, int iBit); + + void operator=(int val); + operator uint32(); + +private: + uint32 *m_pDWords; + int m_iBit; +}; + + +//----------------------------------------------------------------------------- +// Support functions +//----------------------------------------------------------------------------- + +#define LOG2_BITS_PER_INT 5 +#define BITS_PER_INT 32 + +#if _WIN32 && !defined(_X360) +#include +#pragma intrinsic(_BitScanForward) +#endif + +inline int FirstBitInWord( unsigned int elem, int offset ) +{ +#if _WIN32 + if ( !elem ) + return -1; +#if _X360 + // this implements CountTrailingZeros() / BitScanForward() + unsigned int mask = elem-1; + unsigned int comp = ~elem; + elem = mask & comp; + return (32 - _CountLeadingZeros(elem)) + offset; +#else + unsigned long out; + _BitScanForward(&out, elem); + return out + offset; +#endif + +#else + static unsigned firstBitLUT[256] = + { + 0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0, + 3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0, + 3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0, + 3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, + 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + }; + unsigned elemByte; + + elemByte = (elem & 0xFF); + if ( elemByte ) + return offset + firstBitLUT[elemByte]; + + elem >>= 8; + offset += 8; + elemByte = (elem & 0xFF); + if ( elemByte ) + return offset + firstBitLUT[elemByte]; + + elem >>= 8; + offset += 8; + elemByte = (elem & 0xFF); + if ( elemByte ) + return offset + firstBitLUT[elemByte]; + + elem >>= 8; + offset += 8; + elemByte = (elem & 0xFF); + if ( elemByte ) + return offset + firstBitLUT[elemByte]; + + return -1; +#endif +} + +//------------------------------------- + +inline unsigned GetEndMask( int numBits ) +{ + static unsigned bitStringEndMasks[] = + { + 0xffffffff, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + }; + + return bitStringEndMasks[numBits % BITS_PER_INT]; +} + + +inline int GetBitForBitnum( int bitNum ) +{ + static int bitsForBitnum[] = + { + ( 1 << 0 ), + ( 1 << 1 ), + ( 1 << 2 ), + ( 1 << 3 ), + ( 1 << 4 ), + ( 1 << 5 ), + ( 1 << 6 ), + ( 1 << 7 ), + ( 1 << 8 ), + ( 1 << 9 ), + ( 1 << 10 ), + ( 1 << 11 ), + ( 1 << 12 ), + ( 1 << 13 ), + ( 1 << 14 ), + ( 1 << 15 ), + ( 1 << 16 ), + ( 1 << 17 ), + ( 1 << 18 ), + ( 1 << 19 ), + ( 1 << 20 ), + ( 1 << 21 ), + ( 1 << 22 ), + ( 1 << 23 ), + ( 1 << 24 ), + ( 1 << 25 ), + ( 1 << 26 ), + ( 1 << 27 ), + ( 1 << 28 ), + ( 1 << 29 ), + ( 1 << 30 ), + ( 1 << 31 ), + }; + + return bitsForBitnum[ (bitNum) & (BITS_PER_INT-1) ]; +} + +inline int GetBitForBitnumByte( int bitNum ) +{ + static int bitsForBitnum[] = + { + ( 1 << 0 ), + ( 1 << 1 ), + ( 1 << 2 ), + ( 1 << 3 ), + ( 1 << 4 ), + ( 1 << 5 ), + ( 1 << 6 ), + ( 1 << 7 ), + }; + + return bitsForBitnum[ bitNum & 7 ]; +} + +inline int CalcNumIntsForBits( int numBits ) { return (numBits + (BITS_PER_INT-1)) / BITS_PER_INT; } + +#ifdef _X360 +#define BitVec_Bit( bitNum ) GetBitForBitnum( bitNum ) +#define BitVec_BitInByte( bitNum ) GetBitForBitnumByte( bitNum ) +#else +#define BitVec_Bit( bitNum ) ( 1 << ( (bitNum) & (BITS_PER_INT-1) ) ) +#define BitVec_BitInByte( bitNum ) ( 1 << ( (bitNum) & 7 ) ) +#endif +#define BitVec_Int( bitNum ) ( (bitNum) >> LOG2_BITS_PER_INT ) + + +//----------------------------------------------------------------------------- +// template CBitVecT +// +// Defines the operations relevant to any bit array. Simply requires a base +// class that implements GetNumBits(), Base(), GetNumDWords() & ValidateOperand() +// +// CVarBitVec and CBitVec are the actual classes generally used +// by clients +// + +template +class CBitVecT : public BASE_OPS +{ +public: + CBitVecT(); + CBitVecT(int numBits); // Must be initialized with the number of bits + + void Init(int val = 0); + + // Access the bits like an array. + CBitVecAccessor operator[](int i); + + // Do NOT override bitwise operators (see note in header) + void And(const CBitVecT &andStr, CBitVecT *out) const; + void Or(const CBitVecT &orStr, CBitVecT *out) const; + void Xor(const CBitVecT &orStr, CBitVecT *out) const; + + void Not(CBitVecT *out) const; + + void CopyTo(CBitVecT *out) const; + void Copy( const CBitVecT &other, int nBits=-1 ); + bool Compare( const CBitVecT &other, int nBits=-1 ) const; + + bool IsAllClear(void) const; // Are all bits zero? + bool IsAllSet(void) const; // Are all bits one? + + uint32 Get( uint32 bitNum ) const; + bool IsBitSet( int bitNum ) const; + void Set( int bitNum ); + void Set( int bitNum, bool bNewVal ); + void Clear(int bitNum); + + bool TestAndSet(int bitNum); + + void Set( uint32 offset, uint32 mask ); + void Clear( uint32 offset, uint32 mask ); + uint32 Get( uint32 offset, uint32 mask ); + + void SetAll(void); // Sets all bits + void ClearAll(void); // Clears all bits + + uint32 GetDWord(int i) const; + void SetDWord(int i, uint32 val); + + CBitVecT& operator=(const CBitVecT &other) { other.CopyTo( this ); return *this; } + bool operator==(const CBitVecT &other) { return Compare( other ); } + bool operator!=(const CBitVecT &other) { return !operator==( other ); } + + static void GetOffsetMaskForBit( uint32 bitNum, uint32 *pOffset, uint32 *pMask ) { *pOffset = BitVec_Int( bitNum ); *pMask = BitVec_Bit( bitNum ); } +}; + +//----------------------------------------------------------------------------- +// class CVarBitVecBase +// +// Defines the operations necessary for a variable sized bit array + +class CVarBitVecBase +{ +public: + bool IsFixedSize() const { return false; } + int GetNumBits(void) const { return m_numBits; } + void Resize( int numBits, bool bClearAll = false ); // resizes bit array + + int GetNumDWords() const { return m_numInts; } + uint32 *Base() { return m_pInt; } + const uint32 *Base() const { return m_pInt; } + + void Attach( uint32 *pBits, int numBits ); + bool Detach( uint32 **ppBits, int *pNumBits ); + + int FindNextSetBit(int iStartBit) const; // returns -1 if no set bit was found + +protected: + CVarBitVecBase(); + CVarBitVecBase(int numBits); + CVarBitVecBase( const CVarBitVecBase &from ); + CVarBitVecBase &operator=( const CVarBitVecBase &from ); + ~CVarBitVecBase(void); + + void ValidateOperand( const CVarBitVecBase &operand ) const { Assert(GetNumBits() == operand.GetNumBits()); } + + unsigned GetEndMask() const { return ::GetEndMask( GetNumBits() ); } + +private: + + unsigned short m_numBits; // Number of bits in the bitstring + unsigned short m_numInts; // Number of ints to needed to store bitstring + uint32 m_iBitStringStorage; // If the bit string fits in one int, it goes here + uint32 * m_pInt; // Array of ints containing the bitstring + + void AllocInts( int numInts ); // Free the allocated bits + void ReallocInts( int numInts ); + void FreeInts( void ); // Free the allocated bits +}; + +//----------------------------------------------------------------------------- +// class CFixedBitVecBase +// +// Defines the operations necessary for a fixed sized bit array. +// + +template struct BitCountToEndMask_t { }; +template <> struct BitCountToEndMask_t< 0> { enum { MASK = 0xffffffff }; }; +template <> struct BitCountToEndMask_t< 1> { enum { MASK = 0x00000001 }; }; +template <> struct BitCountToEndMask_t< 2> { enum { MASK = 0x00000003 }; }; +template <> struct BitCountToEndMask_t< 3> { enum { MASK = 0x00000007 }; }; +template <> struct BitCountToEndMask_t< 4> { enum { MASK = 0x0000000f }; }; +template <> struct BitCountToEndMask_t< 5> { enum { MASK = 0x0000001f }; }; +template <> struct BitCountToEndMask_t< 6> { enum { MASK = 0x0000003f }; }; +template <> struct BitCountToEndMask_t< 7> { enum { MASK = 0x0000007f }; }; +template <> struct BitCountToEndMask_t< 8> { enum { MASK = 0x000000ff }; }; +template <> struct BitCountToEndMask_t< 9> { enum { MASK = 0x000001ff }; }; +template <> struct BitCountToEndMask_t<10> { enum { MASK = 0x000003ff }; }; +template <> struct BitCountToEndMask_t<11> { enum { MASK = 0x000007ff }; }; +template <> struct BitCountToEndMask_t<12> { enum { MASK = 0x00000fff }; }; +template <> struct BitCountToEndMask_t<13> { enum { MASK = 0x00001fff }; }; +template <> struct BitCountToEndMask_t<14> { enum { MASK = 0x00003fff }; }; +template <> struct BitCountToEndMask_t<15> { enum { MASK = 0x00007fff }; }; +template <> struct BitCountToEndMask_t<16> { enum { MASK = 0x0000ffff }; }; +template <> struct BitCountToEndMask_t<17> { enum { MASK = 0x0001ffff }; }; +template <> struct BitCountToEndMask_t<18> { enum { MASK = 0x0003ffff }; }; +template <> struct BitCountToEndMask_t<19> { enum { MASK = 0x0007ffff }; }; +template <> struct BitCountToEndMask_t<20> { enum { MASK = 0x000fffff }; }; +template <> struct BitCountToEndMask_t<21> { enum { MASK = 0x001fffff }; }; +template <> struct BitCountToEndMask_t<22> { enum { MASK = 0x003fffff }; }; +template <> struct BitCountToEndMask_t<23> { enum { MASK = 0x007fffff }; }; +template <> struct BitCountToEndMask_t<24> { enum { MASK = 0x00ffffff }; }; +template <> struct BitCountToEndMask_t<25> { enum { MASK = 0x01ffffff }; }; +template <> struct BitCountToEndMask_t<26> { enum { MASK = 0x03ffffff }; }; +template <> struct BitCountToEndMask_t<27> { enum { MASK = 0x07ffffff }; }; +template <> struct BitCountToEndMask_t<28> { enum { MASK = 0x0fffffff }; }; +template <> struct BitCountToEndMask_t<29> { enum { MASK = 0x1fffffff }; }; +template <> struct BitCountToEndMask_t<30> { enum { MASK = 0x3fffffff }; }; +template <> struct BitCountToEndMask_t<31> { enum { MASK = 0x7fffffff }; }; + +//------------------------------------- + +template +class CFixedBitVecBase +{ +public: + bool IsFixedSize() const { return true; } + int GetNumBits(void) const { return NUM_BITS; } + void Resize( int numBits, bool bClearAll = false ) { Assert(numBits == NUM_BITS); if ( bClearAll ) Plat_FastMemset( m_Ints, 0, NUM_INTS * sizeof(uint32) ); }// for syntatic consistency (for when using templates) + + int GetNumDWords() const { return NUM_INTS; } + uint32 * Base() { return m_Ints; } + const uint32 * Base() const { return m_Ints; } + + int FindNextSetBit(int iStartBit) const; // returns -1 if no set bit was found + +protected: + CFixedBitVecBase() {} + CFixedBitVecBase(int numBits) { Assert( numBits == NUM_BITS ); } // doesn't make sense, really. Supported to simplify templates & allow easy replacement of variable + + void ValidateOperand( const CFixedBitVecBase &operand ) const { } // no need, compiler does so statically + +public: // for test code + unsigned GetEndMask() const { return static_cast( BitCountToEndMask_t::MASK ); } + +private: + enum + { + NUM_INTS = (NUM_BITS + (BITS_PER_INT-1)) / BITS_PER_INT + }; + + uint32 m_Ints[(NUM_BITS + (BITS_PER_INT-1)) / BITS_PER_INT]; +}; + +//----------------------------------------------------------------------------- +// +// The actual classes used +// + +// inheritance instead of typedef to allow forward declarations +class CVarBitVec : public CBitVecT +{ +public: + CVarBitVec() + { + } + + CVarBitVec(int numBits) + : CBitVecT(numBits) + { + } +}; + +//----------------------------------------------------------------------------- + +template < int NUM_BITS > +class CBitVec : public CBitVecT< CFixedBitVecBase > +{ +public: + CBitVec() + { + } + + CBitVec(int numBits) + : CBitVecT< CFixedBitVecBase >(numBits) + { + } +}; + + +//----------------------------------------------------------------------------- + +typedef CBitVec<32> CDWordBitVec; + +//----------------------------------------------------------------------------- + +inline CVarBitVecBase::CVarBitVecBase() +{ + Plat_FastMemset( this, 0, sizeof( *this ) ); +} + +//----------------------------------------------------------------------------- + +inline CVarBitVecBase::CVarBitVecBase(int numBits) +{ + Assert( numBits ); + m_numBits = numBits; + + // Figure out how many ints are needed + m_numInts = CalcNumIntsForBits( numBits ); + m_pInt = NULL; + AllocInts( m_numInts ); +} + +//----------------------------------------------------------------------------- + +inline CVarBitVecBase::CVarBitVecBase( const CVarBitVecBase &from ) +{ + if ( from.m_numInts ) + { + m_numBits = from.m_numBits; + m_numInts = from.m_numInts; + m_pInt = NULL; + AllocInts( m_numInts ); + memcpy( m_pInt, from.m_pInt, m_numInts * sizeof(int) ); + } + else + memset( this, 0, sizeof( *this ) ); +} + +//----------------------------------------------------------------------------- + +inline CVarBitVecBase &CVarBitVecBase::operator=( const CVarBitVecBase &from ) +{ + Resize( from.GetNumBits() ); + if ( m_pInt ) + memcpy( m_pInt, from.m_pInt, m_numInts * sizeof(int) ); + return (*this); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +// Input : +// Output : +//----------------------------------------------------------------------------- + +inline CVarBitVecBase::~CVarBitVecBase(void) +{ + FreeInts(); +} + +//----------------------------------------------------------------------------- + +inline void CVarBitVecBase::Attach( uint32 *pBits, int numBits ) +{ + FreeInts(); + m_numBits = numBits; + m_numInts = CalcNumIntsForBits( numBits ); + if ( m_numInts > 1 ) + { + m_pInt = pBits; + } + else + { + m_iBitStringStorage = *pBits; + m_pInt = &m_iBitStringStorage; + free( pBits ); + } +} + +//----------------------------------------------------------------------------- + +inline bool CVarBitVecBase::Detach( uint32 **ppBits, int *pNumBits ) +{ + if ( !m_numBits ) + { + return false; + } + + *pNumBits = m_numBits; + if ( m_numInts > 1 ) + { + *ppBits = m_pInt; + } + else + { + *ppBits = (uint32 *)malloc( sizeof(uint32) ); + **ppBits = m_iBitStringStorage; + free( m_pInt ); + } + + memset( this, 0, sizeof( *this ) ); + return true; +} + +//----------------------------------------------------------------------------- + +template +inline CBitVecT::CBitVecT() +{ + // undef this is ints are not 4 bytes + // generate a compile error if sizeof(int) is not 4 (HACK: can't use the preprocessor so use the compiler) + + COMPILE_TIME_ASSERT( sizeof(int)==4 ); + + // Initialize bitstring by clearing all bits + ClearAll(); +} + +//----------------------------------------------------------------------------- +template +inline CBitVecT::CBitVecT(int numBits) + : BASE_OPS( numBits ) +{ + // undef this is ints are not 4 bytes + // generate a compile error if sizeof(int) is not 4 (HACK: can't use the preprocessor so use the compiler) + + COMPILE_TIME_ASSERT( sizeof(int)==4 ); + + // Initialize bitstring by clearing all bits + ClearAll(); +} + +//----------------------------------------------------------------------------- + +template +inline CBitVecAccessor CBitVecT::operator[](int i) +{ + Assert(i >= 0 && i < this->GetNumBits()); + return CBitVecAccessor(this->Base(), i); +} + + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Init( int val ) +{ + if ( this->Base() ) + Plat_FastMemset( this->Base(), ( val ) ? 0xff : 0, this->GetNumDWords() * sizeof(int) ); +} + +//----------------------------------------------------------------------------- + +template +inline uint32 CBitVecT::Get( uint32 bitNum ) const +{ + Assert( bitNum < (uint32)this->GetNumBits() ); + const uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + return ( *pInt & BitVec_Bit( bitNum ) ); +} + +//----------------------------------------------------------------------------- + +template +inline bool CBitVecT::IsBitSet( int bitNum ) const +{ + Assert( bitNum >= 0 && bitNum < this->GetNumBits() ); + const uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + return ( ( *pInt & BitVec_Bit( bitNum ) ) != 0 ); +} + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Set( int bitNum ) +{ + Assert( bitNum >= 0 && bitNum < this->GetNumBits() ); + uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + *pInt |= BitVec_Bit( bitNum ); +} + +//----------------------------------------------------------------------------- + +template +inline bool CBitVecT::TestAndSet(int bitNum) +{ + Assert( bitNum >= 0 && bitNum < this->GetNumBits() ); + uint32 bitVecBit = BitVec_Bit( bitNum ); + uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + bool bResult = ( ( *pInt & bitVecBit) != 0 ); + *pInt |= bitVecBit; + return bResult; +} + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Clear(int bitNum) +{ + Assert( bitNum >= 0 && bitNum < this->GetNumBits() ); + uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + *pInt &= ~BitVec_Bit( bitNum ); +} + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Set( int bitNum, bool bNewVal ) +{ + uint32 *pInt = this->Base() + BitVec_Int( bitNum ); + uint32 bitMask = BitVec_Bit( bitNum ); + if ( bNewVal ) + { + *pInt |= bitMask; + } + else + { + *pInt &= ~bitMask; + } +} + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Set( uint32 offset, uint32 mask ) +{ + uint32 *pInt = this->Base() + offset; + *pInt |= mask; +} + +//----------------------------------------------------------------------------- + +template +inline void CBitVecT::Clear( uint32 offset, uint32 mask ) +{ + uint32 *pInt = this->Base() + offset; + *pInt &= ~mask; +} + +//----------------------------------------------------------------------------- + +template +inline uint32 CBitVecT::Get( uint32 offset, uint32 mask ) +{ + uint32 *pInt = this->Base() + offset; + return ( *pInt & mask ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::And(const CBitVecT &addStr, CBitVecT *out) const +{ + ValidateOperand( addStr ); + ValidateOperand( *out ); + + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = this->Base(); + const uint32 *pOperand2 = addStr.Base(); + + for (int i = this->GetNumDWords() - 1; i >= 0 ; --i) + { + pDest[i] = pOperand1[i] & pOperand2[i]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::Or(const CBitVecT &orStr, CBitVecT *out) const +{ + ValidateOperand( orStr ); + ValidateOperand( *out ); + + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = this->Base(); + const uint32 *pOperand2 = orStr.Base(); + + for (int i = this->GetNumDWords() - 1; i >= 0; --i) + { + pDest[i] = pOperand1[i] | pOperand2[i]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::Xor(const CBitVecT &xorStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = this->Base(); + const uint32 *pOperand2 = xorStr.Base(); + + for (int i = this->GetNumDWords() - 1; i >= 0; --i) + { + pDest[i] = pOperand1[i] ^ pOperand2[i]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::Not(CBitVecT *out) const +{ + ValidateOperand( *out ); + + uint32 * pDest = out->Base(); + const uint32 *pOperand = this->Base(); + + for (int i = this->GetNumDWords() - 1; i >= 0; --i) + { + pDest[i] = ~(pOperand[i]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Copy a bit string +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::CopyTo(CBitVecT *out) const +{ + out->Resize( this->GetNumBits() ); + + ValidateOperand( *out ); + Assert( out != this ); + + memcpy( out->Base(), this->Base(), this->GetNumDWords() * sizeof( int ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Are all bits zero? +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline bool CBitVecT::IsAllClear(void) const +{ + // Number of available bits may be more than the number + // actually used, so make sure to mask out unused bits + // before testing for zero + (const_cast(this))->Base()[this->GetNumDWords()-1] &= CBitVecT::GetEndMask(); // external semantics of const retained + + for (int i = this->GetNumDWords() - 1; i >= 0; --i) + { + if ( this->Base()[i] !=0 ) + { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Are all bits set? +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline bool CBitVecT::IsAllSet(void) const +{ + // Number of available bits may be more than the number + // actually used, so make sure to mask out unused bits + // before testing for set bits + (const_cast(this))->Base()[this->GetNumDWords()-1] |= ~CBitVecT::GetEndMask(); // external semantics of const retained + + for (int i = this->GetNumDWords() - 1; i >= 0; --i) + { + if ( this->Base()[i] != ~0 ) + { + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets all bits +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::SetAll(void) +{ + if ( this->Base() ) + Plat_FastMemset( this->Base(), 0xff, this->GetNumDWords() * sizeof(int) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Clears all bits +// Input : +// Output : +//----------------------------------------------------------------------------- +template +inline void CBitVecT::ClearAll(void) +{ + if ( this->Base() ) + Plat_FastMemset( this->Base(), 0, this->GetNumDWords() * sizeof(int) ); +} + +//----------------------------------------------------------------------------- +template +inline void CBitVecT::Copy( const CBitVecT &other, int nBits ) +{ + if ( nBits == - 1 ) + { + nBits = other.GetNumBits(); + } + + this->Resize( nBits ); + + ValidateOperand( other ); + Assert( &other != this ); + + memcpy( this->Base(), other.Base(), this->GetNumDWords() * sizeof( uint32 ) ); +} + +//----------------------------------------------------------------------------- +template +inline bool CBitVecT::Compare( const CBitVecT &other, int nBits ) const +{ + if ( nBits == - 1 ) + { + if ( other.GetNumBits() != this->GetNumBits() ) + { + return false; + } + + nBits = other.GetNumBits(); + } + + if ( nBits > other.GetNumBits() || nBits > this->GetNumBits() ) + { + return false; + } + + (const_cast(this))->Base()[this->GetNumDWords()-1] &= CBitVecT::GetEndMask(); // external semantics of const retained + (const_cast(&other))->Base()[this->GetNumDWords()-1] &= other.CBitVecT::GetEndMask(); // external semantics of const retained + + int nBytes = PAD_NUMBER( nBits, 8 ) >> 3; + + return ( memcmp( this->Base(), other.Base(), nBytes ) == 0 ); +} + +//----------------------------------------------------------------------------- +template +inline uint32 CBitVecT::GetDWord(int i) const +{ + Assert(i >= 0 && i < this->GetNumDWords()); + return this->Base()[i]; +} + +//----------------------------------------------------------------------------- +template +inline void CBitVecT::SetDWord(int i, uint32 val) +{ + Assert(i >= 0 && i < this->GetNumDWords()); + this->Base()[i] = val; +} + +//----------------------------------------------------------------------------- + +inline unsigned GetStartBitMask( int startBit ) +{ + static unsigned int g_StartMask[32] = + { + 0xffffffff, + 0xfffffffe, + 0xfffffffc, + 0xfffffff8, + 0xfffffff0, + 0xffffffe0, + 0xffffffc0, + 0xffffff80, + 0xffffff00, + 0xfffffe00, + 0xfffffc00, + 0xfffff800, + 0xfffff000, + 0xffffe000, + 0xffffc000, + 0xffff8000, + 0xffff0000, + 0xfffe0000, + 0xfffc0000, + 0xfff80000, + 0xfff00000, + 0xffe00000, + 0xffc00000, + 0xff800000, + 0xff000000, + 0xfe000000, + 0xfc000000, + 0xf8000000, + 0xf0000000, + 0xe0000000, + 0xc0000000, + 0x80000000, + }; + + return g_StartMask[ startBit & 31 ]; +} + +inline int CVarBitVecBase::FindNextSetBit( int startBit ) const +{ + if ( startBit < GetNumBits() ) + { + int wordIndex = BitVec_Int(startBit); + unsigned int startMask = GetStartBitMask( startBit ); + int lastWord = GetNumDWords()-1; + + // handle non dword lengths + if ( (GetNumBits() % BITS_PER_INT) != 0 ) + { + unsigned int elem = Base()[wordIndex]; + elem &= startMask; + if ( wordIndex == lastWord) + { + elem &= (GetEndMask()); + // there's a bit remaining in this word + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + } + else + { + // there's a bit remaining in this word + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + + // iterate the words + for ( int i = wordIndex+1; i < lastWord; i++ ) + { + elem = Base()[i]; + if ( elem ) + return FirstBitInWord(elem, i << 5); + } + elem = Base()[lastWord] & GetEndMask(); + if ( elem ) + return FirstBitInWord(elem, lastWord << 5); + } + } + else + { + const uint32 * RESTRICT pCurElem = Base() + wordIndex; + unsigned int elem = *pCurElem; + elem &= startMask; + do + { + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + ++pCurElem; + elem = *pCurElem; + ++wordIndex; + } while( wordIndex <= lastWord ); + } + + } + + return -1; +} + +template +inline int CFixedBitVecBase::FindNextSetBit( int startBit ) const +{ + if ( startBit < NUM_BITS ) + { + int wordIndex = BitVec_Int(startBit); + unsigned int startMask = GetStartBitMask( startBit ); + + // handle non dword lengths + if ( (NUM_BITS % BITS_PER_INT) != 0 ) + { + unsigned int elem = Base()[wordIndex]; + elem &= startMask; + if ( wordIndex == NUM_INTS-1) + { + elem &= (GetEndMask()); + // there's a bit remaining in this word + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + } + else + { + // there's a bit remaining in this word + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + + // iterate the words + for ( int i = wordIndex+1; i < NUM_INTS-1; i++ ) + { + elem = Base()[i]; + if ( elem ) + return FirstBitInWord(elem, i << 5); + } + elem = Base()[NUM_INTS-1] & GetEndMask(); + if ( elem ) + return FirstBitInWord(elem, (NUM_INTS-1) << 5); + } + } + else + { + const uint32 * RESTRICT pCurElem = Base() + wordIndex; + unsigned int elem = *pCurElem; + elem &= startMask; + do + { + if ( elem ) + return FirstBitInWord(elem, wordIndex << 5); + ++pCurElem; + elem = *pCurElem; + ++wordIndex; + } while( wordIndex <= NUM_INTS-1); + } + + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Unrolled loops for some common sizes + +template<> +FORCEINLINE_TEMPLATE void CBitVecT< CFixedBitVecBase<256> >::And(const CBitVecT &addStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = Base(); + const uint32 *pOperand2 = addStr.Base(); + + pDest[0] = pOperand1[0] & pOperand2[0]; + pDest[1] = pOperand1[1] & pOperand2[1]; + pDest[2] = pOperand1[2] & pOperand2[2]; + pDest[3] = pOperand1[3] & pOperand2[3]; + pDest[4] = pOperand1[4] & pOperand2[4]; + pDest[5] = pOperand1[5] & pOperand2[5]; + pDest[6] = pOperand1[6] & pOperand2[6]; + pDest[7] = pOperand1[7] & pOperand2[7]; +} + +template<> +FORCEINLINE_TEMPLATE bool CBitVecT< CFixedBitVecBase<256> >::IsAllClear(void) const +{ + const uint32 *pInts = Base(); + return ( pInts[0] == 0 && pInts[1] == 0 && pInts[2] == 0 && pInts[3] == 0 && pInts[4] == 0 && pInts[5] == 0 && pInts[6] == 0 && pInts[7] == 0 ); +} + +template<> +FORCEINLINE_TEMPLATE void CBitVecT< CFixedBitVecBase<256> >::CopyTo(CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pInts = Base(); + + pDest[0] = pInts[0]; + pDest[1] = pInts[1]; + pDest[2] = pInts[2]; + pDest[3] = pInts[3]; + pDest[4] = pInts[4]; + pDest[5] = pInts[5]; + pDest[6] = pInts[6]; + pDest[7] = pInts[7]; +} + +template<> +FORCEINLINE_TEMPLATE void CBitVecT< CFixedBitVecBase<128> >::And(const CBitVecT &addStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = Base(); + const uint32 *pOperand2 = addStr.Base(); + + pDest[0] = pOperand1[0] & pOperand2[0]; + pDest[1] = pOperand1[1] & pOperand2[1]; + pDest[2] = pOperand1[2] & pOperand2[2]; + pDest[3] = pOperand1[3] & pOperand2[3]; +} + +template<> +FORCEINLINE_TEMPLATE bool CBitVecT< CFixedBitVecBase<128> >::IsAllClear(void) const +{ + const uint32 *pInts = Base(); + return ( pInts[0] == 0 && pInts[1] == 0 && pInts[2] == 0 && pInts[3] == 0 ); +} + +template<> +FORCEINLINE_TEMPLATE void CBitVecT< CFixedBitVecBase<128> >::CopyTo(CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pInts = Base(); + + pDest[0] = pInts[0]; + pDest[1] = pInts[1]; + pDest[2] = pInts[2]; + pDest[3] = pInts[3]; +} + +template<> +inline void CBitVecT< CFixedBitVecBase<96> >::And(const CBitVecT &addStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = Base(); + const uint32 *pOperand2 = addStr.Base(); + + pDest[0] = pOperand1[0] & pOperand2[0]; + pDest[1] = pOperand1[1] & pOperand2[1]; + pDest[2] = pOperand1[2] & pOperand2[2]; +} + +template<> +inline bool CBitVecT< CFixedBitVecBase<96> >::IsAllClear(void) const +{ + const uint32 *pInts = Base(); + return ( pInts[0] == 0 && pInts[1] == 0 && pInts[2] == 0 ); +} + +template<> +inline void CBitVecT< CFixedBitVecBase<96> >::CopyTo(CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pInts = Base(); + + pDest[0] = pInts[0]; + pDest[1] = pInts[1]; + pDest[2] = pInts[2]; +} + +template<> +inline void CBitVecT< CFixedBitVecBase<64> >::And(const CBitVecT &addStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = Base(); + const uint32 *pOperand2 = addStr.Base(); + + pDest[0] = pOperand1[0] & pOperand2[0]; + pDest[1] = pOperand1[1] & pOperand2[1]; +} + +template<> +inline bool CBitVecT< CFixedBitVecBase<64> >::IsAllClear(void) const +{ + const uint32 *pInts = Base(); + return ( pInts[0] == 0 && pInts[1] == 0 ); +} + +template<> +inline void CBitVecT< CFixedBitVecBase<64> >::CopyTo(CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pInts = Base(); + + pDest[0] = pInts[0]; + pDest[1] = pInts[1]; +} + +template<> +inline void CBitVecT< CFixedBitVecBase<32> >::And(const CBitVecT &addStr, CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pOperand1 = Base(); + const uint32 *pOperand2 = addStr.Base(); + + pDest[0] = pOperand1[0] & pOperand2[0]; +} + +template<> +inline bool CBitVecT< CFixedBitVecBase<32> >::IsAllClear(void) const +{ + const uint32 *pInts = Base(); + + return ( pInts[0] == 0 ); +} + +template<> +inline void CBitVecT< CFixedBitVecBase<32> >::CopyTo(CBitVecT *out) const +{ + uint32 * pDest = out->Base(); + const uint32 *pInts = Base(); + + pDest[0] = pInts[0]; +} + +//----------------------------------------------------------------------------- + +template <> +inline uint32 CBitVecT< CFixedBitVecBase<32> >::Get( uint32 bitNum ) const +{ + return ( *Base() & BitVec_Bit( bitNum ) ); +} + +//----------------------------------------------------------------------------- + +template <> +inline bool CBitVecT< CFixedBitVecBase<32> >::IsBitSet( int bitNum ) const +{ + return ( ( *Base() & BitVec_Bit( bitNum ) ) != 0 ); +} + +//----------------------------------------------------------------------------- + +template <> +inline void CBitVecT< CFixedBitVecBase<32> >::Set( int bitNum ) +{ + *Base() |= BitVec_Bit( bitNum ); +} + +//----------------------------------------------------------------------------- + +template <> +inline void CBitVecT< CFixedBitVecBase<32> >::Clear(int bitNum) +{ + *Base() &= ~BitVec_Bit( bitNum ); +} + +//----------------------------------------------------------------------------- + +template <> +inline void CBitVecT< CFixedBitVecBase<32> >::Set( int bitNum, bool bNewVal ) +{ + uint32 bitMask = BitVec_Bit( bitNum ); + if ( bNewVal ) + { + *Base() |= bitMask; + } + else + { + *Base() &= ~bitMask; + } +} + + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Resizes the bit string to a new number of bits +// Input : resizeNumBits - +//----------------------------------------------------------------------------- +inline void CVarBitVecBase::Resize( int resizeNumBits, bool bClearAll ) +{ + Assert( resizeNumBits >= 0 && resizeNumBits <= USHRT_MAX ); + + int newIntCount = CalcNumIntsForBits( resizeNumBits ); + if ( newIntCount != GetNumDWords() ) + { + if ( Base() ) + { + ReallocInts( newIntCount ); + if ( !bClearAll && resizeNumBits >= GetNumBits() ) + { + Base()[GetNumDWords() - 1] &= GetEndMask(); + Plat_FastMemset( Base() + GetNumDWords(), 0, (newIntCount - GetNumDWords()) * sizeof(int) ); + } + } + else + { + // Figure out how many ints are needed + AllocInts( newIntCount ); + // Initialize bitstring by clearing all bits + bClearAll = true; + } + + m_numInts = newIntCount; + } + else if ( !bClearAll && resizeNumBits >= GetNumBits() && Base() ) + { + Base()[GetNumDWords() - 1] &= GetEndMask(); + } + + if ( bClearAll && Base() ) + { + Plat_FastMemset( Base(), 0, newIntCount * sizeof(int) ); + } + + // store the new size and end mask + m_numBits = resizeNumBits; +} + +//----------------------------------------------------------------------------- +// Purpose: Allocate the storage for the ints +// Input : numInts - +//----------------------------------------------------------------------------- +inline void CVarBitVecBase::AllocInts( int numInts ) +{ + Assert( !m_pInt ); + + if ( numInts == 0 ) + return; + + if ( numInts == 1 ) + { + m_pInt = &m_iBitStringStorage; + return; + } + + m_pInt = (uint32 *)malloc( numInts * sizeof(int) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reallocate the storage for the ints +// Input : numInts - +//----------------------------------------------------------------------------- +inline void CVarBitVecBase::ReallocInts( int numInts ) +{ + Assert( Base() ); + if ( numInts == 0) + { + FreeInts(); + return; + } + + if ( m_pInt == &m_iBitStringStorage ) + { + if ( numInts != 1 ) + { + m_pInt = ((uint32 *)malloc( numInts * sizeof(int) )); + *m_pInt = m_iBitStringStorage; + } + + return; + } + + if ( numInts == 1 ) + { + m_iBitStringStorage = *m_pInt; + free( m_pInt ); + m_pInt = &m_iBitStringStorage; + return; + } + + m_pInt = (uint32 *)realloc( m_pInt, numInts * sizeof(int) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Free storage allocated with AllocInts +//----------------------------------------------------------------------------- +inline void CVarBitVecBase::FreeInts( void ) +{ + if ( m_numInts > 1 ) + { + free( m_pInt ); + } + m_pInt = NULL; +} + +#include "tier0/memdbgoff.h" + +// ------------------------------------------------------------------------ // +// CBitVecAccessor inlines. +// ------------------------------------------------------------------------ // + +inline CBitVecAccessor::CBitVecAccessor(uint32 *pDWords, int iBit) +{ + m_pDWords = pDWords; + m_iBit = iBit; +} + + +inline void CBitVecAccessor::operator=(int val) +{ + if(val) + m_pDWords[m_iBit >> 5] |= (1 << (m_iBit & 31)); + else + m_pDWords[m_iBit >> 5] &= ~(unsigned long)(1 << (m_iBit & 31)); +} + +inline CBitVecAccessor::operator uint32() +{ + return m_pDWords[m_iBit >> 5] & (1 << (m_iBit & 31)); +} + + +//============================================================================= + +#endif // BITVEC_H diff --git a/public/blockingudpsocket.cpp b/public/blockingudpsocket.cpp new file mode 100644 index 0000000..bc5336e --- /dev/null +++ b/public/blockingudpsocket.cpp @@ -0,0 +1,153 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#if defined(_WIN32) && !defined(_X360) +#include +#elif POSIX +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include +#include +#include +#include +#define closesocket close +#endif + +#include "blockingudpsocket.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +class CBlockingUDPSocket::CImpl +{ +public: + struct sockaddr_in m_SocketIP; + fd_set m_FDSet; +}; + +CBlockingUDPSocket::CBlockingUDPSocket() : + m_cserIP(), + m_Socket( 0 ), + m_pImpl( new CImpl ) +{ + CreateSocket(); +} + +CBlockingUDPSocket::~CBlockingUDPSocket() +{ + delete m_pImpl; + closesocket( static_cast( m_Socket )); +} + +bool CBlockingUDPSocket::CreateSocket (void) +{ + struct sockaddr_in address; + + m_Socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( m_Socket == INVALID_SOCKET ) + { + return false; + } + + address = m_pImpl->m_SocketIP; + + if ( SOCKET_ERROR == bind( m_Socket, ( struct sockaddr * )&address, sizeof( address ) ) ) + { + return false; + } + +#ifdef _WIN32 + if ( m_pImpl->m_SocketIP.sin_addr.S_un.S_addr == INADDR_ANY ) + { + m_pImpl->m_SocketIP.sin_addr.S_un.S_addr = 0L; + } +#elif POSIX + if ( m_pImpl->m_SocketIP.sin_addr.s_addr == INADDR_ANY ) + { + m_pImpl->m_SocketIP.sin_addr.s_addr = 0L; + } +#endif + + return true; +} + +bool CBlockingUDPSocket::WaitForMessage( float timeOutInSeconds ) +{ + struct timeval tv; + + FD_ZERO( &m_pImpl->m_FDSet ); + FD_SET( m_Socket, &m_pImpl->m_FDSet );//lint !e717 + + tv.tv_sec = (int)timeOutInSeconds; + float remainder = timeOutInSeconds - (int)timeOutInSeconds; + tv.tv_usec = (int)( remainder * 1000000 + 0.5f ); /* micro seconds */ + + if ( SOCKET_ERROR == select( ( int )m_Socket + 1, &m_pImpl->m_FDSet, NULL, NULL, &tv ) ) + { + return false; + } + + if ( FD_ISSET( m_Socket, &m_pImpl->m_FDSet) ) + { + return true; + } + + // Timed out + return false; +} + +unsigned int CBlockingUDPSocket::ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ) +{ + memset( packet_from, 0, sizeof( *packet_from ) ); + + struct sockaddr fromaddress; + int fromlen = sizeof( fromaddress ); + + int packet_length = recvfrom + ( + m_Socket, + (char *)buf, + (int)bufsize, + 0, + &fromaddress, + &fromlen + ); + + if ( SOCKET_ERROR == packet_length ) + { + return 0; + } + + // In case it's parsed as a string + buf[ packet_length ] = 0; + + // Copy over the receive address + *packet_from = *( struct sockaddr_in * )&fromaddress; + + return ( unsigned int )packet_length; +} + +bool CBlockingUDPSocket::SendSocketMessage( const struct sockaddr_in & rRecipient, const unsigned char *buf, size_t bufsize ) +{ + // Send data + int bytesSent = sendto + ( + m_Socket, + (const char *)buf, + (int)bufsize, + 0, + reinterpret_cast< const sockaddr * >( &rRecipient ), + sizeof( rRecipient ) + ); + + if ( SOCKET_ERROR == bytesSent ) + { + return false; + } + + return true; +} diff --git a/public/blockingudpsocket.h b/public/blockingudpsocket.h new file mode 100644 index 0000000..b30520a --- /dev/null +++ b/public/blockingudpsocket.h @@ -0,0 +1,39 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BLOCKINGUDPSOCKET_H +#define BLOCKINGUDPSOCKET_H +#ifdef _WIN32 +#pragma once +#endif + +#include "netadr.h" + +class CBlockingUDPSocket +{ +public: + explicit CBlockingUDPSocket(); + virtual ~CBlockingUDPSocket(); + + bool WaitForMessage( float timeOutInSeconds ); + unsigned int ReceiveSocketMessage( struct sockaddr_in *packet_from, unsigned char *buf, size_t bufsize ); + bool SendSocketMessage( const struct sockaddr_in& rRecipient, const unsigned char *buf, size_t bufsize ); + + bool IsValid() const { return m_Socket != 0; } + +protected: + bool CreateSocket (void); + + class CImpl; + CImpl *m_pImpl; + + netadr_t m_cserIP; + unsigned int m_Socket; + + +}; + +#endif // BLOCKINGUDPSOCKET_H diff --git a/public/bone_accessor.cpp b/public/bone_accessor.cpp new file mode 100644 index 0000000..bc12089 --- /dev/null +++ b/public/bone_accessor.cpp @@ -0,0 +1,37 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "bone_accessor.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +#if defined( CLIENT_DLL ) && defined( _DEBUG ) + + void CBoneAccessor::SanityCheckBone( int iBone, bool bReadable ) const + { + if ( m_pAnimating ) + { + CStudioHdr *pHdr = m_pAnimating->GetModelPtr(); + if ( pHdr ) + { + mstudiobone_t *pBone = pHdr->pBone( iBone ); + if ( bReadable ) + { + AssertOnce( pBone->flags & m_ReadableBones ); + } + else + { + AssertOnce( pBone->flags & m_WritableBones ); + } + } + } + } + +#endif + diff --git a/public/bone_accessor.h b/public/bone_accessor.h new file mode 100644 index 0000000..3023253 --- /dev/null +++ b/public/bone_accessor.h @@ -0,0 +1,131 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef BONE_ACCESSOR_H +#define BONE_ACCESSOR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "studio.h" + + +class C_BaseAnimating; + + +class CBoneAccessor +{ +public: + + CBoneAccessor(); + CBoneAccessor( matrix3x4a_t *pBones ); // This can be used to allow access to all bones. + + // Initialize. +#if defined( CLIENT_DLL ) + void Init( const C_BaseAnimating *pAnimating, matrix3x4a_t *pBones ); +#endif + + int GetReadableBones(); + void SetReadableBones( int flags ); + + int GetWritableBones(); + void SetWritableBones( int flags ); + + // Get bones for read or write access. + const matrix3x4a_t& GetBone( int iBone ) const; + const matrix3x4a_t& operator[]( int iBone ) const; + matrix3x4a_t& GetBoneForWrite( int iBone ); + + matrix3x4a_t *GetBoneArrayForWrite( ) const; + +private: + +#if defined( CLIENT_DLL ) && defined( _DEBUG ) + void SanityCheckBone( int iBone, bool bReadable ) const; +#endif + + // Only used in the client DLL for debug verification. + const C_BaseAnimating *m_pAnimating; + + matrix3x4a_t *m_pBones; + + int m_ReadableBones; // Which bones can be read. + int m_WritableBones; // Which bones can be written. +}; + + +inline CBoneAccessor::CBoneAccessor() +{ + m_pAnimating = NULL; + m_pBones = NULL; + m_ReadableBones = m_WritableBones = 0; +} + +inline CBoneAccessor::CBoneAccessor( matrix3x4a_t *pBones ) +{ + m_pAnimating = NULL; + m_pBones = pBones; +} + +#if defined( CLIENT_DLL ) + inline void CBoneAccessor::Init( const C_BaseAnimating *pAnimating, matrix3x4a_t *pBones ) + { + m_pAnimating = pAnimating; + m_pBones = pBones; + } +#endif + +inline int CBoneAccessor::GetReadableBones() +{ + return m_ReadableBones; +} + +inline void CBoneAccessor::SetReadableBones( int flags ) +{ + m_ReadableBones = flags; +} + +inline int CBoneAccessor::GetWritableBones() +{ + return m_WritableBones; +} + +inline void CBoneAccessor::SetWritableBones( int flags ) +{ + m_WritableBones = flags; +} + +inline const matrix3x4a_t& CBoneAccessor::GetBone( int iBone ) const +{ +#if defined( CLIENT_DLL ) && defined( _DEBUG ) + SanityCheckBone( iBone, true ); +#endif + return m_pBones[iBone]; +} + +inline const matrix3x4a_t& CBoneAccessor::operator[]( int iBone ) const +{ +#if defined( CLIENT_DLL ) && defined( _DEBUG ) + SanityCheckBone( iBone, true ); +#endif + return m_pBones[iBone]; +} + +inline matrix3x4a_t& CBoneAccessor::GetBoneForWrite( int iBone ) +{ +#if defined( CLIENT_DLL ) && defined( _DEBUG ) + SanityCheckBone( iBone, false ); +#endif + return m_pBones[iBone]; +} + +inline matrix3x4a_t *CBoneAccessor::GetBoneArrayForWrite( void ) const +{ + return m_pBones; +} + +#endif // BONE_ACCESSOR_H diff --git a/public/bone_setup.h b/public/bone_setup.h new file mode 100644 index 0000000..daed179 --- /dev/null +++ b/public/bone_setup.h @@ -0,0 +1,525 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BONE_SETUP_H +#define BONE_SETUP_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "studio.h" +#include "cmodel.h" +#include "bitvec.h" + + +class CBoneToWorld; +class CIKContext; +class CBoneAccessor; +class IPoseDebugger; + + +// This provides access to networked arrays, so if this code actually changes a value, +// the entity is marked as changed. +abstract_class IParameterAccess +{ +public: + virtual float GetParameter( int iParam ) = 0; + virtual void SetParameter( int iParam, float flValue ) = 0; +}; + + + +class CBoneBitList : public CBitVec +{ +public: + inline void MarkBone(int iBone) + { + Set(iBone); + } + inline bool IsBoneMarked(int iBone) + { + return Get(iBone) != 0 ? true : false; + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: blends together all the bones from two p:q lists +// +// p1 = p1 * (1 - s) + p2 * s +// q1 = q1 * (1 - s) + q2 * s +//----------------------------------------------------------------------------- +void SlerpBones( + const CStudioHdr *pStudioHdr, + Quaternion q1[MAXSTUDIOBONES], + Vector pos1[MAXSTUDIOBONES], + mstudioseqdesc_t &seqdesc, // source of q2 and pos2 + int sequence, + const QuaternionAligned q2[MAXSTUDIOBONES], + const Vector pos2[MAXSTUDIOBONES], + float s, + int boneMask + ); + + +class CBoneSetup; +class IBoneSetup +{ +public: + IBoneSetup( const CStudioHdr *pStudioHdr, int boneMask, const float poseParameter[], IPoseDebugger *pPoseDebugger = NULL ); + ~IBoneSetup( void ); + void InitPose( Vector pos[], QuaternionAligned q[] ); + void AccumulatePose( Vector pos[], Quaternion q[], int sequence, float cycle, float flWeight, float flTime, CIKContext *pIKContext ); + void CalcAutoplaySequences( Vector pos[], Quaternion q[], float flRealTime, CIKContext *pIKContext ); + void CalcBoneAdj( Vector pos[], Quaternion q[], const float controllers[] ); + + CStudioHdr *GetStudioHdr(); + +private: + CBoneSetup *m_pBoneSetup; +}; + +// Given two samples of a bone separated in time by dt, +// compute the velocity and angular velocity of that bone +void CalcBoneDerivatives( Vector &velocity, AngularImpulse &angVel, const matrix3x4_t &prev, const matrix3x4_t ¤t, float dt ); +// Give a derivative of a bone, compute the velocity & angular velocity of that bone +void CalcBoneVelocityFromDerivative( const QAngle &vecAngles, Vector &velocity, AngularImpulse &angVel, const matrix3x4_t ¤t ); + +// This function sets up the local transform for a single frame of animation. It doesn't handle +// pose parameters or interpolation between frames. +void SetupSingleBoneMatrix( + CStudioHdr *pOwnerHdr, + int nSequence, + int iFrame, + int iBone, + matrix3x4_t &mBoneLocal ); + + +// Purpose: build boneToWorld transforms for a specific bone +void BuildBoneChain( + const CStudioHdr *pStudioHdr, + const matrix3x4a_t &rootxform, + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4a_t *pBoneToWorld ); + +void BuildBoneChain( + const CStudioHdr *pStudioHdr, + const matrix3x4a_t &rootxform, + const Vector pos[], + const Quaternion q[], + int iBone, + matrix3x4a_t *pBoneToWorld, + CBoneBitList &boneComputed ); + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +// ik info +class CIKTarget +{ +public: + void SetOwner( int entindex, const Vector &pos, const QAngle &angles ); + void ClearOwner( void ); + int GetOwner( void ); + void UpdateOwner( int entindex, const Vector &pos, const QAngle &angles ); + void SetPos( const Vector &pos ); + void SetAngles( const QAngle &angles ); + void SetQuaternion( const Quaternion &q ); + void SetNormal( const Vector &normal ); + void SetPosWithNormalOffset( const Vector &pos, const Vector &normal ); + void SetOnWorld( bool bOnWorld = true ); + + bool IsActive( void ); + void IKFailed( void ); + int chain; + int type; + void MoveReferenceFrame( Vector &deltaPos, QAngle &deltaAngles ); + // accumulated offset from ideal footplant location +public: + struct x2 { + char *pAttachmentName; + Vector pos; + Quaternion q; + } offset; +private: + struct x3 { + Vector pos; + Quaternion q; + } ideal; +public: + struct x4 { + float latched; + float release; + float height; + float floor; + float radius; + float flTime; + float flWeight; + Vector pos; + Quaternion q; + bool onWorld; + } est; // estimate contact position + struct x5 { + float hipToFoot; // distance from hip + float hipToKnee; // distance from hip to knee + float kneeToFoot; // distance from knee to foot + Vector hip; // location of hip + Vector closest; // closest valid location from hip to foot that the foot can move to + Vector knee; // pre-ik location of knee + Vector farthest; // farthest valid location from hip to foot that the foot can move to + Vector lowest; // lowest position directly below hip that the foot can drop to + } trace; +private: + // internally latched footset, position + struct x1 { + // matrix3x4a_t worldTarget; + bool bNeedsLatch; + bool bHasLatch; + float influence; + int iFramecounter; + int owner; + Vector absOrigin; + QAngle absAngles; + Vector pos; + Quaternion q; + Vector deltaPos; // acculated error + Quaternion deltaQ; + Vector debouncePos; + Quaternion debounceQ; + } latched; + struct x6 { + float flTime; // time last error was detected + float flErrorTime; + float ramp; + bool bInError; + } error; + + friend class CIKContext; +}; + + +struct ikchainresult_t +{ + // accumulated offset from ideal footplant location + int target; + Vector pos; + Quaternion q; + float flWeight; +}; + + + +struct ikcontextikrule_t +{ + int index; + + int type; + int chain; + + int bone; + + int slot; // iktarget slot. Usually same as chain. + float height; + float radius; + float floor; + Vector pos; + Quaternion q; + + float start; // beginning of influence + float peak; // start of full influence + float tail; // end of full influence + float end; // end of all influence + + float top; + float drop; + + float commit; // frame footstep target should be committed + float release; // frame ankle should end rotation from latched orientation + + float flWeight; // processed version of start-end cycle + float flRuleWeight; // blending weight + float latched; // does the IK rule use a latched value? + char *szLabel; + + Vector kneeDir; + Vector kneePos; + + ikcontextikrule_t() {} + +private: + // No copy constructors allowed + ikcontextikrule_t(const ikcontextikrule_t& vOther); +}; + + +void Studio_AlignIKMatrix( matrix3x4a_t &mMat, const Vector &vAlignTo ); + +bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, matrix3x4a_t* pBoneToWorld ); + +bool Studio_SolveIK( int iThigh, int iKnee, int iFoot, Vector &targetFoot, Vector &targetKneePos, Vector &targetKneeDir, matrix3x4a_t* pBoneToWorld ); + + + +class CIKContext +{ +public: + CIKContext( ); + void Init( const CStudioHdr *pStudioHdr, const QAngle &angles, const Vector &pos, float flTime, int iFramecounter, int boneMask ); + void AddDependencies( mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, const float poseParameters[], float flWeight = 1.0f ); + + void ClearTargets( void ); + void UpdateTargets( Vector pos[], Quaternion q[], matrix3x4a_t boneToWorld[], CBoneBitList &boneComputed ); + void AutoIKRelease( void ); + void SolveDependencies( Vector pos[], Quaternion q[], matrix3x4a_t boneToWorld[], CBoneBitList &boneComputed ); + + void AddAutoplayLocks( Vector pos[], Quaternion q[] ); + void SolveAutoplayLocks( Vector pos[], Quaternion q[] ); + + void AddSequenceLocks( mstudioseqdesc_t &SeqDesc, Vector pos[], Quaternion q[] ); + void SolveSequenceLocks( mstudioseqdesc_t &SeqDesc, Vector pos[], Quaternion q[] ); + + void AddAllLocks( Vector pos[], Quaternion q[] ); + void SolveAllLocks( Vector pos[], Quaternion q[] ); + + void SolveLock( const mstudioiklock_t *plock, int i, Vector pos[], Quaternion q[], matrix3x4a_t boneToWorld[], CBoneBitList &boneComputed ); + + CUtlVectorFixed< CIKTarget, 12 > m_target; + +private: + + CStudioHdr const *m_pStudioHdr; + + bool Estimate( int iSequence, float flCycle, int iTarget, const float poseParameter[], float flWeight = 1.0f ); + void BuildBoneChain( const Vector pos[], const Quaternion q[], int iBone, matrix3x4a_t *pBoneToWorld, CBoneBitList &boneComputed ); + + // virtual IK rules, filtered and combined from each sequence + CUtlVector< CUtlVector< ikcontextikrule_t > > m_ikChainRule; + CUtlVector< ikcontextikrule_t > m_ikLock; + matrix3x4a_t m_rootxform; + + int m_iFramecounter; + float m_flTime; + int m_boneMask; +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +// replaces the bonetoworld transforms for all bones that are procedural +bool CalcProceduralBone( + const CStudioHdr *pStudioHdr, + int iBone, + CBoneAccessor &bonetoworld + ); + +void Studio_BuildMatrices( + const CStudioHdr *pStudioHdr, + const QAngle& angles, + const Vector& origin, + const Vector pos[], + const Quaternion q[], + int iBone, + float flScale, + matrix3x4a_t bonetoworld[MAXSTUDIOBONES], + int boneMask + ); + + +// Get a bone->bone relative transform +void Studio_CalcBoneToBoneTransform( const CStudioHdr *pStudioHdr, int inputBoneIndex, int outputBoneIndex, matrix3x4_t &matrixOut ); + +// Given a bone rotation value, figures out the value you need to give to the controller +// to have the bone at that value. +// [in] flValue = the desired bone rotation value +// [out] ctlValue = the (0-1) value to set the controller t. +// return value = flValue, unwrapped to lie between the controller's start and end. +float Studio_SetController( const CStudioHdr *pStudioHdr, int iController, float flValue, float &ctlValue ); + + +// Given a 0-1 controller value, maps it into the controller's start and end and returns the bone rotation angle. +// [in] ctlValue = value in controller space (0-1). +// return value = value in bone space +float Studio_GetController( const CStudioHdr *pStudioHdr, int iController, float ctlValue ); + +void Studio_CalcDefaultPoseParameters( const CStudioHdr *pStudioHdr, float flPoseParameter[MAXSTUDIOPOSEPARAM], int nCount ); +float Studio_GetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float ctlValue ); +float Studio_SetPoseParameter( const CStudioHdr *pStudioHdr, int iParameter, float flValue, float &ctlValue ); + +// converts a global 0..1 pose parameter into the local sequences blending value +void Studio_LocalPoseParameter( const CStudioHdr *pStudioHdr, const float poseParameter[], mstudioseqdesc_t &seqdesc, int iSequence, int iLocalIndex, float &flSetting, int &index ); + +void Studio_SeqAnims( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[], mstudioanimdesc_t *panim[4], float *weight ); +int Studio_MaxFrame( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ); +float Studio_FPS( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ); +float Studio_CPS( const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int iSequence, const float poseParameter[] ); +float Studio_Duration( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[] ); +void Studio_MovementRate( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], Vector *pVec ); +float Studio_SeqMovementAndDuration( const CStudioHdr *pStudioHdr, int iSequence, float flCycleFrom, float flCycleTo, const float poseParameter[], Vector &deltaPos ); + +// void Studio_Movement( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], Vector *pVec ); + +//void Studio_AnimPosition( mstudioanimdesc_t *panim, float flCycle, Vector &vecPos, Vector &vecAngle ); +//void Studio_AnimVelocity( mstudioanimdesc_t *panim, float flCycle, Vector &vecVelocity ); +//float Studio_FindAnimDistance( mstudioanimdesc_t *panim, float flDist ); +bool Studio_AnimMovement( mstudioanimdesc_t *panim, float flCycleFrom, float flCycleTo, Vector &deltaPos, QAngle &deltaAngle ); +bool Studio_SeqMovement( const CStudioHdr *pStudioHdr, int iSequence, float flCycleFrom, float flCycleTo, const float poseParameter[], Vector &deltaMovement, QAngle &deltaAngle ); +bool Studio_SeqVelocity( const CStudioHdr *pStudioHdr, int iSequence, float flCycle, const float poseParameter[], Vector &vecVelocity ); +float Studio_FindSeqDistance( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], float flDist ); +float Studio_FindSeqVelocity( const CStudioHdr *pStudioHdr, int iSequence, const float poseParameter[], float flVelocity ); +int Studio_FindAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ); +int Studio_FindRandomAttachment( const CStudioHdr *pStudioHdr, const char *pAttachmentName ); +int Studio_BoneIndexByName( const CStudioHdr *pStudioHdr, const char *pName ); +const char *Studio_GetDefaultSurfaceProps( CStudioHdr *pstudiohdr ); +float Studio_GetMass( CStudioHdr *pstudiohdr ); +const char *Studio_GetKeyValueText( const CStudioHdr *pStudioHdr, int iSequence ); + +FORWARD_DECLARE_HANDLE( memhandle_t ); +struct bonecacheparams_t +{ + CStudioHdr *pStudioHdr; + matrix3x4a_t *pBoneToWorld; + float curtime; + int boneMask; +}; + +class CBoneCache +{ +public: + + // you must implement these static functions for the ResourceManager + // ----------------------------------------------------------- + static CBoneCache *CreateResource( const bonecacheparams_t ¶ms ); + static unsigned int EstimatedSize( const bonecacheparams_t ¶ms ); + // ----------------------------------------------------------- + // member functions that must be present for the ResourceManager + void DestroyResource(); + CBoneCache *GetData() { return this; } + unsigned int Size() { return m_size; } + // ----------------------------------------------------------- + + CBoneCache(); + + // was constructor, but placement new is messy wrt memdebug - so cast & init instead + void Init( const bonecacheparams_t ¶ms, unsigned int size, short *pStudioToCached, short *pCachedToStudio, int cachedBoneCount ); + + void UpdateBones( const matrix3x4_t *pBoneToWorld, int numbones, float curtime ); + matrix3x4a_t *GetCachedBone( int studioIndex ); + void ReadCachedBones( matrix3x4_t *pBoneToWorld ); + void ReadCachedBonePointers( matrix3x4_t **bones, int numbones ); + + bool IsValid( float curtime, float dt = 0.1f ); + +public: + float m_timeValid; + int m_boneMask; + +private: + matrix3x4a_t *BoneArray(); + short *StudioToCached(); + short *CachedToStudio(); + + unsigned int m_size; + unsigned short m_cachedBoneCount; + unsigned short m_matrixOffset; + unsigned short m_cachedToStudioOffset; + unsigned short m_boneOutOffset; +}; + +void Studio_LockBoneCache(); +void Studio_UnlockBoneCache(); + +CBoneCache *Studio_GetBoneCache( memhandle_t cacheHandle, bool bLock = false ); +void Studio_ReleaseBoneCache( memhandle_t cacheHandle ); +memhandle_t Studio_CreateBoneCache( bonecacheparams_t ¶ms ); +void Studio_DestroyBoneCache( memhandle_t cacheHandle ); +void Studio_InvalidateBoneCache( memhandle_t cacheHandle ); + +// Given a ray, trace for an intersection with this studiomodel. Get the array of bones from StudioSetupHitboxBones +bool TraceToStudio( class IPhysicsSurfaceProps *pProps, const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, matrix3x4_t **hitboxbones, int fContentsMask, const Vector &vecOrigin, float flScale, trace_t &trace ); +// TERROR: TraceToStudio variant that prioritizes hitgroups, so bullets can pass through arms and chest to hit the head, for instance +bool TraceToStudioGrouped( IPhysicsSurfaceProps *pProps, const Ray_t& ray, CStudioHdr *pStudioHdr, mstudiohitboxset_t *set, + matrix3x4_t **hitboxbones, int fContentsMask, trace_t &tr, const CUtlVector< int > &sortedHitgroups ); + +void QuaternionSM( float s, const Quaternion &p, const Quaternion &q, Quaternion &qt ); +void QuaternionMA( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ); + +bool Studio_PrefetchSequence( const CStudioHdr *pStudioHdr, int iSequence ); + +void Studio_RunBoneFlexDrivers( float *pFlexController, const CStudioHdr *pStudioHdr, const Vector *pPositions, const matrix3x4_t *pBoneToWorld, const matrix3x4_t &mRootToWorld ); + +//----------------------------------------------------------------------------- +// Computes a number of twist bones given a parent/child pair +// pqTwists, pflWeights, pqTwistBinds must all have at least nCount elements +//----------------------------------------------------------------------------- +void ComputeTwistBones( + Quaternion *pqTwists, + int nCount, + bool bInverse, + const Vector &vUp, + const Quaternion &qParent, + const matrix3x4_t &mChild, + const Quaternion &qBaseInv, + const float *pflWeights, + const Quaternion *pqTwistBinds ); + + + +//----------------------------------------------------------------------------- +// Find the non-linear transforms that fit a model with attachments to another model with attachments +//----------------------------------------------------------------------------- + +class CAttachmentFit +{ +public: + CAttachmentFit() { + m_bHasConverged = false; + m_localCenter.Init(); + m_posError.Init(); + m_bHasPosition = false; + m_scaleError.Init(); + m_rotError = quat_identity; + }; + + void LocalTransform( const matrix3x4_t &matFollowBoneToWorld, matrix3x4_t &matLocalBoneToWorld ); + + int m_nFollowBone; // bone to follow, as taken from attachment matches + CUtlVector< int > m_nLocalAtt; // index of local attachments + CUtlVector< int > m_nFollowAtt; // index of attachments that are being followed + + bool m_bHasConverged; // solution for this bone converged + Vector m_localCenter; // idealized rotation center of attachments + Vector m_posError; // accumulated position error to add to transform + bool m_bHasPosition; // flag to not calc rotation/scale until position error has been estimated + Vector m_scaleError; // accumulated axis independant scale error (relative to 1) + Quaternion m_rotError; // accumulated rotation error +}; + +class CAttachmentFitter +{ +public: + CAttachmentFitter( CStudioHdr *pStudioHdr, CStudioHdr *pFollowParent ); + bool Converg( const matrix3x4_t *pFollowBoneToWorld, matrix3x4_t *pLocalBoneToWorld ); + bool LocalTransform( int iBone, const matrix3x4_t *pFollowBoneToWorld, matrix3x4_t *pLocalBoneToWorld ); + bool IsValid( void ) { return (m_pFits.Count() > 0); } + +private: + bool m_bHasConverged; + CStudioHdr *m_pLocalHdr; + CStudioHdr *m_pFollowHdr; + + CUtlVector< CAttachmentFit *> m_pFits; // indexed by bones +}; + +#endif // BONE_SETUP_H \ No newline at end of file diff --git a/public/branchingsingleton.h b/public/branchingsingleton.h new file mode 100644 index 0000000..18fa340 --- /dev/null +++ b/public/branchingsingleton.h @@ -0,0 +1,48 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Macros for defining branching singletons. +// +// A branching singleton defines a singleton class within another class, and subclasses +// of the outer class can automatically expand on that singleton at their node in the +// class branching tree with the confidence that changes will be reflected in all +// subclasses. +// +// The primary reason to have a branching singleton is to centralize management code +// without being tied explicitly to one interface. The interface can possibly change +// vastly as it gets passed down the tree to the point where all the original functions +// are stubs and the interface uses an entirely different set of functions. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BRANCHINGSINGLETON_H +#define BRANCHINGSINGLETON_H +#ifdef _WIN32 +#pragma once +#endif + + +#define START_BRANCHING_SINGLETON_DEFINITION_NOBASE( classname ) class classname + +#define START_BRANCHING_SINGLETON_DEFINITION( classname ) class classname : public Base##classname + +#define _END_BRANCHING_SINGLETON_DEFINITION( classname );\ + static classname *Get_##classname##_Static( void )\ + {\ + static classname s_Singleton;\ + return &s_Singleton;\ + }\ + \ + virtual Root##classname *Get_##classname##( void )\ + {\ + return Get_##classname##_Static();\ + }\ + typedef classname Base##classname; + +#define END_BRANCHING_SINGLETON_DEFINITION( classname ) _END_BRANCHING_SINGLETON_DEFINITION( classname ) + +#define END_BRANCHING_SINGLETON_DEFINITION_NOBASE( classname );\ + typedef classname Root##classname;\ + _END_BRANCHING_SINGLETON_DEFINITION( classname ); + +#endif //#ifndef BRANCHINGSINGLETON_H diff --git a/public/bspfile.h b/public/bspfile.h new file mode 100644 index 0000000..479f224 --- /dev/null +++ b/public/bspfile.h @@ -0,0 +1,1285 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines and structures for the BSP file format. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BSPFILE_H +#define BSPFILE_H +#pragma once + +#ifndef MATHLIB_H +#include "mathlib/mathlib.h" +#endif + +#include "mathlib/vector4d.h" +#include "datamap.h" +#include "mathlib/bumpvects.h" +#include "mathlib/compressed_light_cube.h" + +// little-endian "VBSP" +#define IDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'V') + +// MINBSPVERSION is the minimum acceptable version. The engine will load MINBSPVERSION through BSPVERSION +#define MINBSPVERSION 19 +#define BSPVERSION 21 + + +// This needs to match the value in gl_lightmap.h +// Need to dynamically allocate the weights and light values in radial_t to make this variable. +#define MAX_BRUSH_LIGHTMAP_DIM_WITHOUT_BORDER 32 +// This is one more than what vbsp cuts for to allow for rounding errors +#define MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER 35 + +// We can have larger lightmaps on displacements +#define MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER 125 +#define MAX_DISP_LIGHTMAP_DIM_INCLUDING_BORDER 128 + + +// This is the actual max.. (change if you change the brush lightmap dim or disp lightmap dim +#define MAX_LIGHTMAP_DIM_WITHOUT_BORDER MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER +#define MAX_LIGHTMAP_DIM_INCLUDING_BORDER MAX_DISP_LIGHTMAP_DIM_INCLUDING_BORDER + +#define MAX_LIGHTSTYLES 64 + + +// upper design bounds +#define MIN_MAP_DISP_POWER 2 // Minimum and maximum power a displacement can be. +#define MAX_MAP_DISP_POWER 4 + +// Max # of neighboring displacement touching a displacement's corner. +#define MAX_DISP_CORNER_NEIGHBORS 4 + +#define NUM_DISP_POWER_VERTS(power) ( ((1 << (power)) + 1) * ((1 << (power)) + 1) ) +#define NUM_DISP_POWER_TRIS(power) ( (1 << (power)) * (1 << (power)) * 2 ) + +#if !defined( BSP_USE_LESS_MEMORY ) +// Common limits +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define MAX_MAP_MODELS 1024 +#define MAX_MAP_BRUSHES 8192 +#define MAX_MAP_ENTITIES 16384 +#define MAX_MAP_TEXINFO 12288 +#define MAX_MAP_TEXDATA 2048 +#define MAX_MAP_DISPINFO 2048 +#define MAX_MAP_DISP_VERTS ( MAX_MAP_DISPINFO * ((1< x --------------> 2 +// +// ^ ^ +// | | +// | | +// M2C | | M2C +// | | +// | | +// +// x x x +// +// ^ ^ +// | | +// | | +// C2M | | C2M +// | | +// | | +// +// 0 --------------> x --------------> 3 +// +// C2M M2C +// +// +// The CHILDNODE_ defines can be used to refer to a node's child nodes (this is for when you're +// recursing into the node tree inside a displacement): +// +// --------- +// | | | +// | 1 | 0 | +// | | | +// |---x---| +// | | | +// | 2 | 3 | +// | | | +// --------- +// +// ------------------------------------------------------------------------------------------------ // + +// These can be used to index g_ChildNodeIndexMul. +enum +{ + CHILDNODE_UPPER_RIGHT=0, + CHILDNODE_UPPER_LEFT=1, + CHILDNODE_LOWER_LEFT=2, + CHILDNODE_LOWER_RIGHT=3 +}; + + +// Corner indices. Used to index m_CornerNeighbors. +enum +{ + CORNER_LOWER_LEFT=0, + CORNER_UPPER_LEFT=1, + CORNER_UPPER_RIGHT=2, + CORNER_LOWER_RIGHT=3 +}; + + +// These edge indices must match the edge indices of the CCoreDispSurface. +enum +{ + NEIGHBOREDGE_LEFT=0, + NEIGHBOREDGE_TOP=1, + NEIGHBOREDGE_RIGHT=2, + NEIGHBOREDGE_BOTTOM=3 +}; + + +// These denote where one dispinfo fits on another. +// Note: tables are generated based on these indices so make sure to update +// them if these indices are changed. +typedef enum +{ + CORNER_TO_CORNER=0, + CORNER_TO_MIDPOINT=1, + MIDPOINT_TO_CORNER=2 +} NeighborSpan; + + +// These define relative orientations of displacement neighbors. +typedef enum +{ + ORIENTATION_CCW_0=0, + ORIENTATION_CCW_90=1, + ORIENTATION_CCW_180=2, + ORIENTATION_CCW_270=3 +} NeighborOrientation; + + +//============================================================================= + +enum +{ + LUMP_ENTITIES = 0, // * + LUMP_PLANES = 1, // * + LUMP_TEXDATA = 2, // * + LUMP_VERTEXES = 3, // * + LUMP_VISIBILITY = 4, // * + LUMP_NODES = 5, // * + LUMP_TEXINFO = 6, // * + LUMP_FACES = 7, // * + LUMP_LIGHTING = 8, // * + LUMP_OCCLUSION = 9, + LUMP_LEAFS = 10, // * + LUMP_FACEIDS = 11, + LUMP_EDGES = 12, // * + LUMP_SURFEDGES = 13, // * + LUMP_MODELS = 14, // * + LUMP_WORLDLIGHTS = 15, // + LUMP_LEAFFACES = 16, // * + LUMP_LEAFBRUSHES = 17, // * + LUMP_BRUSHES = 18, // * + LUMP_BRUSHSIDES = 19, // * + LUMP_AREAS = 20, // * + LUMP_AREAPORTALS = 21, // * + LUMP_PROPCOLLISION = 22, // static props convex hull lists + LUMP_PROPHULLS = 23, // static prop convex hulls + LUMP_PROPHULLVERTS = 24, // static prop collision verts + LUMP_PROPTRIS = 25, // static prop per hull triangle index start/count + + LUMP_DISPINFO = 26, + LUMP_ORIGINALFACES = 27, + LUMP_PHYSDISP = 28, + LUMP_PHYSCOLLIDE = 29, + LUMP_VERTNORMALS = 30, + LUMP_VERTNORMALINDICES = 31, + LUMP_DISP_LIGHTMAP_ALPHAS = 32, + LUMP_DISP_VERTS = 33, // CDispVerts + LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS = 34, // For each displacement + // For each lightmap sample + // byte for index + // if 255, then index = next byte + 255 + // 3 bytes for barycentric coordinates + // The game lump is a method of adding game-specific lumps + // FIXME: Eventually, all lumps could use the game lump system + LUMP_GAME_LUMP = 35, + LUMP_LEAFWATERDATA = 36, + LUMP_PRIMITIVES = 37, + LUMP_PRIMVERTS = 38, + LUMP_PRIMINDICES = 39, + // A pak file can be embedded in a .bsp now, and the file system will search the pak + // file first for any referenced names, before deferring to the game directory + // file system/pak files and finally the base directory file system/pak files. + LUMP_PAKFILE = 40, + LUMP_CLIPPORTALVERTS = 41, + // A map can have a number of cubemap entities in it which cause cubemap renders + // to be taken after running vrad. + LUMP_CUBEMAPS = 42, + LUMP_TEXDATA_STRING_DATA = 43, + LUMP_TEXDATA_STRING_TABLE = 44, + LUMP_OVERLAYS = 45, + LUMP_LEAFMINDISTTOWATER = 46, + LUMP_FACE_MACRO_TEXTURE_INFO = 47, + LUMP_DISP_TRIS = 48, + LUMP_PROP_BLOB = 49, // static prop triangle & string data + LUMP_WATEROVERLAYS = 50, + LUMP_LEAF_AMBIENT_INDEX_HDR = 51, // index of LUMP_LEAF_AMBIENT_LIGHTING_HDR + LUMP_LEAF_AMBIENT_INDEX = 52, // index of LUMP_LEAF_AMBIENT_LIGHTING + + // optional lumps for HDR + LUMP_LIGHTING_HDR = 53, + LUMP_WORLDLIGHTS_HDR = 54, + LUMP_LEAF_AMBIENT_LIGHTING_HDR = 55, // NOTE: this data overrides part of the data stored in LUMP_LEAFS. + LUMP_LEAF_AMBIENT_LIGHTING = 56, // NOTE: this data overrides part of the data stored in LUMP_LEAFS. + + LUMP_XZIPPAKFILE = 57, // deprecated. xbox 1: xzip version of pak file + LUMP_FACES_HDR = 58, // HDR maps may have different face data. + LUMP_MAP_FLAGS = 59, // extended level-wide flags. not present in all levels + LUMP_OVERLAY_FADES = 60, // Fade distances for overlays + LUMP_OVERLAY_SYSTEM_LEVELS = 61, // System level settings (min/max CPU & GPU to render this overlay) + LUMP_PHYSLEVEL = 62, + LUMP_DISP_MULTIBLEND = 63, // Displacement multiblend info +}; + + +// Lumps that have versions are listed here +enum +{ + LUMP_LIGHTING_VERSION = 1, + LUMP_FACES_VERSION = 1, + LUMP_OCCLUSION_VERSION = 2, + LUMP_LEAFS_VERSION = 1, + LUMP_LEAF_AMBIENT_LIGHTING_VERSION = 1, + LUMP_WORLDLIGHTS_VERSION = 1 +}; + + +#define HEADER_LUMPS 64 + +#include "zip_uncompressed.h" + +struct lump_t +{ + DECLARE_BYTESWAP_DATADESC(); + int fileofs, filelen; + int version; // default to zero + char fourCC[4]; // default to ( char )0, ( char )0, ( char )0, ( char )0 +}; + +struct BSPHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int ident; + int m_nVersion; + lump_t lumps[HEADER_LUMPS]; + int mapRevision; // the map's revision (iteration, version) number (added BSPVERSION 6) +}; + +// level feature flags +#define LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR 0x00000001 // was processed by vrad with -staticproplighting, no hdr data +#define LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR 0x00000002 // was processed by vrad with -staticproplighting, in hdr + +struct dflagslump_t +{ + DECLARE_BYTESWAP_DATADESC(); + uint32 m_LevelFlags; // LVLFLAGS_xxx +}; + +struct lumpfileheader_t +{ + int lumpOffset; + int lumpID; + int lumpVersion; + int lumpLength; + int mapRevision; // the map's revision (iteration, version) number (added BSPVERSION 6) +}; + +struct dgamelumpheader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int lumpCount; + + // dgamelump_t follow this +}; + +// This is expected to be a four-CC code ('lump') +typedef int GameLumpId_t; + +// 360 only: game lump is compressed, filelen reflects original size +// use next entry fileofs to determine actual disk lump compressed size +// compression stage ensures a terminal null dictionary entry +#define GAMELUMPFLAG_COMPRESSED 0x0001 + +struct dgamelump_t +{ + DECLARE_BYTESWAP_DATADESC(); + GameLumpId_t id; + unsigned short flags; + unsigned short version; + int fileofs; + int filelen; +}; + +extern int g_MapRevision; + +struct dmodel_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector mins, maxs; + Vector origin; // for sounds or lights + int headnode; + int firstface, numfaces; // submodels just draw faces without walking the bsp tree +}; + +struct dphysmodel_t +{ + DECLARE_BYTESWAP_DATADESC() + int modelIndex; + int dataSize; + int keydataSize; + int solidCount; +}; + + +struct dphyslevelpolytope_t +{ + DECLARE_BYTESWAP_DATADESC() + +}; + + +struct DiskPhysics2Polytope_t +{ + DECLARE_BYTESWAP_DATADESC(); + int32 offsetPolytope; // this is the offset to the serialized data of this polytope + int32 offsetInertia; +}; + +/* +struct DiskPhysics2LevelMesh_t +{ + DECLARE_BYTESWAP_DATADESC(); + DataLinker::Offset_t polymesh; // this is polysoup in data version 0xC0000002, and polytope in 0xC0000001 + int32 flags; + + enum { FLAG_RESERVED = 1, FLAG_FORCE_POLYSOUP = 1 << 1, FLAG_FORCE_POLYTOPE = 1 << 2 }; +}; + +struct dphyslevelV0_t +{ + DECLARE_BYTESWAP_DATADESC() + enum {DATA_VERSION_WITH_DISPLACEMENT = 0xC0000001}; + enum {DATA_VERSION = 0xC0000002}; + int32 toolVersion; // increment this for backward-compatible data changes (changes that the old code can read without problems) + int32 dataVersion; // change this for backward-incompatible changes + int32 sizeofDiskPhysics2LevelMesh; + int32 buildTime; + DataLinker::OffsetAndSize_t levelMeshes; + DataLinker::Offset_t polysoup; + DataLinker::Offset_t mopp; + DataLinker::Offset_t displacementMesh; + DataLinker::Offset_t staticProps; // serialized polysoup + DataLinker::OffsetAndSize_t levelWaterMeshes; + DataLinker::OffsetAndSize_t< DiskPhysics2LevelMesh_t > levelCModels; // the entities that belong to bsp, implementing features from old physics + DataLinker::OffsetAndSize_t< DataLinker::Offset_t > levelStaticModels; + int32 nReserved2[8]; +}; +*/ + + +// contains the binary blob for each displacement surface's virtual hull +struct dphysdisp_t +{ + DECLARE_BYTESWAP_DATADESC() + unsigned short numDisplacements; + //unsigned short dataSize[numDisplacements]; +}; + +struct dprophull_t +{ + DECLARE_BYTESWAP_DATADESC(); + int m_nVertCount; + int m_nVertStart; + int m_nSurfaceProp; + unsigned int m_nContents; +}; + +struct dprophulltris_t +{ + DECLARE_BYTESWAP_DATADESC(); + int m_nIndexStart; + int m_nIndexCount; +}; + +struct dpropcollision_t +{ + DECLARE_BYTESWAP_DATADESC(); + int m_nHullCount; + int m_nHullStart; +}; + +struct dvertex_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector point; +}; + +// planes (x&~1) and (x&~1)+1 are always opposites +struct dplane_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector normal; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +}; + +#ifndef BSPFLAGS_H +#include "bspflags.h" +#endif + +struct dnode_t +{ + DECLARE_BYTESWAP_DATADESC(); + int planenum; + int children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for frustom culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides + short area; // If all leaves below this node are in the same area, then + // this is the area index. If not, this is -1. +}; + +typedef struct texinfo_s +{ + DECLARE_BYTESWAP_DATADESC(); + float textureVecsTexelsPerWorldUnits[2][4]; // [s/t][xyz offset] + float lightmapVecsLuxelsPerWorldUnits[2][4]; // [s/t][xyz offset] - length is in units of texels/area + int flags; // miptex flags + overrides + int texdata; // Pointer to texture name, size, etc. +} texinfo_t; + +#define TEXTURE_NAME_LENGTH 128 // changed from 64 BSPVERSION 8 + +struct dtexdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector reflectivity; + int nameStringTableID; // index into g_StringTable for the texture name + int width, height; // source image + int view_width, view_height; // +}; + + +//----------------------------------------------------------------------------- +// Occluders are simply polygons +//----------------------------------------------------------------------------- +// Flags field of doccluderdata_t +enum +{ + OCCLUDER_FLAGS_INACTIVE = 0x1, +}; + +struct doccluderdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + int flags; + int firstpoly; // index into doccluderpolys + int polycount; + Vector mins; + Vector maxs; + int area; +}; + +struct doccluderdataV1_t +{ + int flags; + int firstpoly; // index into doccluderpolys + int polycount; + Vector mins; + Vector maxs; +}; + +struct doccluderpolydata_t +{ + DECLARE_BYTESWAP_DATADESC(); + int firstvertexindex; // index into doccludervertindices + int vertexcount; + int planenum; +}; + + +// NOTE: see the section above titled "displacement neighbor rules". +struct CDispSubNeighbor +{ +public: + DECLARE_BYTESWAP_DATADESC(); + unsigned short GetNeighborIndex() const { return m_iNeighbor; } + NeighborSpan GetSpan() const { return (NeighborSpan)m_Span; } + NeighborSpan GetNeighborSpan() const { return (NeighborSpan)m_NeighborSpan; } + NeighborOrientation GetNeighborOrientation() const { return (NeighborOrientation)m_NeighborOrientation; } + + bool IsValid() const { return m_iNeighbor != 0xFFFF; } + void SetInvalid() { m_iNeighbor = 0xFFFF; } + + +public: + unsigned short m_iNeighbor; // This indexes into ddispinfos. + // 0xFFFF if there is no neighbor here. + + unsigned char m_NeighborOrientation; // (CCW) rotation of the neighbor wrt this displacement. + + // These use the NeighborSpan type. + unsigned char m_Span; // Where the neighbor fits onto this side of our displacement. + unsigned char m_NeighborSpan; // Where we fit onto our neighbor. +}; + + +// NOTE: see the section above titled "displacement neighbor rules". +class CDispNeighbor +{ +public: + DECLARE_BYTESWAP_DATADESC(); + void SetInvalid() { m_SubNeighbors[0].SetInvalid(); m_SubNeighbors[1].SetInvalid(); } + + // Returns false if there isn't anything touching this edge. + bool IsValid() { return m_SubNeighbors[0].IsValid() || m_SubNeighbors[1].IsValid(); } + + +public: + // Note: if there is a neighbor that fills the whole side (CORNER_TO_CORNER), + // then it will always be in CDispNeighbor::m_Neighbors[0] + CDispSubNeighbor m_SubNeighbors[2]; +}; + + +class CDispCornerNeighbors +{ +public: + DECLARE_BYTESWAP_DATADESC(); + void SetInvalid() { m_nNeighbors = 0; } + + +public: + unsigned short m_Neighbors[MAX_DISP_CORNER_NEIGHBORS]; // indices of neighbors. + unsigned char m_nNeighbors; +}; + + +class CDispVert +{ +public: + DECLARE_BYTESWAP_DATADESC(); + Vector m_vVector; // Vector field defining displacement volume. + float m_flDist; // Displacement distances. + float m_flAlpha; // "per vertex" alpha values. +}; + +#define DISPTRI_TAG_SURFACE (1<<0) +#define DISPTRI_TAG_WALKABLE (1<<1) +#define DISPTRI_TAG_BUILDABLE (1<<2) +#define DISPTRI_FLAG_SURFPROP1 (1<<3) +#define DISPTRI_FLAG_SURFPROP2 (1<<4) + +class CDispTri +{ +public: + DECLARE_BYTESWAP_DATADESC(); + unsigned short m_uiTags; // Displacement triangle tags. +}; + +#define MAX_MULTIBLEND_CHANNELS 4 + +class CDispMultiBlend +{ +public: + DECLARE_BYTESWAP_DATADESC(); + + Vector4D m_vMultiBlend; + Vector4D m_vAlphaBlend; + Vector m_vMultiBlendColors[ MAX_MULTIBLEND_CHANNELS ]; +}; + +#define DISP_INFO_FLAG_HAS_MULTIBLEND 0x40000000 +#define DISP_INFO_FLAG_MAGIC 0x80000000 + +class ddispinfo_t +{ +public: + DECLARE_BYTESWAP_DATADESC(); + int NumVerts() const { return NUM_DISP_POWER_VERTS(power); } + int NumTris() const { return NUM_DISP_POWER_TRIS(power); } + +public: + Vector startPosition; // start position used for orientation -- (added BSPVERSION 6) + int m_iDispVertStart; // Index into LUMP_DISP_VERTS. + int m_iDispTriStart; // Index into LUMP_DISP_TRIS. + + int power; // power - indicates size of map (2^power + 1) + int minTess; // minimum tesselation allowed + float smoothingAngle; // lighting smoothing angle + int contents; // surface contents + + unsigned short m_iMapFace; // Which map face this displacement comes from. + + int m_iLightmapAlphaStart; // Index into ddisplightmapalpha. + // The count is m_pParent->lightmapTextureSizeInLuxels[0]*m_pParent->lightmapTextureSizeInLuxels[1]. + + int m_iLightmapSamplePositionStart; // Index into LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS. + + CDispNeighbor m_EdgeNeighbors[4]; // Indexed by NEIGHBOREDGE_ defines. + CDispCornerNeighbors m_CornerNeighbors[4]; // Indexed by CORNER_ defines. + + enum unnamed { ALLOWEDVERTS_SIZE = PAD_NUMBER( MAX_DISPVERTS, 32 ) / 32 }; + unsigned long m_AllowedVerts[ALLOWEDVERTS_SIZE]; // This is built based on the layout and sizes of our neighbors + // and tells us which vertices are allowed to be active. +}; + + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +struct dedge_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short v[2]; // vertex numbers +}; + +#define MAXLIGHTMAPS 4 + +enum dprimitive_type +{ + PRIM_TRILIST=0, + PRIM_TRISTRIP=1, +}; + +struct dprimitive_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned char type; + unsigned short firstIndex; + unsigned short indexCount; + unsigned short firstVert; + unsigned short vertCount; +}; + +struct dprimvert_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector pos; +}; + +struct dface_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short planenum; + byte side; // faces opposite to the node's plane direction + byte onNode; // 1 of on node, 0 if in leaf + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + // This is a union under the assumption that a fog volume boundary (ie. water surface) + // isn't a displacement map. + // FIXME: These should be made a union with a flags or type field for which one it is + // if we can add more to this. +// union +// { + short dispinfo; + // This is only for surfaces that are the boundaries of fog volumes + // (ie. water surfaces) + // All of the rest of the surfaces can look at their leaf to find out + // what fog volume they are in. + short surfaceFogVolumeID; +// }; + + // lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples + float area; + + // TODO: make these unsigned chars? + int m_LightmapTextureMinsInLuxels[2]; + int m_LightmapTextureSizeInLuxels[2]; + + int origFace; // reference the original face this face was derived from + + +public: + + unsigned short GetNumPrims() const; + void SetNumPrims( unsigned short nPrims ); + bool AreDynamicShadowsEnabled(); + void SetDynamicShadowsEnabled( bool bEnabled ); + + // non-polygon primitives (strips and lists) +private: + unsigned short m_NumPrims; // Top bit, if set, disables shadows on this surface (this is why there are accessors). + +public: + unsigned short firstPrimID; + + unsigned int smoothingGroups; +}; + + +inline unsigned short dface_t::GetNumPrims() const +{ + return m_NumPrims & 0x7FFF; +} + +inline void dface_t::SetNumPrims( unsigned short nPrims ) +{ + Assert( (nPrims & 0x8000) == 0 ); + m_NumPrims &= ~0x7FFF; + m_NumPrims |= (nPrims & 0x7FFF); +} + +inline bool dface_t::AreDynamicShadowsEnabled() +{ + return (m_NumPrims & 0x8000) == 0; +} + +inline void dface_t::SetDynamicShadowsEnabled( bool bEnabled ) +{ + if ( bEnabled ) + m_NumPrims &= ~0x8000; + else + m_NumPrims |= 0x8000; +} + +struct dfaceid_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short hammerfaceid; +}; + + +// NOTE: Only 7-bits stored on disk!!! +#define LEAF_FLAGS_SKY 0x01 // This leaf has 3D sky in its PVS +#define LEAF_FLAGS_RADIAL 0x02 // This leaf culled away some portals due to radial vis +#define LEAF_FLAGS_SKY2D 0x04 // This leaf has 2D sky in its PVS +#define LEAF_FLAGS_CONTAINS_DETAILOBJECTS 0x08 // this leaf has at least one detail object in it (set by loader). + +#if defined( _X360 ) +#pragma bitfield_order( push, lsb_to_msb ) +#endif +#pragma warning( disable:4201 ) // C4201: nonstandard extension used: nameless struct/union +struct dleaf_version_0_t +{ + DECLARE_BYTESWAP_DATADESC(); + int contents; // OR of all brushes (not needed?) + + short cluster; + + BEGIN_BITFIELD( bf ); + short area:9; + short flags:7; // Per leaf flags. + END_BITFIELD(); + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; + short leafWaterDataID; // -1 for not in water + + // Precaculated light info for entities. + CompressedLightCube m_AmbientLighting; +}; + +// version 1 +struct dleaf_t +{ + DECLARE_BYTESWAP_DATADESC(); + int contents; // OR of all brushes (not needed?) + + short cluster; + + BEGIN_BITFIELD( bf ); + short area:9; + short flags:7; // Per leaf flags. + END_BITFIELD(); + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstleafface; + unsigned short numleaffaces; + + unsigned short firstleafbrush; + unsigned short numleafbrushes; + short leafWaterDataID; // -1 for not in water + + // NOTE: removed this for version 1 and moved into separate lump "LUMP_LEAF_AMBIENT_LIGHTING" or "LUMP_LEAF_AMBIENT_LIGHTING_HDR" + // Precaculated light info for entities. +// CompressedLightCube m_AmbientLighting; +}; +#pragma warning( default:4201 ) // C4201: nonstandard extension used: nameless struct/union +#if defined( _X360 ) +#pragma bitfield_order( pop ) +#endif + +// each leaf contains N samples of the ambient lighting +// each sample contains a cube of ambient light projected on to each axis +// and a sampling position encoded as a 0.8 fraction (mins=0,maxs=255) of the leaf's bounding box +struct dleafambientlighting_t +{ + DECLARE_BYTESWAP_DATADESC(); + CompressedLightCube cube; + byte x; // fixed point fraction of leaf bounds + byte y; // fixed point fraction of leaf bounds + byte z; // fixed point fraction of leaf bounds + byte pad; // unused +}; + +struct dleafambientindex_t +{ + DECLARE_BYTESWAP_DATADESC(); + + unsigned short ambientSampleCount; + unsigned short firstAmbientSample; +}; + +struct dbrushside_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short planenum; // facing out of the leaf + short texinfo; + short dispinfo; // displacement info (BSPVERSION 7) + byte bevel; // is the side a bevel plane? (BSPVERSION 7) + byte thin; // is a thin side? +}; + +struct dbrush_t +{ + DECLARE_BYTESWAP_DATADESC(); + int firstside; + int numsides; + int contents; +}; + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the visibility lump consists of a header with a count, then +// byte offsets for the PVS and PHS of each cluster, then the raw +// compressed bit vectors +#define DVIS_PVS 0 +#define DVIS_PAS 1 +struct dvis_t +{ + int numclusters; + int bitofs[8][2]; // bitofs[numclusters][2] +}; + +// each area has a list of portals that lead into other areas +// when portals are closed, other areas may not be visible or +// hearable even if the vis info says that it should be +struct dareaportal_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short m_PortalKey; // Entities have a key called portalnumber (and in vbsp a variable + // called areaportalnum) which is used + // to bind them to the area portals by comparing with this value. + + unsigned short otherarea; // The area this portal looks into. + + unsigned short m_FirstClipPortalVert; // Portal geometry. + unsigned short m_nClipPortalVerts; + + int planenum; +}; + + +struct darea_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numareaportals; + int firstareaportal; +}; + +struct dleafwaterdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + float surfaceZ; + float minZ; + short surfaceTexInfoID; +}; + +class CFaceMacroTextureInfo +{ +public: + DECLARE_BYTESWAP_DATADESC(); + // This looks up into g_TexDataStringTable, which looks up into g_TexDataStringData. + // 0xFFFF if the face has no macro texture. + unsigned short m_MacroTextureNameID; +}; + +// lights that were used to illuminate the world +enum emittype_t +{ + emit_surface, // 90 degree spotlight + emit_point, // simple point light source + emit_spotlight, // spotlight with penumbra + emit_skylight, // directional light with no falloff (surface must trace to SKY texture) + emit_quakelight, // linear falloff, non-lambertian + emit_skyambient, // spherical light source with no falloff (surface must trace to SKY texture) +}; + + +// Flags for dworldlight_t::flags +#define DWL_FLAGS_INAMBIENTCUBE 0x0001 // This says that the light was put into the per-leaf ambient cubes. +#define DWL_FLAGS_CASTENTITYSHADOWS 0x0002 // This says that the light will cast shadows from entities + +// Old version of the worldlight struct, used for backward compatibility loading. +struct dworldlight_version0_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector origin; + Vector intensity; + Vector normal; // for surfaces and spotlights + int cluster; + emittype_t type; + int style; + float stopdot; // start of penumbra for emit_spotlight + float stopdot2; // end of penumbra for emit_spotlight + float exponent; // + float radius; // cutoff distance + // falloff for emit_spotlight + emit_point: + // 1 / (constant_attn + linear_attn * dist + quadratic_attn * dist^2) + float constant_attn; + float linear_attn; + float quadratic_attn; + int flags; // Uses a combination of the DWL_FLAGS_ defines. + int texinfo; // + int owner; // entity that this light it relative to +}; + +struct dworldlight_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector origin; + Vector intensity; + Vector normal; // for surfaces and spotlights + Vector shadow_cast_offset; // gets added to the light origin when this light is used as a shadow caster (only if DWL_FLAGS_CASTENTITYSHADOWS flag is set) + int cluster; + emittype_t type; + int style; + float stopdot; // start of penumbra for emit_spotlight + float stopdot2; // end of penumbra for emit_spotlight + float exponent; // + float radius; // cutoff distance + // falloff for emit_spotlight + emit_point: + // 1 / (constant_attn + linear_attn * dist + quadratic_attn * dist^2) + float constant_attn; + float linear_attn; + float quadratic_attn; + int flags; // Uses a combination of the DWL_FLAGS_ defines. + int texinfo; // + int owner; // entity that this light it relative to +}; + +struct dcubemapsample_t +{ + DECLARE_BYTESWAP_DATADESC(); + int origin[3]; // position of light snapped to the nearest integer + // the filename for the vtf file is derived from the position + unsigned char size; // 0 - default + // otherwise, 1<<(size-1) +}; + +#define OVERLAY_BSP_FACE_COUNT 64 + +#define OVERLAY_NUM_RENDER_ORDERS (1<> (16 - OVERLAY_RENDER_ORDER_NUM_BITS)); +} + + +struct doverlayfade_t +{ + DECLARE_BYTESWAP_DATADESC(); + + float flFadeDistMinSq; + float flFadeDistMaxSq; +}; + + +struct doverlaysystemlevel_t +{ + DECLARE_BYTESWAP_DATADESC(); + + unsigned char nMinCPULevel; + unsigned char nMaxCPULevel; + unsigned char nMinGPULevel; + unsigned char nMaxGPULevel; +}; + + +#define WATEROVERLAY_BSP_FACE_COUNT 256 +#define WATEROVERLAY_RENDER_ORDER_NUM_BITS 2 +#define WATEROVERLAY_NUM_RENDER_ORDERS (1<> ( 16 - WATEROVERLAY_RENDER_ORDER_NUM_BITS ) ); +} + +#ifndef _DEF_BYTE_ +#define _DEF_BYTE_ +typedef unsigned char byte; +typedef unsigned short word; +#endif + + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +//=============== + + +struct epair_t +{ + epair_t *next; + char *key; + char *value; +}; + +// finalized page of surface's lightmaps +#define MAX_LIGHTMAPPAGE_WIDTH 256 +#define MAX_LIGHTMAPPAGE_HEIGHT 128 +typedef struct nameForDatadesc_dlightmappage_t // unnamed structs collide in the datadesc macros +{ + DECLARE_BYTESWAP_DATADESC(); + byte data[MAX_LIGHTMAPPAGE_WIDTH*MAX_LIGHTMAPPAGE_HEIGHT]; + byte palette[256*4]; +} dlightmappage_t; + +typedef struct nameForDatadesc_dlightmappageinfo_t // unnamed structs collide in the datadesc macros +{ + DECLARE_BYTESWAP_DATADESC(); + byte page; // lightmap page [0..?] + byte offset[2]; // offset into page (s,t) + byte pad; // unused + ColorRGBExp32 avgColor; // average used for runtime lighting calcs +} dlightmappageinfo_t; + +#endif // BSPFILE_H diff --git a/public/bspflags.h b/public/bspflags.h new file mode 100644 index 0000000..0cb0004 --- /dev/null +++ b/public/bspflags.h @@ -0,0 +1,156 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef BSPFLAGS_H +#define BSPFLAGS_H + +#ifdef _WIN32 +#pragma once +#endif + +// contents flags are seperate bits +// a given brush can contribute multiple content bits +// multiple brushes can be in a single leaf + +// these definitions also need to be in q_shared.h! + +// lower bits are stronger, and will eat weaker brushes completely +#define CONTENTS_EMPTY 0 // No contents + +#define CONTENTS_SOLID 0x1 // an eye is never valid in a solid +#define CONTENTS_WINDOW 0x2 // translucent, but not watery (glass) +#define CONTENTS_AUX 0x4 +#define CONTENTS_GRATE 0x8 // alpha-tested "grate" textures. Bullets/sight pass through, but solids don't +#define CONTENTS_SLIME 0x10 +#define CONTENTS_WATER 0x20 +#define CONTENTS_BLOCKLOS 0x40 // block AI line of sight +#define CONTENTS_OPAQUE 0x80 // things that cannot be seen through (may be non-solid though) +#define LAST_VISIBLE_CONTENTS CONTENTS_OPAQUE + +#define ALL_VISIBLE_CONTENTS (LAST_VISIBLE_CONTENTS | (LAST_VISIBLE_CONTENTS-1)) + +#define CONTENTS_TESTFOGVOLUME 0x100 +#define CONTENTS_UNUSED 0x200 + +// unused +// NOTE: If it's visible, grab from the top + update LAST_VISIBLE_CONTENTS +// if not visible, then grab from the bottom. +// CONTENTS_OPAQUE + SURF_NODRAW count as CONTENTS_OPAQUE (shadow-casting toolsblocklight textures) +#define CONTENTS_BLOCKLIGHT 0x400 + +#define CONTENTS_TEAM1 0x800 // per team contents used to differentiate collisions +#define CONTENTS_TEAM2 0x1000 // between players and objects on different teams + +// ignore CONTENTS_OPAQUE on surfaces that have SURF_NODRAW +#define CONTENTS_IGNORE_NODRAW_OPAQUE 0x2000 + +// hits entities which are MOVETYPE_PUSH (doors, plats, etc.) +#define CONTENTS_MOVEABLE 0x4000 + +// remaining contents are non-visible, and don't eat brushes +#define CONTENTS_AREAPORTAL 0x8000 + +#define CONTENTS_PLAYERCLIP 0x10000 +#define CONTENTS_MONSTERCLIP 0x20000 + +// currents can be added to any other contents, and may be mixed +#define CONTENTS_CURRENT_0 0x40000 +#define CONTENTS_CURRENT_90 0x80000 +#define CONTENTS_CURRENT_180 0x100000 +#define CONTENTS_CURRENT_270 0x200000 +#define CONTENTS_CURRENT_UP 0x400000 +#define CONTENTS_CURRENT_DOWN 0x800000 + +#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity + +#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game +#define CONTENTS_DEBRIS 0x4000000 +#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs +#define CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans +#define CONTENTS_LADDER 0x20000000 +#define CONTENTS_HITBOX 0x40000000 // use accurate hitboxes on trace + +// NOTE: These are stored in a short in the engine now. Don't use more than 16 bits +#define SURF_LIGHT 0x0001 // value will hold the light strength +#define SURF_SKY2D 0x0002 // don't draw, indicates we should skylight + draw 2d sky but not draw the 3D skybox +#define SURF_SKY 0x0004 // don't draw, but add to skybox +#define SURF_WARP 0x0008 // turbulent water warp +#define SURF_TRANS 0x0010 +#define SURF_NOPORTAL 0x0020 // the surface can not have a portal placed on it +#define SURF_TRIGGER 0x0040 // FIXME: This is an xbox hack to work around elimination of trigger surfaces, which breaks occluders +#define SURF_NODRAW 0x0080 // don't bother referencing the texture + +#define SURF_HINT 0x0100 // make a primary bsp splitter + +#define SURF_SKIP 0x0200 // completely ignore, allowing non-closed brushes +#define SURF_NOLIGHT 0x0400 // Don't calculate light +#define SURF_BUMPLIGHT 0x0800 // calculate three lightmaps for the surface for bumpmapping +#define SURF_NOSHADOWS 0x1000 // Don't receive shadows +#define SURF_NODECALS 0x2000 // Don't receive decals +#define SURF_NOPAINT SURF_NODECALS // the surface can not have paint placed on it +#define SURF_NOCHOP 0x4000 // Don't subdivide patches on this surface +#define SURF_HITBOX 0x8000 // surface is part of a hitbox + + + +// ----------------------------------------------------- +// spatial content masks - used for spatial queries (traceline,etc.) +// ----------------------------------------------------- +#define MASK_ALL (0xFFFFFFFF) +// everything that is normally solid +#define MASK_SOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) +// everything that blocks player movement +#define MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) +// blocks npc movement +#define MASK_NPCSOLID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER|CONTENTS_GRATE) +// blocks fluid movement +#define MASK_NPCFLUID (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_WINDOW|CONTENTS_MONSTER) +// water physics in these contents +#define MASK_WATER (CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_SLIME) +// everything that blocks lighting +#define MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_OPAQUE) +// everything that blocks lighting, but with monsters added. +#define MASK_OPAQUE_AND_NPCS (MASK_OPAQUE|CONTENTS_MONSTER) +// everything that blocks line of sight for AI +#define MASK_BLOCKLOS (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS) +// everything that blocks line of sight for AI plus NPCs +#define MASK_BLOCKLOS_AND_NPCS (MASK_BLOCKLOS|CONTENTS_MONSTER) +// everything that blocks line of sight for players +#define MASK_VISIBLE (MASK_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE) +// everything that blocks line of sight for players, but with monsters added. +#define MASK_VISIBLE_AND_NPCS (MASK_OPAQUE_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE) +// bullets see these as solid +#define MASK_SHOT (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_HITBOX) +// bullets see these as solid, except monsters (world+brush only) +#define MASK_SHOT_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_DEBRIS) +// non-raycasted weapons see this as solid (includes grates) +#define MASK_SHOT_HULL (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_MONSTER|CONTENTS_WINDOW|CONTENTS_DEBRIS|CONTENTS_GRATE) +// hits solids (not grates) and passes through everything else +#define MASK_SHOT_PORTAL (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTER) +// everything normally solid, except monsters (world+brush only) +#define MASK_SOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_GRATE) +// everything normally solid for player movement, except monsters (world+brush only) +#define MASK_PLAYERSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_PLAYERCLIP|CONTENTS_GRATE) +// everything normally solid for npc movement, except monsters (world+brush only) +#define MASK_NPCSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE) +// just the world, used for route rebuilding +#define MASK_NPCWORLDSTATIC (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP|CONTENTS_GRATE) +// just the world, used for route rebuilding +#define MASK_NPCWORLDSTATIC_FLUID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTERCLIP) +// These are things that can split areaportals +#define MASK_SPLITAREAPORTAL (CONTENTS_WATER|CONTENTS_SLIME) + +// UNDONE: This is untested, any moving water +#define MASK_CURRENT (CONTENTS_CURRENT_0|CONTENTS_CURRENT_90|CONTENTS_CURRENT_180|CONTENTS_CURRENT_270|CONTENTS_CURRENT_UP|CONTENTS_CURRENT_DOWN) + +// everything that blocks corpse movement +// UNDONE: Not used yet / may be deleted +#define MASK_DEADSOLID (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_WINDOW|CONTENTS_GRATE) + +#endif // BSPFLAGS_H diff --git a/public/bsptreedata.cpp b/public/bsptreedata.cpp new file mode 100644 index 0000000..6ce54e3 --- /dev/null +++ b/public/bsptreedata.cpp @@ -0,0 +1,352 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +// +// The BSP tree leaf data system +// +//=============================================================================// + +#include "basetypes.h" +#include "bsptreedata.h" +#include "utllinkedlist.h" +#include "utlvector.h" +#include "tier0/dbg.h" +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// The BSP tree leaf data system +//----------------------------------------------------------------------------- +class CBSPTreeData : public IBSPTreeData, public ISpatialLeafEnumerator +{ +public: + // constructor, destructor + CBSPTreeData(); + virtual ~CBSPTreeData(); + + // Methods of IBSPTreeData + void Init( ISpatialQuery* pBSPTree ); + void Shutdown(); + + BSPTreeDataHandle_t Insert( int userId, Vector const& mins, Vector const& maxs ); + void Remove( BSPTreeDataHandle_t handle ); + void ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ); + + // Enumerate elements in a particular leaf + bool EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context ); + + // For convenience, enumerates the leaves along a ray, box, etc. + bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ); + bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ); + + // methods of IBSPLeafEnumerator + bool EnumerateLeaf( int leaf, int context ); + + // Is the element in any leaves at all? + bool IsElementInTree( BSPTreeDataHandle_t handle ) const; + +private: + // Creates a new handle + BSPTreeDataHandle_t NewHandle( int userId ); + + // Adds a handle to the list of handles + void AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle ); + + // insert, remove handles from leaves + void InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ); + void RemoveFromTree( BSPTreeDataHandle_t handle ); + + // Returns the number of elements in a leaf + int CountElementsInLeaf( int leaf ); + +private: + // All the information associated with a particular handle + struct HandleInfo_t + { + int m_UserId; // Client-defined id + unsigned short m_LeafList; // What leafs is it in? + }; + + // The leaf contains an index into a list of elements + struct Leaf_t + { + unsigned short m_FirstElement; + }; + + // The handle knows about the leaves it lies in + struct HandleInLeaf_t + { + int m_Leaf; // what leaf is the handle in? + unsigned short m_LeafElementIndex; // what's the m_LeafElements index of the entry? + }; + + // Stores data associated with each leaf. + CUtlVector< Leaf_t > m_Leaf; + + // Stores all unique handles + CUtlLinkedList< HandleInfo_t, unsigned short > m_Handles; + + // Maintains the list of all handles in a particular leaf + CUtlLinkedList< BSPTreeDataHandle_t, unsigned short, true > m_LeafElements; + + // Maintains the list of all leaves a particular handle spans + CUtlLinkedList< HandleInLeaf_t, unsigned short, true > m_HandleLeafList; + + // Interface to BSP tree + ISpatialQuery* m_pBSPTree; +}; + + +//----------------------------------------------------------------------------- +// Class factory +//----------------------------------------------------------------------------- + +IBSPTreeData* CreateBSPTreeData() +{ + return new CBSPTreeData; +} + +void DestroyBSPTreeData( IBSPTreeData* pTreeData ) +{ + if (pTreeData) + delete pTreeData; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +CBSPTreeData::CBSPTreeData() +{ +} + +CBSPTreeData::~CBSPTreeData() +{ +} + + +//----------------------------------------------------------------------------- +// Level init, shutdown +//----------------------------------------------------------------------------- + +void CBSPTreeData::Init( ISpatialQuery* pBSPTree ) +{ + Assert( pBSPTree ); + m_pBSPTree = pBSPTree; + + m_Handles.EnsureCapacity( 1024 ); + m_LeafElements.EnsureCapacity( 1024 ); + m_HandleLeafList.EnsureCapacity( 1024 ); + + // Add all the leaves we'll need + int leafCount = m_pBSPTree->LeafCount(); + m_Leaf.EnsureCapacity( leafCount ); + + Leaf_t newLeaf; + newLeaf.m_FirstElement = m_LeafElements.InvalidIndex(); + while ( --leafCount >= 0 ) + { + m_Leaf.AddToTail( newLeaf ); + } +} + +void CBSPTreeData::Shutdown() +{ + m_Handles.Purge(); + m_LeafElements.Purge(); + m_HandleLeafList.Purge(); + m_Leaf.Purge(); +} + + +//----------------------------------------------------------------------------- +// Creates a new handle +//----------------------------------------------------------------------------- + +BSPTreeDataHandle_t CBSPTreeData::NewHandle( int userId ) +{ + BSPTreeDataHandle_t handle = m_Handles.AddToTail(); + m_Handles[handle].m_UserId = userId; + m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex(); + + return handle; +} + +//----------------------------------------------------------------------------- +// Add/remove handle +//----------------------------------------------------------------------------- + +BSPTreeDataHandle_t CBSPTreeData::Insert( int userId, Vector const& mins, Vector const& maxs ) +{ + BSPTreeDataHandle_t handle = NewHandle( userId ); + InsertIntoTree( handle, mins, maxs ); + return handle; +} + +void CBSPTreeData::Remove( BSPTreeDataHandle_t handle ) +{ + if (!m_Handles.IsValidIndex(handle)) + return; + + RemoveFromTree( handle ); + m_Handles.Free( handle ); +} + + +//----------------------------------------------------------------------------- +// Adds a handle to a leaf +//----------------------------------------------------------------------------- +void CBSPTreeData::AddHandleToLeaf( int leaf, BSPTreeDataHandle_t handle ) +{ + // Got to a leaf baby! Add the handle to the leaf's list of elements + unsigned short leafElement = m_LeafElements.Alloc( true ); + if (m_Leaf[leaf].m_FirstElement != m_LeafElements.InvalidIndex() ) + m_LeafElements.LinkBefore( m_Leaf[leaf].m_FirstElement, leafElement ); + m_Leaf[leaf].m_FirstElement = leafElement; + m_LeafElements[leafElement] = handle; + + // Insert the leaf into the handles's list of leaves + unsigned short handleElement = m_HandleLeafList.Alloc( true ); + if (m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex() ) + m_HandleLeafList.LinkBefore( m_Handles[handle].m_LeafList, handleElement ); + m_Handles[handle].m_LeafList = handleElement; + m_HandleLeafList[handleElement].m_Leaf = leaf; + m_HandleLeafList[handleElement].m_LeafElementIndex = leafElement; +} + + +//----------------------------------------------------------------------------- +// Inserts an element into the tree +//----------------------------------------------------------------------------- +bool CBSPTreeData::EnumerateLeaf( int leaf, int context ) +{ + BSPTreeDataHandle_t handle = (BSPTreeDataHandle_t)context; + AddHandleToLeaf( leaf, handle ); + return true; +} + +void CBSPTreeData::InsertIntoTree( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ) +{ + m_pBSPTree->EnumerateLeavesInBox( mins, maxs, this, handle ); +} + +//----------------------------------------------------------------------------- +// Removes an element from the tree +//----------------------------------------------------------------------------- + +void CBSPTreeData::RemoveFromTree( BSPTreeDataHandle_t handle ) +{ + // Iterate over the list of all leaves the handle is in + unsigned short i = m_Handles[handle].m_LeafList; + while (i != m_HandleLeafList.InvalidIndex()) + { + int leaf = m_HandleLeafList[i].m_Leaf; + unsigned short leafElement = m_HandleLeafList[i].m_LeafElementIndex; + + // Unhook the handle from the leaf handle list + if (leafElement == m_Leaf[leaf].m_FirstElement) + m_Leaf[leaf].m_FirstElement = m_LeafElements.Next(leafElement); + m_LeafElements.Free(leafElement); + + unsigned short prevNode = i; + i = m_HandleLeafList.Next(i); + m_HandleLeafList.Free(prevNode); + } + + m_Handles[handle].m_LeafList = m_HandleLeafList.InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Call this when the element moves +//----------------------------------------------------------------------------- +void CBSPTreeData::ElementMoved( BSPTreeDataHandle_t handle, Vector const& mins, Vector const& maxs ) +{ + if (handle != TREEDATA_INVALID_HANDLE) + { + RemoveFromTree( handle ); + InsertIntoTree( handle, mins, maxs ); + } +} + + +//----------------------------------------------------------------------------- +// Is the element in any leaves at all? +//----------------------------------------------------------------------------- +bool CBSPTreeData::IsElementInTree( BSPTreeDataHandle_t handle ) const +{ + return m_Handles[handle].m_LeafList != m_HandleLeafList.InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Enumerate elements in a particular leaf +//----------------------------------------------------------------------------- +int CBSPTreeData::CountElementsInLeaf( int leaf ) +{ + int i; + int nCount = 0; + for( i = m_Leaf[leaf].m_FirstElement; i != m_LeafElements.InvalidIndex(); i = m_LeafElements.Next(i) ) + { + ++nCount; + } + + return nCount; +} + +//----------------------------------------------------------------------------- +// Enumerate elements in a particular leaf +//----------------------------------------------------------------------------- +bool CBSPTreeData::EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context ) +{ +#ifdef DBGFLAG_ASSERT + // The enumeration method better damn well not change this list... + int nCount = CountElementsInLeaf(leaf); +#endif + + unsigned short idx = m_Leaf[leaf].m_FirstElement; + while (idx != m_LeafElements.InvalidIndex()) + { + BSPTreeDataHandle_t handle = m_LeafElements[idx]; + if (!pEnum->EnumerateElement( m_Handles[handle].m_UserId, context )) + { + Assert( CountElementsInLeaf(leaf) == nCount ); + return false; + } + idx = m_LeafElements.Next(idx); + } + + Assert( CountElementsInLeaf(leaf) == nCount ); + + return true; +} + + +//----------------------------------------------------------------------------- +// For convenience, enumerates the leaves along a ray, box, etc. +//----------------------------------------------------------------------------- +bool CBSPTreeData::EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ) +{ + return m_pBSPTree->EnumerateLeavesAtPoint( pt, pEnum, context ); +} + +bool CBSPTreeData::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) +{ + return m_pBSPTree->EnumerateLeavesInBox( mins, maxs, pEnum, context ); +} + +bool CBSPTreeData::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) +{ + return m_pBSPTree->EnumerateLeavesInSphere( center, radius, pEnum, context ); +} + +bool CBSPTreeData::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) +{ + return m_pBSPTree->EnumerateLeavesAlongRay( ray, pEnum, context ); +} + diff --git a/public/bsptreedata.h b/public/bsptreedata.h new file mode 100644 index 0000000..72f13c6 --- /dev/null +++ b/public/bsptreedata.h @@ -0,0 +1,141 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +// +// The BSP tree leaf data system +// +//=============================================================================// + +#include "tier0/platform.h" + +#if !defined( BSPTREEDATA ) +#define BSPTREEDATA +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + +class Vector; +struct Ray_t; + + +//----------------------------------------------------------------------------- +// Handle to an renderable in the client leaf system +//----------------------------------------------------------------------------- + +typedef unsigned short BSPTreeDataHandle_t; + +enum +{ + TREEDATA_INVALID_HANDLE = (BSPTreeDataHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Interface needed by tree data to do its job +// +// Note that anything that has convex spatial regions with leaves identified +// by indices can implement the ISpatialQuery. All you have to do is to implement +// a class that can answer the 5 questions in the Query interface about the +// spatial subdivision. For example, a K-D tree or a BSP tree could implement +// this interface +// +//----------------------------------------------------------------------------- + +abstract_class ISpatialLeafEnumerator +{ +public: + // call back with a leaf and a context + // The context is completely user defined; it's passed into the enumeration + // function of ISpatialQuery. + // This gets called by the enumeration methods with each leaf + // that passes the test; return true to continue enumerating, + // false to stop + + virtual bool EnumerateLeaf( int leaf, int context ) = 0; +}; + +abstract_class ISpatialQuery +{ +public: + // Returns the number of leaves + virtual int LeafCount() const = 0; + + // Enumerates the leaves along a ray, box, etc. + virtual bool EnumerateLeavesAtPoint( const Vector& pt, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesInBox( const Vector& mins, const Vector& maxs, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesInSphere( const Vector& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesInSphereWithFlagSet( const Vector& center, float radius, ISpatialLeafEnumerator* pEnum, int context, int nFlagsCheck ) = 0; + virtual int ListLeavesInBox( const Vector& mins, const Vector& maxs, unsigned short *pList, int listMax ) = 0; + + // Used to determine which leaves passed in (specified by leafcount, pLeafs, and nLeafStride ) + // are within radius flRadius of vecCenter and have the flag set. + // The result is placed in the pLeafsInSphere array, which specifies _indices_ into the original pLeafs array + // The number of leaves found within the sphere is the return value. + // The caller is expected to have allocated at least nLeafCount pLeafsInSphere to place the results into + virtual int ListLeavesInSphereWithFlagSet( int *pLeafsInSphere, const Vector& vecCenter, float flRadius, int nLeafCount, const uint16 *pLeafs, int nLeafStride = sizeof(uint16), int nFlagsCheck = 0xFFFFFFFF ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Data associated with leaves. +// +// This is a parasitic class that attaches data to the leaves specified by the +// ISpatialQuery sent in to the initialization function. It can't exist without +// a spatial partition of some sort to hold onto. +//----------------------------------------------------------------------------- + +abstract_class IBSPTreeDataEnumerator +{ +public: + // call back with a userId and a context + virtual bool FASTCALL EnumerateElement( int userId, int context ) = 0; +}; + +abstract_class IBSPTreeData +{ +public: + // Initializes, shuts down + virtual void Init( ISpatialQuery* pBSPTree ) = 0; + virtual void Shutdown() = 0; + + // Adds and removes data from the leaf lists + virtual BSPTreeDataHandle_t Insert( int userId, const Vector& mins, const Vector& maxs ) = 0; + virtual void Remove( BSPTreeDataHandle_t handle ) = 0; + + // Call this when a element moves + virtual void ElementMoved( BSPTreeDataHandle_t handle, const Vector& mins, const Vector& maxs ) = 0; + + // Enumerate elements in a particular leaf + virtual bool EnumerateElementsInLeaf( int leaf, IBSPTreeDataEnumerator* pEnum, int context ) = 0; + + // Is the element in any leaves at all? + virtual bool IsElementInTree( BSPTreeDataHandle_t handle ) const = 0; + + // NOTE: These methods call through to the functions in the attached + // ISpatialQuery + // For convenience, enumerates the leaves along a ray, box, etc. + virtual bool EnumerateLeavesAtPoint( const Vector& pt, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesInBox( const Vector& mins, const Vector& maxs, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesInSphere( const Vector& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) = 0; + virtual bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) = 0; +}; + +//----------------------------------------------------------------------------- +// Class factory +//----------------------------------------------------------------------------- + +IBSPTreeData* CreateBSPTreeData(); +void DestroyBSPTreeData( IBSPTreeData* pTreeData ); + + +#endif // BSPTREEDATA + + diff --git a/public/builddisp.cpp b/public/builddisp.cpp new file mode 100644 index 0000000..848f4af --- /dev/null +++ b/public/builddisp.cpp @@ -0,0 +1,3153 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +//#include +#include +#include +#include "builddisp.h" +#include "collisionutils.h" +#include "tier1/strtools.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// +// Node Functions (friend functions) +// + +//----------------------------------------------------------------------------- +// should make this more programatic and extensible! +//----------------------------------------------------------------------------- +int GetNodeLevel( int index ) +{ + // root + if( index == 0 ) + return 1; + + // [1...4] + if( index < 5 ) + return 2; + + // [5....20] + if( index < 21 ) + return 3; + + // [21....84] + if( index < 85 ) + return 4; + + // error!!! + return -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeCount( int power ) +{ + return ( ( 1 << ( power << 1 ) ) / 3 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeParent( int index ) +{ + // ( index - 1 ) / 4 + return ( ( index - 1 ) >> 2 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeChild( int power, int index, int direction ) +{ + // ( index * 4 ) + direction + return ( ( index << 2 ) + ( direction - 3 ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeMinNodeAtLevel( int level ) +{ + switch( level ) + { + case 1: return 0; + case 2: return 1; + case 3: return 5; + case 4: return 21; + default: return -99999; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void GetComponentsFromNodeIndex( int index, int *x, int *y ) +{ + *x = 0; + *y = 0; + + for( int shift = 0; index != 0; shift++ ) + { + *x |= ( index & 1 ) << shift; + index >>= 1; + + *y |= ( index & 1 ) << shift; + index >>= 1; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeIndexFromComponents( int x, int y ) +{ + int index = 0; + + // Interleave bits from the x and y values to create the index: + + int shift; + for( shift = 0; x != 0; shift += 2, x >>= 1 ) + { + index |= ( x & 1 ) << shift; + } + + for( shift = 1; y != 0; shift += 2, y >>= 1 ) + { + index |= ( y & 1 ) << shift; + } + + return index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, + Vector const &pt, float &c0, float &c1, float &c2 ) +{ + Vector vSeg0, vSeg1, vCross; + vSeg0 = v1 - v0; + vSeg1 = v2 - v0; + + // get the area of the triangle + vCross = vSeg0.Cross( vSeg1 ); + float totalArea = vCross.Length() * 0.5f; + float ooTotalArea = totalArea ? 1.0f / totalArea : 0.0f; + + // get the area for cooeficient 0 (pt, v1, v2) + vSeg0 = v1 - pt; + vSeg1 = v2 - pt; + vCross = vSeg0.Cross( vSeg1 ); + float subArea = vCross.Length() * 0.5f; + c0 = subArea * ooTotalArea; + + // get the area for cooeficient 1 (v0, pt, v2) + vSeg0 = v2 - pt; + vSeg1 = v0 - pt; + vCross = vSeg0.Cross( vSeg1 ); + subArea = vCross.Length() * 0.5f; + c1 = subArea * ooTotalArea; + + // get the area for cooeficient 2 (v0, v1, pt) + vSeg0 = v0 - pt; + vSeg1 = v1 - pt; + vCross = vSeg0.Cross( vSeg1 ); + subArea = vCross.Length() * 0.5f; + c2 = subArea * ooTotalArea; + + float cTotal = c0 + c1 + c2; + if ( FloatMakePositive( 1.0f - cTotal ) < 1e-3 ) + return true; + + return false; +} + +// For some reason, the global optimizer screws up the recursion here. disable the global optimizations to fix this. +// IN VC++ 6.0 +#pragma optimize( "g", off ) + +CCoreDispSurface::CCoreDispSurface() +{ + Init(); +} + + +//============================================================================= +// +// CDispSurface Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::Init( void ) +{ + m_Index = -1; + + m_PointCount = 0; + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorClear( m_Points[i] ); + VectorClear( m_Normals[i] ); + Vector2DClear( m_TexCoords[i] ); + + for( int j = 0; j < NUM_BUMP_VECTS+1; j++ ) + { + Vector2DClear( m_LuxelCoords[i][j] ); + } + + m_Alphas[i] = 1.0f; + + m_MultiBlends[ i ].Init( 0.0f, 0.0f, 0.0f, 0.0f ); + m_AlphaBlends[ i ].Init( 0.0f, 0.0f, 0.0f, 0.0f ); + for( int j = 0; j < MAX_MULTIBLEND_CHANNELS; j++ ) + { + m_vBlendColors[ i ][ j ].Init( 1.0f, 1.0f, 1.0f ); + } + } + + m_PointStartIndex = -1; + VectorClear( m_PointStart ); + VectorClear( sAxis ); + VectorClear( tAxis ); + + for( i = 0; i < 4; i++ ) + { + m_EdgeNeighbors[i].SetInvalid(); + m_CornerNeighbors[i].SetInvalid(); + } + + m_Flags = 0; + m_Contents = 0; +} + + +void CCoreDispSurface::SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) +{ + for ( int i=0; i < 4; i++ ) + { + m_EdgeNeighbors[i] = edgeNeighbors[i]; + m_CornerNeighbors[i] = cornerNeighbors[i]; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ) +{ + if( m_PointStartIndex != -1 ) + return; + + int numIndices = 0; + int indices[4]; + int offsetIndex; + + // + // project all points on to the v-axis first and find the minimum + // + float minValue = DotProduct( tAxis, m_Points[0] ); + indices[numIndices] = 0; + numIndices++; + + int i; + for( i = 1; i < m_PointCount; i++ ) + { + float value = DotProduct( tAxis, m_Points[i] ); + float delta = ( value - minValue ); + delta = FloatMakePositive( delta ); + if( delta < 0.1 ) + { + indices[numIndices] = i; + numIndices++; + } + else if( value < minValue ) + { + minValue = value; + indices[0] = i; + numIndices = 1; + } + } + + // + // break ties with the u-axis projection + // + minValue = DotProduct( sAxis, m_Points[indices[0]] ); + offsetIndex = indices[0]; + + for( i = 1; i < numIndices; i++ ) + { + float value = DotProduct( sAxis, m_Points[indices[i]] ); + if( ( value < minValue ) ) + { + minValue = value; + offsetIndex = indices[i]; + } + } + + m_PointStartIndex = offsetIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispSurface::GenerateSurfPointStartIndex( void ) +{ + // + // get the minimum surface component values + // + Vector bMin; + VectorFill( bMin, 99999.0f ); + + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + for( int j = 0; j < 3; j++ ) + { + if( m_Points[i][j] < bMin[j] ) + { + bMin[j] = m_Points[i][j]; + } + } + } + + // + // find the point closest to the minimum, that is the start point + // + int minIndex = -1; + float minDistance = 999999999.0f; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + Vector segment; + segment = m_Points[i] - bMin; + float distanceSq = segment.LengthSqr(); + if( distanceSq < minDistance ) + { + minDistance = distanceSq; + minIndex = i; + } + } + + m_PointStartIndex = minIndex; + + return minIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispSurface::FindSurfPointStartIndex( void ) +{ + if( m_PointStartIndex != -1 ) + return m_PointStartIndex; + + int minIndex = -1; + float minDistance = 999999999.0f; + + for( int i = 0; i < QUAD_POINT_COUNT; i++ ) + { + Vector segment; + VectorSubtract( m_PointStart, m_Points[i], segment ); + float distanceSq = segment.LengthSqr(); + if( distanceSq < minDistance ) + { + minDistance = distanceSq; + minIndex = i; + } + } + + m_PointStartIndex = minIndex; + + return minIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispSurface::AdjustSurfPointData( void ) +{ + Vector tmpPoints[4]; + Vector tmpNormals[4]; + Vector2D tmpTexCoords[4]; + float tmpAlphas[4]; + Vector4D tmpMultiBlend[ 4 ]; + Vector4D tmpAlphaBlend[ 4 ]; + Vector tmpBlendColor[ 4 ][ MAX_MULTIBLEND_CHANNELS ]; + + int i; + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorCopy( m_Points[i], tmpPoints[i] ); + VectorCopy( m_Normals[i], tmpNormals[i] ); + Vector2DCopy( m_TexCoords[i], tmpTexCoords[i] ); + + tmpAlphas[i] = m_Alphas[i]; + tmpMultiBlend[ i ] = m_MultiBlends[ i ]; + tmpAlphaBlend[ i ] = m_AlphaBlends[ i ]; + for( int j = 0; j < MAX_MULTIBLEND_CHANNELS; j++ ) + { + tmpBlendColor[ i ][ j ] = m_vBlendColors[ i ][ j ]; + } + } + + for( i = 0; i < QUAD_POINT_COUNT; i++ ) + { + VectorCopy( tmpPoints[(i+m_PointStartIndex)%4], m_Points[i] ); + VectorCopy( tmpNormals[(i+m_PointStartIndex)%4], m_Normals[i] ); + Vector2DCopy( tmpTexCoords[(i+m_PointStartIndex)%4], m_TexCoords[i] ); + + m_Alphas[i] = tmpAlphas[i]; // is this correct? + m_MultiBlends[ i ] = tmpMultiBlend[ (i+m_PointStartIndex)%4 ]; + m_AlphaBlends[ i ] = tmpAlphaBlend[ (i+m_PointStartIndex)%4 ]; + for( int j = 0; j < MAX_MULTIBLEND_CHANNELS; j++ ) + { + m_vBlendColors[ i ][ j ] = tmpBlendColor[ ( i + m_PointStartIndex ) % 4 ][ j ]; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCoreDispSurface::LongestInU( const Vector &vecU, const Vector &vecV ) +{ + Vector vecNormU = vecU; + Vector vecNormV = vecV; + VectorNormalize( vecNormU ); + VectorNormalize( vecNormV ); + + float flDistU[4]; + float flDistV[4]; + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + flDistU[iPoint] = vecNormU.Dot( m_Points[iPoint] ); + flDistV[iPoint] = vecNormV.Dot( m_Points[iPoint] ); + } + + float flULength = 0.0f; + float flVLength = 0.0f; + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + float flTestDist = fabs( flDistU[(iPoint+1)%4] - flDistU[iPoint] ); + if ( flTestDist > flULength ) + { + flULength = flTestDist; + } + + flTestDist = fabs( flDistV[(iPoint+1)%4] - flDistV[iPoint] ); + if ( flTestDist > flVLength ) + { + flVLength = flTestDist; + } + } + + if ( flULength < flVLength ) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +bool CCoreDispSurface::CalcLuxelCoords( int nLuxels, bool bAdjust, const Vector &vecU, const Vector &vecV ) +{ + // Valid value? + if ( nLuxels <= 0.0f ) + return false; + + // Get the start point offset. + int iOffset = 0; + if ( bAdjust ) + { + iOffset = GetPointStartIndex(); + } + + // Does projecting along U or V create the longest edge? + bool bLongU = LongestInU( vecU, vecV ); + + float flLengthTemp = 0.0f; + float flULength = ( m_Points[(3+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length(); + flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(1+iOffset)%4] ).Length(); + if ( flLengthTemp > flULength ) + { + flULength = flLengthTemp; + } + + // Find the largest edge in V. + float flVLength = ( m_Points[(1+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length(); + flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(3+iOffset)%4] ).Length(); + if ( flLengthTemp > flVLength ) + { + flVLength = flLengthTemp; + } + + float flOOLuxelScale = 1.0f / static_cast( nLuxels ); + float flUValue = static_cast( static_cast( flULength * flOOLuxelScale ) + 1 ); + if ( flUValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) + { + flUValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; + } + + float flVValue = static_cast( static_cast( flVLength * flOOLuxelScale ) + 1 ); + if ( flVValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER ) + { + flVValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; + } + + // Swap if necessary. + bool bSwapped = false; + if ( bLongU ) + { + if ( flVValue > flUValue ) + { + bSwapped = true; + } + } + else + { + if ( flUValue > flVValue ) + { + bSwapped = true; + } + } + + m_nLuxelU = static_cast( flUValue ); + m_nLuxelV = static_cast( flVValue ); + + // Generate luxel coordinates. + for( int iBump = 0; iBump < NUM_BUMP_VECTS+1; ++iBump ) + { + m_LuxelCoords[iBump][(0+iOffset)%4].Init( 0.5f, 0.5f ); + m_LuxelCoords[iBump][(1+iOffset)%4].Init( 0.5f, flVValue + 0.5 ); + m_LuxelCoords[iBump][(2+iOffset)%4].Init( flUValue + 0.5, flVValue + 0.5 ); + m_LuxelCoords[iBump][(3+iOffset)%4].Init( flUValue + 0.5, 0.5f ); + } + + return bSwapped; +} + +//============================================================================= +// +// CDispNode Functions +// + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispNode::Init( void ) +{ + VectorClear( m_BBox[0] ); + VectorClear( m_BBox[1] ); + + m_ErrorTerm = 0.0f; + + m_VertIndex = -1; + + int j; + for( j = 0; j < MAX_NEIGHBOR_NODE_COUNT; j++ ) + { + m_NeighborVertIndices[j] = -1; + } + + for( j = 0; j < MAX_SURF_AT_NODE_COUNT; j++ ) + { + VectorClear( m_SurfBBoxes[j][0] ); + VectorClear( m_SurfBBoxes[j][1] ); + VectorClear( m_SurfPlanes[j].normal ); + m_SurfPlanes[j].dist = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, Vector& v1, Vector& v2, Vector& v3 ) +{ + // get the node + CCoreDispNode *pNode = pDisp->GetNode( nodeIndex ); + + switch( triIndex ) + { + case 0: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 4 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v3 ); + return; + } + case 1: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); + return; + } + case 2: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v3 ); + return; + } + case 3: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v1 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); + return; + } + case 4: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); + pDisp->GetVert( pNode->GetCenterVertIndex(), v3 ); + return; + } + case 5: + { + pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v3 ); + return; + } + case 6: + { + pDisp->GetVert( pNode->GetCenterVertIndex(), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 ); + return; + } + case 7: + { + pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v1 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 ); + pDisp->GetVert( pNode->GetNeighborVertIndex( 7 ), v3 ); + return; + } + default: { return; } + } +} + + +//============================================================================= +// +// CCoreDispInfo Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CCoreDispInfo::CCoreDispInfo() +{ + m_pVerts = NULL; + m_RenderIndices = NULL; + m_Nodes = NULL; + m_pTris = NULL; + + // initialize the base surface data + m_Surf.Init(); + + // + // initialize the disp info + // + m_Power = 0; + m_Elevation = 0.0f; + m_RenderIndexCount = 0; + m_RenderCounter = 0; + m_bTouched = false; + + m_pNext = NULL; + + m_ppListBase = NULL; + m_ListSize = 0; + m_nListIndex = -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CCoreDispInfo::~CCoreDispInfo() +{ + if (m_pVerts) + delete [] m_pVerts; + if (m_RenderIndices) + delete [] m_RenderIndices; + if (m_Nodes) + delete [] m_Nodes; + if (m_pTris) + delete [] m_pTris; +} + + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitSurf( int parentIndex, Vector points[4], Vector normals[4], + Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, + bool bGenerateSurfPointStart, Vector& startPoint, + bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ) +{ + // save the "parent" index + m_Surf.m_Index = parentIndex; + + // + // save the surface points and point normals, texture coordinates, and + // lightmap coordinates + // + m_Surf.m_PointCount = CSurface::QUAD_POINT_COUNT; + for( int i = 0; i < CSurface::QUAD_POINT_COUNT; i++ ) + { + VectorCopy( points[i], m_Surf.m_Points[i] ); + + if( normals ) + { + VectorCopy( normals[i], m_Surf.m_pVerts[i].m_Normal ); + } + + if( texCoords ) + { + Vector2DCopy( texCoords[i], m_Surf.m_TexCoords[i] ); + } + + if( lightCoords ) + { + Assert( NUM_BUMP_VECTS == 3 ); + Vector2DCopy( lightCoords[0][i], m_Surf.m_LightCoords[i][0] ); + Vector2DCopy( lightCoords[1][i], m_Surf.m_LightCoords[i][1] ); + Vector2DCopy( lightCoords[2][i], m_Surf.m_LightCoords[i][2] ); + Vector2DCopy( lightCoords[3][i], m_Surf.m_LightCoords[i][3] ); + } + } + + // save the starting point + if( startPoint ) + { + VectorCopy( startPoint, m_Surf.m_PointStart ); + } + + // + // save the surface contents and flags + // + m_Contents = contents; + m_Flags = flags; + + // + // adjust surface points, texture coordinates, etc.... + // + if( bHasMappingAxes && ( m_Surf.m_PointStartIndex == -1 ) ) + { + GeneratePointStartIndexFromMappingAxes( uAxis, vAxis ); + } + else + { + // + // adjust the surf data + // + if( bGenerateSurfPointStart ) + { + GenerateSurfPointStartIndex(); + } + else + { + FindSurfPointStartIndex(); + } + } + + AdjustSurfPointData(); +} +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, float *alphas, Vector *dispVectorField, float *dispDistances, int nFlags, const CDispMultiBlend *pvMultiBlends ) +{ + Assert( power >= MIN_MAP_DISP_POWER && power <= MAX_MAP_DISP_POWER ); + + // + // general displacement data + // + m_Power = power; + + m_nFlags = nFlags; + + if ( ( minTess & 0x80000000 ) != 0 ) + { + // If the high bit is set, this represents FLAGS (SURF_NOPHYSICS_COLL, etc.) flags. + int nFlags = minTess; + nFlags &= ~0x80000000; + GetSurface()->SetFlags( nFlags ); + } + + // Allocate + initialize verts + int size = GetSize(); + m_pVerts = new CoreDispVert_t[size]; + + int nIndexCount = size * 2 * 3; + m_RenderIndices = new unsigned short[nIndexCount]; + + int nNodeCount = GetNodeCount(power); + m_Nodes = new CCoreDispNode[nNodeCount]; + + int i; + for( i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldVector.Init(); + m_pVerts[i].m_SubdivPos.Init(); + m_pVerts[i].m_SubdivNormal.Init(); + + m_pVerts[i].m_FieldDistance = 0.0f; + + m_pVerts[i].m_Vert.Init(); + m_pVerts[i].m_FlatVert.Init(); + m_pVerts[i].m_Normal.Init(); + m_pVerts[i].m_TangentS.Init(); + m_pVerts[i].m_TangentT.Init(); + m_pVerts[i].m_TexCoord.Init(); + + for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ ) + { + m_pVerts[i].m_LuxelCoords[j].Init(); + } + + m_pVerts[i].m_Alpha = 0.0f; + m_pVerts[i].m_MultiBlend.Init( 0.0f, 0.0f, 0.0f, 0.0f ); + m_pVerts[i].m_AlphaBlend.Init( 0.0f, 0.0f, 0.0f, 0.0f ); + for( int j = 0; j < MAX_MULTIBLEND_CHANNELS; j++ ) + { + m_pVerts[i].m_vBlendColors[ j ].Init( 1.0f, 1.0f, 1.0f ); + } + } + + for( i = 0; i < nIndexCount; i++ ) + { + m_RenderIndices[i] = 0; + } + + for( i = 0; i < nNodeCount; i++ ) + { + m_Nodes[i].Init(); + } + + // + // save the displacement vector field and distances within the field + // offset have been combined with fieldvectors at this point!!! + // + if (alphas && dispVectorField && dispDistances) + { + for( i = 0; i < size; i++ ) + { + VectorCopy( dispVectorField[i], m_pVerts[i].m_FieldVector ); + m_pVerts[i].m_FieldDistance = dispDistances[i]; + m_pVerts[i].m_Alpha = alphas[i]; + } + } + + if ( ( m_nFlags & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 && pvMultiBlends != NULL ) + { + for( i = 0; i < size; i++ ) + { + m_pVerts[ i ].m_MultiBlend = pvMultiBlends[ i ].m_vMultiBlend; + m_pVerts[ i ].m_AlphaBlend = pvMultiBlends[ i ].m_vAlphaBlend; + for( int j = 0; j < MAX_MULTIBLEND_CHANNELS; j++ ) + { + m_pVerts[ i ].m_vBlendColors[ j ] = pvMultiBlends[ i ].m_vMultiBlendColors[ j ]; + } + } + } + else + { // clear it just in case + m_nFlags &= ~DISP_INFO_FLAG_HAS_MULTIBLEND; + } + + // Init triangle information. + int nTriCount = GetTriCount(); + if ( nTriCount != 0 ) + { + m_pTris = new CoreDispTri_t[nTriCount]; + if ( m_pTris ) + { + InitTris(); + } + } +} + + +void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris, int nFlags, const CDispMultiBlend *pvMultiBlends ) +{ + Vector vectors[MAX_DISPVERTS]; + float dists[MAX_DISPVERTS]; + float alphas[MAX_DISPVERTS]; + + int nVerts = NUM_DISP_POWER_VERTS( power ); + for ( int i=0; i < nVerts; i++ ) + { + vectors[i] = pVerts[i].m_vVector; + dists[i] = pVerts[i].m_flDist; + alphas[i] = pVerts[i].m_flAlpha; + } + + InitDispInfo( power, minTess, smoothingAngle, alphas, vectors, dists, nFlags, pvMultiBlends ); + + int nTris = NUM_DISP_POWER_TRIS( power ); + for ( int iTri = 0; iTri < nTris; ++iTri ) + { + m_pTris[iTri].m_uiTags = pTris[iTri].m_uiTags; + } +} + + +void CCoreDispInfo::SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ) +{ + m_ppListBase = ppListBase; + m_ListSize = listSize; +} + +const CPowerInfo* CCoreDispInfo::GetPowerInfo() const +{ + return ::GetPowerInfo( GetPower() ); +} + +CDispNeighbor* CCoreDispInfo::GetEdgeNeighbor( int index ) +{ + return GetSurface()->GetEdgeNeighbor( index ); +} + +CDispCornerNeighbors* CCoreDispInfo::GetCornerNeighbors( int index ) +{ + return GetSurface()->GetCornerNeighbors( index ); +} + +CDispUtilsHelper* CCoreDispInfo::GetDispUtilsByIndex( int index ) +{ + Assert( m_ppListBase ); + return index == 0xFFFF ? 0 : m_ppListBase[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BuildTriTLtoBR( int ndx ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; + m_RenderIndexCount += 3; + + m_RenderIndices[m_RenderIndexCount] = ndx + 1; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; + m_RenderIndexCount += 3; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BuildTriBLtoTR( int ndx ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth; + m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1; + m_RenderIndexCount += 3; + + m_RenderIndices[m_RenderIndexCount] = ndx; + m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth + 1; + m_RenderIndices[m_RenderIndexCount+2] = ndx + 1; + m_RenderIndexCount += 3; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateCollisionSurface( void ) +{ + // get width and height of displacement maps + int nWidth = ( ( 1 << m_Power ) + 1 ); + int nHeight = ( ( 1 << m_Power ) + 1 ); + + // + // generate a fan tesselated (at quadtree node) rendering index list + // + m_RenderIndexCount = 0; + for ( int iV = 0; iV < ( nHeight - 1 ); iV++ ) + { + for ( int iU = 0; iU < ( nWidth - 1 ); iU++ ) + { + int ndx = ( iV * nWidth ) + iU; + + // test whether or not the index is odd + bool bOdd = ( ( ndx %2 ) == 1 ); + + // Top Left to Bottom Right + if( bOdd ) + { + BuildTriTLtoBR( ndx ); + } + // Bottom Left to Top Right + else + { + BuildTriBLtoTR( ndx ); + } + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateCollisionData( void ) +{ + GenerateCollisionSurface(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ) +{ + // + // calculate plane info for each face + // + for( int i = 0; i < 8; i++ ) + { + Vector v[3]; + VectorCopy( m_pVerts[indices[i][0]].m_Vert, v[0] ); + VectorCopy( m_pVerts[indices[i][1]].m_Vert, v[1] ); + VectorCopy( m_pVerts[indices[i][2]].m_Vert, v[2] ); + + Vector seg[2]; + VectorSubtract( v[1], v[0], seg[0] ); + VectorSubtract( v[2], v[0], seg[1] ); + + Vector normal; + CrossProduct( seg[1], seg[0], normal ); + VectorNormalize( normal ); + float dist = DotProduct( v[0], normal ); + + m_Nodes[nodeIndex].SetTriPlane( i, normal, dist ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ) +{ + Vector triMin, triMax; + + for( int i = 0; i < 4; i++ ) + { + triMin[0] = triMax[0] = m_pVerts[indices[(i*2)][0]].m_Vert[0]; + triMin[1] = triMax[1] = m_pVerts[indices[(i*2)][0]].m_Vert[1]; + triMin[2] = triMax[2] = m_pVerts[indices[(i*2)][0]].m_Vert[2]; + + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( triMin[0] > m_pVerts[indices[(i*2)][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; + if( triMin[0] > m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; + + if( triMin[1] > m_pVerts[indices[(i*2)][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; + if( triMin[1] > m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; + + if( triMin[2] > m_pVerts[indices[(i*2)][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; + if( triMin[2] > m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; + + // + // maximum + // + if( triMax[0] < m_pVerts[indices[(i*2)][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0]; + if( triMax[0] < m_pVerts[indices[(i*2+1)][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0]; + + if( triMax[1] < m_pVerts[indices[(i*2)][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1]; + if( triMax[1] < m_pVerts[indices[(i*2+1)][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1]; + + if( triMax[2] < m_pVerts[indices[(i*2)][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2]; + if( triMax[2] < m_pVerts[indices[(i*2+1)][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetRayBoundingBox( i, triMin, triMax ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ) +{ + Vector triMin, triMax; + + for( int i = 0; i < 8; i++ ) + { + m_Nodes[nodeIndex].GetTriBoundingBox( i, triMin, triMax ); + + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( triMin[0] > m_pVerts[indices[i][j]].m_Vert[0] ) + triMin[0] = m_pVerts[indices[i][j]].m_Vert[0]; + + if( triMin[1] > m_pVerts[indices[i][j]].m_Vert[1] ) + triMin[1] = m_pVerts[indices[i][j]].m_Vert[1]; + + if( triMin[2] > m_pVerts[indices[i][j]].m_Vert[2] ) + triMin[2] = m_pVerts[indices[i][j]].m_Vert[2]; + + // + // maximum + // + if( triMax[0] < m_pVerts[indices[i][j]].m_Vert[0] ) + triMax[0] = m_pVerts[indices[i][j]].m_Vert[0]; + + if( triMax[1] < m_pVerts[indices[i][j]].m_Vert[1] ) + triMax[1] = m_pVerts[indices[i][j]].m_Vert[1]; + + if( triMax[2] < m_pVerts[indices[i][j]].m_Vert[2] ) + triMax[2] = m_pVerts[indices[i][j]].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetTriBoundingBox( i, triMin, triMax ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfIndices( int nodeIndex, int indices[8][3] ) +{ + indices[0][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 4 ); + indices[0][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[0][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + + indices[1][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + indices[1][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[1][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + indices[2][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 ); + indices[2][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[2][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); + + indices[3][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 ); + indices[3][1] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[3][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + + indices[4][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 ); + indices[4][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); + indices[4][2] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + indices[5][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[5][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 ); + indices[5][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + + indices[6][0] = m_Nodes[nodeIndex].GetCenterVertIndex(); + indices[6][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + indices[6][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + + indices[7][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 ); + indices[7][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 ); + indices[7][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 7 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcTriSurfInfoAtNode( int nodeIndex ) +{ + int indices[8][3]; + + CalcTriSurfIndices( nodeIndex, indices ); + CalcTriSurfBoundingBoxes( nodeIndex, indices ); + CalcRayBoundingBoxes( nodeIndex, indices ); + CalcTriSurfPlanes( nodeIndex, indices ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ) +{ + // get the child node index + int childNodeIndex = GetNodeChild( m_Power, nodeIndex, 4 ); + + // get initial bounding box values + m_Nodes[childNodeIndex].GetBoundingBox( bMin, bMax ); + + Vector nodeMin, nodeMax; + for( int i = 1, j = 5; i < 4; i++, j++ ) + { + // + // get the child node bounding box + // + childNodeIndex = GetNodeChild( m_Power, nodeIndex, j ); + m_Nodes[childNodeIndex].GetBoundingBox( nodeMin, nodeMax ); + + // minimum + if( bMin[0] > nodeMin[0] ) + bMin[0] = nodeMin[0]; + + if( bMin[1] > nodeMin[1] ) + bMin[1] = nodeMin[1]; + + if( bMin[2] > nodeMin[2] ) + bMin[2] = nodeMin[2]; + + // maximum + if( bMax[0] < nodeMax[0] ) + bMax[0] = nodeMax[0]; + + if( bMax[1] < nodeMax[1] ) + bMax[1] = nodeMax[1]; + + if( bMax[2] < nodeMax[2] ) + bMax[2] = nodeMax[2]; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcBoundingBoxAtNode( int nodeIndex ) +{ + Vector bMin, bMax; + + // + // initialize the minimum and maximum values for the bounding box + // + int level = GetNodeLevel( nodeIndex ); + + int vertIndex = m_Nodes[nodeIndex].GetCenterVertIndex(); + if( level == m_Power ) + { + VectorCopy( m_pVerts[vertIndex].m_Vert, bMin ); + VectorCopy( m_pVerts[vertIndex].m_Vert, bMax ); + } + else + { + CalcMinMaxBoundingBoxAtNode( nodeIndex, bMin, bMax ); + + if( bMin[0] > m_pVerts[vertIndex].m_Vert[0] ) + bMin[0] = m_pVerts[vertIndex].m_Vert[0]; + + if( bMin[1] > m_pVerts[vertIndex].m_Vert[1] ) + bMin[1] = m_pVerts[vertIndex].m_Vert[1]; + + if( bMin[2] > m_pVerts[vertIndex].m_Vert[2] ) + bMin[2] = m_pVerts[vertIndex].m_Vert[2]; + + + if( bMax[0] < m_pVerts[vertIndex].m_Vert[0] ) + bMax[0] = m_pVerts[vertIndex].m_Vert[0]; + + if( bMax[1] < m_pVerts[vertIndex].m_Vert[1] ) + bMax[1] = m_pVerts[vertIndex].m_Vert[1]; + + if( bMax[2] < m_pVerts[vertIndex].m_Vert[2] ) + bMax[2] = m_pVerts[vertIndex].m_Vert[2]; + } + + for( int i = 0; i < 8; i++ ) + { + int neighborVertIndex = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); + + // + // minimum + // + if( bMin[0] > m_pVerts[neighborVertIndex].m_Vert[0] ) + bMin[0] = m_pVerts[neighborVertIndex].m_Vert[0]; + + if( bMin[1] > m_pVerts[neighborVertIndex].m_Vert[1] ) + bMin[1] = m_pVerts[neighborVertIndex].m_Vert[1]; + + if( bMin[2] > m_pVerts[neighborVertIndex].m_Vert[2] ) + bMin[2] = m_pVerts[neighborVertIndex].m_Vert[2]; + + // + // maximum + // + if( bMax[0] < m_pVerts[neighborVertIndex].m_Vert[0] ) + bMax[0] = m_pVerts[neighborVertIndex].m_Vert[0]; + + if( bMax[1] < m_pVerts[neighborVertIndex].m_Vert[1] ) + bMax[1] = m_pVerts[neighborVertIndex].m_Vert[1]; + + if( bMax[2] < m_pVerts[neighborVertIndex].m_Vert[2] ) + bMax[2] = m_pVerts[neighborVertIndex].m_Vert[2]; + } + + m_Nodes[nodeIndex].SetBoundingBox( bMin, bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CCoreDispInfo::GetMaxErrorFromChildren( int nodeIndex, int level ) +{ + // + // check for children nodes + // + if( level == m_Power ) + return 0.0f; + + // + // get the child's error term and save the greatest error -- SW, SE, NW, NE + // + float errorTerm = 0.0f; + for( int i = 4; i < 8; i++ ) + { + int childIndex = GetNodeChild( m_Power, nodeIndex, i ); + + float nodeErrorTerm = m_Nodes[childIndex].GetErrorTerm(); + if( errorTerm < nodeErrorTerm ) + { + errorTerm = nodeErrorTerm; + } + } + + return errorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcErrorTermAtNode( int nodeIndex, int level ) +{ + if( level == m_Power ) + return; + + // + // get the vertex indices + // + int neighborVertIndices[9]; + for( int i = 0; i < 8; i++ ) + { + neighborVertIndices[i] = m_Nodes[nodeIndex].GetNeighborVertIndex( i ); + } + neighborVertIndices[8] = m_Nodes[nodeIndex].GetCenterVertIndex(); + + + // + // calculate the error terms + // + Vector segment; + Vector v; + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[0]].m_Vert, v, segment ); + float errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[1]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[6]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[2]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[7]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[3]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[4]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v ); + VectorScale( v, 0.5f, v ); + VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment ); + if( errorTerm < ( float )VectorLength( segment ) ) + errorTerm = ( float )VectorLength( segment ); + + // + // add the max child's error term + // + errorTerm += GetMaxErrorFromChildren( nodeIndex, level ); + + // set the error term + m_Nodes[nodeIndex].SetErrorTerm( errorTerm ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNeighborVertIndicesAtNode( int nodeIndex, int level ) +{ + // calculate the shift in direction in the matrix + int shift = ( 1 << ( m_Power - level ) ); + + // calculate the width, height of the displacement surface (are uniform) + int extent = ( ( 1 << m_Power ) + 1 ); + + // + // get the neighbor vertex indices (defining the surface at the node level) + // + for( int direction = 0; direction < 8; direction++ ) + { + // + // get the parent vertex index in component form + // + int posX = m_Nodes[nodeIndex].GetCenterVertIndex() % extent; + int posY = m_Nodes[nodeIndex].GetCenterVertIndex() / extent; + + // + // calculate the neighboring vertex indices for surface rendering + // + bool bError = false; + switch( direction ) + { + case WEST: { posX -= shift; break; } + case NORTH: { posY += shift; break; } + case EAST: { posX += shift; break; } + case SOUTH: { posY -= shift; break; } + case SOUTHWEST: { posX -= shift; posY -= shift; break; } + case SOUTHEAST: { posX += shift; posY -= shift; break; } + case NORTHWEST: { posX -= shift; posY += shift; break; } + case NORTHEAST: { posX += shift; posY += shift; break; } + default: { bError = true; break; } + } + + if( bError ) + { + m_Nodes[nodeIndex].SetNeighborVertIndex( direction, -99999 ); + } + else + { + m_Nodes[nodeIndex].SetNeighborVertIndex( direction, ( ( posY * extent ) + posX ) ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNodeInfo( int nodeIndex, int terminationLevel ) +{ + // get the level of the current node + int level = GetNodeLevel( nodeIndex ); + + // + // get the node data at the termination level + // + if( level == terminationLevel ) + { + // get the neighbor vertex indices (used to create surface at node level) + CalcNeighborVertIndicesAtNode( nodeIndex, level ); + + // get the neighbor node indices + //CalcNeighborNodeIndicesAtNode( nodeIndex, level ); + + // calculate the error term at the node + CalcErrorTermAtNode( nodeIndex, level ); + + // calcluate the axial-aligned bounding box at the node + CalcBoundingBoxAtNode( nodeIndex ); + + // calculate the triangular surface info at the node + CalcTriSurfInfoAtNode( nodeIndex ); + + return; + } + + // + // continue recursion (down to nodes "children") + // + for( int i = 4; i < 8; i++ ) + { + int childIndex = GetNodeChild( m_Power, nodeIndex, i ); + CalcNodeInfo( childIndex, terminationLevel ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CCoreDispInfo::GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ) +{ + // calculate the "shift" + int shift = ( 1 << ( m_Power - ( level + 1 ) ) ); + + // calculate the width and height of displacement (is uniform) + int extent = ( ( 1 << m_Power ) + 1 ); + + // get the parent vertex index in component form + int posX = parentVertIndex % extent; + int posY = parentVertIndex / extent; + + // + // calculate the child index based on the parent index and child + // direction + // + switch( direction ) + { + case SOUTHWEST: { posX -= shift; posY -= shift; break; } + case SOUTHEAST: { posX += shift; posY -= shift; break; } + case NORTHWEST: { posX -= shift; posY += shift; break; } + case NORTHEAST: { posX += shift; posY += shift; break; } + default: return -99999; + } + + // return the child vertex index + return ( ( posY * extent ) + posX ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcVertIndicesAtNodes( int nodeIndex ) +{ + // + // check for recursion termination ( node level = power ) + // + int level = GetNodeLevel( nodeIndex ); + if( level == m_Power ) + return; + + // + // get the children indices - SW, SE, NW, NE + // + int childIndices[4]; + int i, j; + for( i = 0, j = 4; i < 4; i++, j++ ) + { + childIndices[i] = GetNodeChild( m_Power, nodeIndex, j ); + int centerIndex = GetNodeVertIndexFromParentIndex( level, m_Nodes[nodeIndex].GetCenterVertIndex(), j ); + m_Nodes[childIndices[i]].SetCenterVertIndex( centerIndex ); + } + + // + // calculate the children's node vertex indices + // + for( i = 0; i < 4; i++ ) + { + CalcVertIndicesAtNodes( childIndices[i] ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateLODTree( void ) +{ + // + // calculate the displacement surface's vertex index at each quad-tree node + // centroid + // + int size = GetSize(); + int initialIndex = ( ( size - 1 ) >> 1 ); + m_Nodes[0].SetCenterVertIndex( initialIndex ); + CalcVertIndicesAtNodes( 0 ); + + // + // calculate the error terms, bounding boxes, and neighboring vertex indices + // at each node + // + for( int i = m_Power; i > 0; i-- ) + { + CalcNodeInfo( 0, i ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcDispSurfCoords( bool bLightMap, int lightmapID ) +{ + // + // get base surface texture coords + // + Vector2D texCoords[4]; + Vector2D luxelCoords[4]; + CCoreDispSurface *pSurf = GetSurface(); + + int i; + for( i = 0; i < 4; i++ ) + { + pSurf->GetTexCoord( i, texCoords[i] ); + pSurf->GetLuxelCoord( lightmapID, i, luxelCoords[i] ); + } + + // + // get images width and intervals along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); + + // + // calculate the parallel edge intervals + // + Vector2D edgeInt[2]; + if( !bLightMap ) + { + Vector2DSubtract( texCoords[1], texCoords[0], edgeInt[0] ); + Vector2DSubtract( texCoords[2], texCoords[3], edgeInt[1] ); + } + else + { + Vector2DSubtract( luxelCoords[1], luxelCoords[0], edgeInt[0] ); + Vector2DSubtract( luxelCoords[2], luxelCoords[3], edgeInt[1] ); + } + Vector2DMultiply( edgeInt[0], ooInt, edgeInt[0] ); + Vector2DMultiply( edgeInt[1], ooInt, edgeInt[1] ); + + // + // calculate the displacement points + // + for( i = 0; i < postSpacing; i++ ) + { + // + // position along parallel edges (start and end for a perpendicular segment) + // + Vector2D endPts[2]; + Vector2DMultiply( edgeInt[0], ( float )i, endPts[0] ); + Vector2DMultiply( edgeInt[1], ( float )i, endPts[1] ); + if( !bLightMap ) + { + Vector2DAdd( endPts[0], texCoords[0], endPts[0] ); + Vector2DAdd( endPts[1], texCoords[3], endPts[1] ); + } + else + { + Vector2DAdd( endPts[0], luxelCoords[0], endPts[0] ); + Vector2DAdd( endPts[1], luxelCoords[3], endPts[1] ); + } + + // + // interval length for perpendicular edge + // + Vector2D seg, segInt; + Vector2DSubtract( endPts[1], endPts[0], seg ); + Vector2DMultiply( seg, ooInt, segInt ); + + // + // calculate the material (texture or light) coordinate at each point + // + for( int j = 0; j < postSpacing; j++ ) + { + Vector2DMultiply( segInt, ( float )j, seg ); + + if( !bLightMap ) + { + Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_TexCoord ); + } + else + { + Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_LuxelCoords[lightmapID] ); + } + } + } +} + +#if 0 +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcDispSurfAlphas( void ) +{ + // + // get images width and intervals along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) ); + + // + // calculate the parallel edge intervals + // + float edgeInt[2]; + edgeInt[0] = m_Surf.m_Alpha[1] - m_Surf.m_Alpha[0]; + edgeInt[1] = m_Surf.m_Alpha[2] - m_Surf.m_Alpha[3]; + edgeInt[0] *= ooInt; + edgeInt[1] *= ooInt; + + // + // calculate the displacement points + // + for( int i = 0; i < postSpacing; i++ ) + { + // + // position along parallel edges (start and end for a perpendicular segment) + // + float endValues[2]; + + endValues[0] = edgeInt[0] * ( float )i; + endValues[1] = edgeInt[1] * ( float )i; + endValues[0] += m_Surf.m_Alpha[0]; + endValues[1] += m_Surf.m_Alpha[3]; + + // + // interval length for perpendicular edge + // + float seg, segInt; + seg = endValues[1] - endValues[0]; + segInt = seg * ooInt; + + // + // calculate the alpha value at each point + // + for( int j = 0; j < postSpacing; j++ ) + { + seg = segInt * ( float )j; + m_Alphas[i*postSpacing+j] = endValues[0] + seg; + } + } +} +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurfTangentSpaces( void ) +{ + // + // get texture axes from base surface + // + CCoreDispSurface *pSurf = GetSurface(); + Vector sAxis, tAxis; + pSurf->GetSAxis( sAxis ); + pSurf->GetTAxis( tAxis ); + + // + // calculate the tangent spaces + // + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + // + // create the axes - normals, tangents, and binormals + // + VectorCopy( tAxis, m_pVerts[i].m_TangentT ); + VectorNormalize( m_pVerts[i].m_TangentT ); + CrossProduct( m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT, m_pVerts[i].m_TangentS ); + VectorNormalize( m_pVerts[i].m_TangentS ); + CrossProduct( m_pVerts[i].m_TangentS, m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT ); + VectorNormalize( m_pVerts[i].m_TangentT ); + + Vector tmpVect; + Vector planeNormal; + pSurf->GetNormal( planeNormal ); + CrossProduct( sAxis, tAxis, tmpVect ); + if( DotProduct( planeNormal, tmpVect ) > 0.0f ) + { + VectorScale( m_pVerts[i].m_TangentS, -1.0f, m_pVerts[i].m_TangentS ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], + Vector& normal ) +{ + // get the post spacing (size/interval of displacement surface) + int postSpacing = ( ( 1 << m_Power ) + 1 ); + + // initialize the normal accumulator - counter + Vector accumNormal; + int normalCount = 0; + + VectorClear( accumNormal ); + + Vector tmpVect[2]; + Vector tmpNormal; + + // + // check quadrant I (posX, posY) + // + if( bIsEdge[1] && bIsEdge[2] ) + { + // tri i + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant II (negX, posY) + // + if( bIsEdge[0] && bIsEdge[1] ) + { + // tri i + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant III (negX, negY) + // + if( bIsEdge[0] && bIsEdge[3] ) + { + // tri i + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + // + // check quadrant IV (posX, negY) + // + if( bIsEdge[2] && bIsEdge[3] ) + { + // tri i + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + + // tri 2 + VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] ); + VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] ); + CrossProduct( tmpVect[1], tmpVect[0], tmpNormal ); + VectorNormalize( tmpNormal ); + VectorAdd( accumNormal, tmpNormal, accumNormal ); + normalCount++; + } + + VectorScale( accumNormal, ( 1.0f / ( float )normalCount ), normal ); +} + + +//----------------------------------------------------------------------------- +// Purpose: This function determines if edges exist in each of the directions +// off of the given point (given in component form). We know ahead of +// time that there are only 4 possibilities. +// +// 1 "directions" +// 0 + 2 +// 3 +// +// Input: indexRow - row position +// indexCol - col position +// direction - the direction (edge) currently being evaluated +// postSpacing - the number of intervals in the row and col directions +// Output: the edge existed? (true/false) +//----------------------------------------------------------------------------- +bool CCoreDispInfo::DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ) +{ + switch( direction ) + { + case 0: + // left edge + if( ( indexRow - 1 ) < 0 ) + return false; + return true; + case 1: + // top edge + if( ( indexCol + 1 ) > ( postSpacing - 1 ) ) + return false; + return true; + case 2: + // right edge + if( ( indexRow + 1 ) > ( postSpacing - 1 ) ) + return false; + return true; + case 3: + // bottom edge + if( ( indexCol - 1 ) < 0 ) + return false; + return true; + default: + return false; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurfNormals( void ) +{ + // get the post spacing (size/interval of displacement surface) + int postSpacing = GetPostSpacing(); + + // + // generate the normals at each displacement surface vertex + // + for( int i = 0; i < postSpacing; i++ ) + { + for( int j = 0; j < postSpacing; j++ ) + { + bool bIsEdge[4]; + + // edges + for( int k = 0; k < 4; k++ ) + { + bIsEdge[k] = DoesEdgeExist( j, i, k, postSpacing ); + } + + Vector normal; + CalcNormalFromEdges( j, i, bIsEdge, normal ); + + // save generated normal + VectorCopy( normal, m_pVerts[i*postSpacing+j].m_Normal ); + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GenerateDispSurf( void ) +{ + int i; + CCoreDispSurface *pSurf = GetSurface(); + Vector points[4]; + for( i = 0; i < 4; i++ ) + { + pSurf->GetPoint( i, points[i] ); + } + + // + // get the spacing (interval = width/height, are equal because it is uniform) along the edge + // + int postSpacing = GetPostSpacing(); + float ooInt = 1.0f / ( float )( postSpacing - 1 ); + + // + // calculate the opposite edge intervals + // + Vector edgeInt[2]; + VectorSubtract( points[1], points[0], edgeInt[0] ); + VectorScale( edgeInt[0], ooInt, edgeInt[0] ); + VectorSubtract( points[2], points[3], edgeInt[1] ); + VectorScale( edgeInt[1], ooInt, edgeInt[1] ); + + Vector elevNormal; + elevNormal.Init(); + if( m_Elevation != 0.0f ) + { + pSurf->GetNormal( elevNormal ); + VectorScale( elevNormal, m_Elevation, elevNormal ); + } + + // + // calculate the displaced vertices + // + for( i = 0; i < postSpacing; i++ ) + { + // + // calculate segment interval between opposite edges + // + Vector endPts[2]; + VectorScale( edgeInt[0], ( float )i, endPts[0] ); + VectorAdd( endPts[0], points[0], endPts[0] ); + VectorScale( edgeInt[1], ( float )i, endPts[1] ); + VectorAdd( endPts[1], points[3], endPts[1] ); + + Vector seg, segInt; + VectorSubtract( endPts[1], endPts[0], seg ); + VectorScale( seg, ooInt, segInt ); + + // + // calculate the surface vertices + // + for( int j = 0; j < postSpacing; j++ ) + { + int ndx = i * postSpacing + j; + + CoreDispVert_t *pVert = &m_pVerts[ndx]; + + // calculate the flat surface position -- saved separately + pVert->m_FlatVert = endPts[0] + ( segInt * ( float )j ); + + // start with the base surface position + pVert->m_Vert = pVert->m_FlatVert; + + // add the elevation vector -- if it exists + if( m_Elevation != 0.0f ) + { + pVert->m_Vert += elevNormal; + } + + // add the subdivision surface position + pVert->m_Vert += pVert->m_SubdivPos; + + // add the displacement field direction(normalized) and distance + pVert->m_Vert += pVert->m_FieldVector * pVert->m_FieldDistance; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//bool CCoreDispInfo::Create( int creationFlags ) +bool CCoreDispInfo::Create( void ) +{ + // sanity check + CCoreDispSurface *pSurf = GetSurface(); + if( pSurf->GetPointCount() != 4 ) + return false; + + // generate the displacement surface + GenerateDispSurf(); + + GenerateDispSurfNormals(); + + GenerateDispSurfTangentSpaces(); + + CalcDispSurfCoords( false, 0 ); + + for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) + { + CalcDispSurfCoords( true, bumpID ); + } + + GenerateLODTree(); + + GenerateCollisionData(); + + CreateTris(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Create a displacement surface without generating the LOD for it. +//----------------------------------------------------------------------------- +bool CCoreDispInfo::CreateWithoutLOD( void ) +{ + // sanity check + CCoreDispSurface *pSurf = GetSurface(); + if( pSurf->GetPointCount() != 4 ) + return false; + + GenerateDispSurf(); + + GenerateDispSurfNormals(); + + GenerateDispSurfTangentSpaces(); + + CalcDispSurfCoords( false, 0 ); + + for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ ) + { + CalcDispSurfCoords( true, bumpID ); + } + GenerateCollisionData(); + + CreateTris(); + + return true; +} + + + +//----------------------------------------------------------------------------- +// Purpose: This function calculates the neighbor node index given the base +// node and direction of the neighbor node in the tree. +// Input: power - the size in one dimension of the displacement map (2^power + 1 ) +// index - the "base" node index +// direction - the direction of the neighbor { W = 1, N = 2, E = 3, S = 4 } +// Output: returns the index of the neighbor node +//----------------------------------------------------------------------------- +int GetNodeNeighborNode( int power, int index, int direction, int level ) +{ + // adjust the index to range [0...?] + int minNodeIndex = GetNodeMinNodeAtLevel( level ); + + // get node extent (uniform: height = width) + int nodeExtent = ( 1 << ( level - 1 ) ); + + // + // get node's component positions in quad-tree + // + int posX, posY; + GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); + + // + // find the neighbor in the "direction" + // + switch( direction ) + { + case CCoreDispInfo::WEST: + { + if( ( posX - 1 ) < 0 ) + { + return -( CCoreDispInfo::WEST + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( ( posX - 1 ), posY ) + minNodeIndex ); + } + } + case CCoreDispInfo::NORTH: + { + if( ( posY + 1 ) == nodeExtent ) + { + return -( CCoreDispInfo::NORTH + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( posX, ( posY + 1 ) ) + minNodeIndex ); + } + } + case CCoreDispInfo::EAST: + { + if( ( posX + 1 ) == nodeExtent ) + { + return -( CCoreDispInfo::EAST + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( ( posX + 1 ), posY ) + minNodeIndex ); + } + } + case CCoreDispInfo::SOUTH: + { + if( ( posY - 1 ) < 0 ) + { + return -( CCoreDispInfo::SOUTH + 1 ); + } + else + { + return ( GetNodeIndexFromComponents( posX, ( posY - 1 ) ) + minNodeIndex ); + } + } + default: + { + return -99999; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ) +{ + // adjust the index to range [0...?] + int minNodeIndex = GetNodeMinNodeAtLevel( level ); + + // get node extent (uniform: height = width) + int nodeExtent = ( 1 << ( level - 1 ) ); + + // + // get node's component positions in quad-tree + // + int posX, posY; + GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY ); + + switch( direction ) + { + case CCoreDispInfo::WEST: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ) - posY, ( nodeExtent - 1 ) ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::NORTH: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::EAST: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + default: return -99999; + } + } + case CCoreDispInfo::SOUTH: + { + switch( neighborOrient ) + { + case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex ); + case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posX, ( nodeExtent - 1 ) ) ) + minNodeIndex ); + case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex ); + case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex ); + default: return -99999; + } + } + default: + { + return -99999; + } + } +} + + + +// Turn the optimizer back on +#pragma optimize( "", on ) + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetPositionOnSurface( float u, float v, Vector &vPos, + Vector *pNormal, float *pAlpha ) +{ + Vector2D dispUV( u, v ); + DispUVToSurf( dispUV, vPos, pNormal, pAlpha ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ) +{ + // Get the base surface points. + CCoreDispSurface *pSurf = GetSurface(); + Vector vecPoints[4]; + for( int iPoint = 0; iPoint < 4; ++iPoint ) + { + pSurf->GetPoint( iPoint, vecPoints[iPoint] ); + } + + PointInQuadToBarycentric( vecPoints[0], vecPoints[3], vecPoints[2], vecPoints[1], planePt, dispUV ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nNextV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nNextU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[1]; + Vector edgeV = vecVerts[2] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nSnapU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[0]; + Vector edgeV = vecVerts[1] - vecVerts[0]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, + float flU, float flV, const Vector &vecIntersectPoint ) +{ + const float TRIEDGE_EPSILON = 0.00001f; + + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + if ( nNextU == nWidth) { --nNextU; } + if ( nNextV == nHeight ) { --nNextV; } + + float flFracU = flU - static_cast( nSnapU ); + float flFracV = flV - static_cast( nSnapV ); + + if ( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) ) + { + DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } + else + { + DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nSnapU; + nIndices[2] = nNextV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeU, edgeV ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[2] - vecVerts[1]; + Vector edgeV = vecVerts[0] - vecVerts[1]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, + int nSnapU, int nNextU, int nSnapV, int nNextV, + Vector &vecPoint, Vector *pNormal, float *pAlpha, + bool bBackup ) +{ + int nWidth = GetWidth(); + + int nIndices[3]; + nIndices[0] = nSnapV * nWidth + nSnapU; + nIndices[1] = nNextV * nWidth + nNextU; + nIndices[2] = nSnapV * nWidth + nNextU; + + Vector vecFlatVerts[3], vecVerts[3]; + float flAlphas[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert; + vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert; + flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha; + } + + if ( nSnapU == nNextU ) + { + if ( nSnapV == nNextV ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else if ( nSnapV == nNextV ) + { + if ( nSnapU == nNextU ) + { + vecPoint = vecVerts[0]; + *pAlpha = flAlphas[0]; + } + else + { + float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length(); + vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) ); + + if ( pAlpha ) + { + *pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) ); + } + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + float flCfs[3]; + if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) ) + { + vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] ); + + if( pAlpha ) + { + *pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] ); + } + + if( pNormal ) + { + Vector edgeU = vecVerts[0] - vecVerts[2]; + Vector edgeV = vecVerts[1] - vecVerts[2]; + *pNormal = CrossProduct( edgeV, edgeU ); + VectorNormalize( *pNormal ); + } + } + else + { + if ( !bBackup ) + { + DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, + float flU, float flV, const Vector &vecIntersectPoint ) +{ + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + if ( nNextU == nWidth) { --nNextU; } + if ( nNextV == nHeight ) { --nNextV; } + + float flFracU = flU - static_cast( nSnapU ); + float flFracV = flV - static_cast( nSnapV ); + + if( flFracU < flFracV ) + { + DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } + else + { + DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, + Vector *pNormal, float *pAlpha ) +{ + // Check to see that the point is on the surface. + if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f ) + return; + + // Get the base surface points. + Vector vecIntersectPoint; + CCoreDispSurface *pSurf = GetSurface(); + PointInQuadFromBarycentric( pSurf->GetPoint( 0 ), pSurf->GetPoint( 3 ), pSurf->GetPoint( 2 ), pSurf->GetPoint( 1 ), dispUV, vecIntersectPoint ); + + // Get the displacement power. + int nWidth = GetWidth(); + int nHeight = GetHeight(); + + // Scale the U, V coordinates to the displacement grid size. + float flU = dispUV.x * ( static_cast( nWidth ) - 1.000001f ); + float flV = dispUV.y * ( static_cast( nHeight ) - 1.000001f ); + + // Find the base U, V. + int nSnapU = static_cast( flU ); + int nSnapV = static_cast( flV ); + + // Use this to get the triangle orientation. + bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 ); + + // Top Left to Bottom Right + if( bOdd ) + { + DispUVToSurf_TriTLToBR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); + } + // Bottom Left to Top Right + else + { + DispUVToSurf_TriBLToTR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Create bounding boxes around pairs of triangles (in a grid-like) +// fashion; used for culling +//----------------------------------------------------------------------------- +void CCoreDispInfo::CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ) +{ + // + // Initialize the bounding boxes. + // + int iBox; + for( iBox = 0; iBox < count; ++iBox ) + { + pBBox[iBox].vMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + pBBox[iBox].vMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); + } + + // Get the width and height of the displacement surface. + int nHeight = GetHeight(); + int nWidth = GetWidth(); + + // Find bounding box of every two consecutive triangles + iBox = 0; + int nIndex = 0; + for( int iHgt = 0; iHgt < ( nHeight - 1 ); ++iHgt ) + { + for( int iWid = 0; iWid < ( nWidth - 1 ); ++iWid ) + { + for( int iPoint = 0; iPoint < 4; ++iPoint ) + { + switch( iPoint ) + { + case 0: { nIndex = ( nHeight * iHgt ) + iWid; break; } + case 1: { nIndex = ( nHeight * ( iHgt + 1 ) ) + iWid; break; } + case 2: { nIndex = ( nHeight * ( iHgt + 1 ) ) + ( iWid + 1 ); break; } + case 3: { nIndex = ( nHeight * iHgt ) + ( iWid + 1 ); break; } + default: { break; } + } + + Vector vecPoint; + GetVert( nIndex, vecPoint ); + if( vecPoint[0] < pBBox[iBox].vMin[0] ) { pBBox[iBox].vMin[0] = vecPoint[0]; } + if( vecPoint[1] < pBBox[iBox].vMin[1] ) { pBBox[iBox].vMin[1] = vecPoint[1]; } + if( vecPoint[2] < pBBox[iBox].vMin[2] ) { pBBox[iBox].vMin[2] = vecPoint[2]; } + + if( vecPoint[0] > pBBox[iBox].vMax[0] ) { pBBox[iBox].vMax[0] = vecPoint[0]; } + if( vecPoint[1] > pBBox[iBox].vMax[1] ) { pBBox[iBox].vMax[1] = vecPoint[1]; } + if( vecPoint[2] > pBBox[iBox].vMax[2] ) { pBBox[iBox].vMax[2] = vecPoint[2]; } + } + + iBox++; + } + } + + // Verify. + Assert( iBox == count ); + + // Bloat. + for ( iBox = 0; iBox < count; ++iBox ) + { + for( int iAxis = 0; iAxis < 3; ++iAxis ) + { + pBBox[iBox].vMin[iAxis] -= 1.0f; + pBBox[iBox].vMax[iAxis] += 1.0f; + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool PointInDispBBox( CoreDispBBox_t *pBox, const Vector &vecPoint ) +{ + // Check to see if point lies in box + if( ( vecPoint.x < pBox->vMin.x ) || ( vecPoint.x > pBox->vMax.x ) ) + return false; + + if( ( vecPoint.y < pBox->vMin.y ) || ( vecPoint.y > pBox->vMax.y ) ) + return false; + + if( ( vecPoint.z < pBox->vMin.z ) || ( vecPoint.z > pBox->vMax.z ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ) +{ + // Test to see whether or not the index is odd. + bool bOdd = ( ( nIndex % 2 ) == 1 ); + + int nWidth = GetWidth(); + + // Tris for TLtoBR + if ( bOdd ) + { + nTris[0][0] = nIndex; + nTris[0][1] = nIndex + nWidth; + nTris[0][2] = nIndex + 1; + + nTris[1][0] = nIndex + 1; + nTris[1][1] = nIndex + nWidth; + nTris[1][2] = nIndex + nWidth + 1; + } + // Tris for BLtoTR + else + { + nTris[0][0] = nIndex; + nTris[0][1] = nIndex + nWidth; + nTris[0][2] = nIndex + nWidth + 1; + + nTris[1][0] = nIndex; + nTris[1][1] = nIndex + nWidth + 1; + nTris[1][2] = nIndex + 1; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CCoreDispInfo::SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ) +{ + // Create bounding boxes + int nBoxCount = ( GetHeight() - 1 ) * ( GetWidth() - 1 ); + CoreDispBBox_t *pBBox = new CoreDispBBox_t[nBoxCount]; + CreateBoundingBoxes( pBBox, nBoxCount ); + + // Use the boxes as a first-pass culling mechanism. + for( int iBox = 0; iBox < nBoxCount; ++iBox ) + { + // Get the current displacement triangle-pair bounding-box. + CoreDispBBox_t *pBox = &pBBox[iBox]; + if( !pBox ) + continue; + + // Check the point against the current displacement bounding-box. + if ( !PointInDispBBox( pBox, surfPt ) ) + continue; + + // Point lies within the bounding box. + int nIndex = iBox + ( iBox / ( GetWidth() - 1 ) ); + + // Get the triangle coordinates for this box. + int aTris[2][3]; + GetTriangleIndicesForDispBBox( nIndex, aTris ); + + // Barycentrically test the triangles on the displacement surface. + Vector vecPoints[3]; + for ( int iTri = 0; iTri < 2; ++iTri ) + { + for ( int iVert = 0; iVert < 3; ++iVert ) + { + GetVert( aTris[iTri][iVert], vecPoints[iVert] ); + } + + float c[3]; + if ( CalcBarycentricCooefs( vecPoints[0], vecPoints[1], vecPoints[2], surfPt, c[0], c[1], c[2] ) ) + { + Vector vecFlatPoints[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + GetFlatVert( aTris[iTri][iVert], vecFlatPoints[iVert] ); + } + + planePt = ( vecFlatPoints[0] * c[0] ) + ( vecFlatPoints[1] * c[1] ) + ( vecFlatPoints[2] * c[2] ); + + // Delete temporary memory. + delete [] pBBox; + return true; + } + } + } + + // Delete temporary memory + delete [] pBBox; + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CCoreDispInfo::GetTriCount( void ) +{ + return ( ( GetHeight() - 1 ) * ( GetWidth() -1 ) * 2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + v1 = pTri->m_iIndex[0]; + v2 = pTri->m_iIndex[1]; + v3 = pTri->m_iIndex[2]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + pTri->m_iIndex[0] = v1; + pTri->m_iIndex[1] = v2; + pTri->m_iIndex[2] = v3; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) ) + { + Assert( iTri >= 0 ); + Assert( iTri < GetTriCount() ); + Assert( m_pTris ); + return; + } + + CoreDispTri_t *pTri = &m_pTris[iTri]; + v1 = m_pVerts[pTri->m_iIndex[0]].m_Vert; + v2 = m_pVerts[pTri->m_iIndex[1]].m_Vert; + v3 = m_pVerts[pTri->m_iIndex[2]].m_Vert; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::InitTris( void ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris ) + { + Assert( m_pTris ); + return; + } + + int nTriCount = GetTriCount(); + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + m_pTris[iTri].m_uiTags = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::CreateTris( void ) +{ + // Verify we have the correct data (only build when collision data is built). + if ( !m_pTris ) + { + Assert( m_pTris ); + return; + } + + // Extra sanity check if wanted! + Assert( GetTriCount() == ( m_RenderIndexCount / 3 ) ); + + int nTriCount = GetTriCount(); + for ( int iTri = 0, iRender = 0; iTri < nTriCount; ++iTri, iRender += 3 ) + { + m_pTris[iTri].m_iIndex[0] = m_RenderIndices[iRender]; + m_pTris[iTri].m_iIndex[1] = m_RenderIndices[iRender+1]; + m_pTris[iTri].m_iIndex[2] = m_RenderIndices[iRender+2]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCoreDispInfo::IsTriWalkable( int iTri ) +{ + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) + { + return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); + } + + return IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCoreDispInfo::IsTriBuildable( int iTri ) +{ + if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) + { + return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); + } + + return IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCoreDispInfo::Position_Update( int iVert, Vector vecPos ) +{ + Vector vSPos, vFlat; + GetFlatVert( iVert, vFlat ); + GetSubdivPosition( iVert, vSPos ); + + Vector vSeg; + vSeg = vecPos - vFlat; + vSeg -= vSPos; + + // Subtract out the elevation. + float elev = GetElevation(); + if( elev != 0.0 ) + { + Vector vNormal; + GetSurface()->GetNormal( vNormal ); + vNormal *= elev; + + vSeg -= vNormal; + } + + float flDistance = VectorNormalize( vSeg ); + + SetFieldVector( iVert, vSeg ); + SetFieldDistance( iVert, flDistance ); +} diff --git a/public/builddisp.h b/public/builddisp.h new file mode 100644 index 0000000..4d6d46d --- /dev/null +++ b/public/builddisp.h @@ -0,0 +1,1557 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUILDDISP_H +#define BUILDDISP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "commonmacros.h" +#include "tier0/dbg.h" +#include "bspfile.h" +#include "mathlib/mathlib.h" +#include "mathlib/bumpvects.h" +#include "mathlib/vector4d.h" +#include "disp_common.h" +#include "bitvec.h" + +#define DISP_ALPHA_PROP_DELTA 382.5f + +class CCoreDispInfo; + +struct CoreDispBBox_t +{ + Vector vMin, vMax; +}; + +//========================================================================= +// +// Surface Class - interfacing class (fill in with MapFace, dface_t, and +// msurface_t) +// +class CCoreDispSurface +{ +public: + + enum { QUAD_POINT_COUNT = 4 }; + enum { MAX_CORNER_NEIGHBOR_COUNT = 16 }; + + CCoreDispSurface(); + + //========================================================================= + // + // initialization + // + void Init( void ); + + //========================================================================= + // + // parent surface id - index to CMapFace, dface_t, or msurface_t + // + inline void SetHandle( int handle ); + inline int GetHandle( void ); + + //========================================================================= + // + // vertex data - pos, normal, texture, lightmap, alpha, etc... + // + inline void SetPointCount( int count ); + inline int GetPointCount( void ) const; + + inline void SetPoint( int index, Vector const &pt ); + inline void GetPoint( int index, Vector& pt ) const; + inline Vector const& GetPoint( int index ) const; + + inline void SetPointNormal( int index, Vector const &normal ); + inline void GetPointNormal( int index, Vector &normal ); + inline void SetTexCoord( int index, Vector2D const& texCoord ); + inline void GetTexCoord( int index, Vector2D& texCoord ) const; + + inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); + inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; + inline void SetLuxelCoords( int bumpIndex, Vector2D const coords[4] ); + inline void GetLuxelCoords( int bumpIndex, Vector2D coords[4] ) const; + + inline void SetLuxelU( int nU ) { m_nLuxelU = nU; } + inline int GetLuxelU( void ) { return m_nLuxelU; } + inline void SetLuxelV( int nV ) { m_nLuxelV = nV; } + inline int GetLuxelV( void ) { return m_nLuxelV; } + bool CalcLuxelCoords( int nLuxels, bool bAdjust, const Vector &vecU, const Vector &vecV ); + + inline void SetAlpha( int index, float alpha ); + inline float GetAlpha( int const index ) const; + + //========================================================================= + // + // utils + // + inline void GetNormal( Vector& normal ); + inline void SetFlags( int flag ); + inline int GetFlags( void ); + inline void SetContents( int contents ); + inline int GetContents( void ); + + //========================================================================= + // + // create utils (texture axis not use anymore but here to support older maps) + // + inline void SetSAxis( Vector const &axis ); + inline void GetSAxis( Vector &axis ); + inline void SetTAxis( Vector const &axis ); + inline void GetTAxis( Vector &axis ); + + inline void SetPointStartIndex( int index ); + inline int GetPointStartIndex( void ); + inline void SetPointStart( Vector const &pt ); + inline void GetPointStart( Vector &pt ); + + // Used by the tools to set the neighbor data from the BSP file. + void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ); + + void GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis ); + int GenerateSurfPointStartIndex( void ); + int FindSurfPointStartIndex( void ); + void AdjustSurfPointData( void ); + + // Indexed by CORNER_ defines. + CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } + const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { Assert( iCorner >= 0 && iCorner < ARRAYSIZE( m_CornerNeighbors ) ); return &m_CornerNeighbors[iCorner]; } + + // Indexed by CORNER_ defines. + int GetCornerNeighborCount( int iCorner ) const { return GetCornerNeighbors( iCorner )->m_nNeighbors; } + int GetCornerNeighbor( int iCorner, int iNeighbor ) const { Assert( iNeighbor >= 0 && iNeighbor < GetCornerNeighbors(iCorner)->m_nNeighbors ); return GetCornerNeighbors( iCorner )->m_Neighbors[iNeighbor]; } + + CDispNeighbor* GetEdgeNeighbor( int iEdge ) { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } + const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { Assert( iEdge >= 0 && iEdge < ARRAYSIZE( m_EdgeNeighbors ) ); return &m_EdgeNeighbors[iEdge]; } + + +protected: + + // Utility + bool LongestInU( const Vector &vecU, const Vector &vecV ); + + + int m_Index; // parent face (CMapFace, dface_t, msurface_t) index "handle" + + int m_PointCount; // number of points in the face (should be 4!) + Vector m_Points[QUAD_POINT_COUNT]; // points + Vector m_Normals[QUAD_POINT_COUNT]; // normals at points + Vector2D m_TexCoords[QUAD_POINT_COUNT]; // texture coordinates at points + Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1][QUAD_POINT_COUNT]; // lightmap coordinates at points + float m_Alphas[QUAD_POINT_COUNT]; // alpha at points + Vector4D m_MultiBlends[ QUAD_POINT_COUNT ]; + Vector4D m_AlphaBlends[ QUAD_POINT_COUNT ]; + Vector m_vBlendColors[ QUAD_POINT_COUNT ][ MAX_MULTIBLEND_CHANNELS ]; + + // Luxels sizes + int m_nLuxelU; + int m_nLuxelV; + + // Straight from the BSP file. + CDispNeighbor m_EdgeNeighbors[4]; + CDispCornerNeighbors m_CornerNeighbors[4]; + + int m_Flags; // surface flags - inherited from the "parent" face + int m_Contents; // contents flags - inherited from the "parent" face + + Vector sAxis; // used to generate start disp orientation (old method) + Vector tAxis; // used to generate start disp orientation (old method) + int m_PointStartIndex; // index to the starting point -- for saving starting point + Vector m_PointStart; // starting point used to determine the orientation of the displacement map on the surface +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetHandle( int handle ) +{ + m_Index = handle; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetHandle( void ) +{ + return m_Index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointCount( int count ) +{ + // quad only -- currently! + if( count != 4 ) + return; + m_PointCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetPointCount( void ) const +{ + return m_PointCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPoint( int index, Vector const &pt ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( pt, m_Points[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPoint( int index, Vector &pt ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( m_Points[index], pt ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline Vector const& CCoreDispSurface::GetPoint( int index ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + return m_Points[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( normal, m_Normals[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPointNormal( int index, Vector& normal ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + VectorCopy( m_Normals[index], normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetTexCoord( int index, Vector2D const& texCoord ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Vector2DCopy( texCoord, m_TexCoords[index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetTexCoord( int index, Vector2D& texCoord ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Vector2DCopy( m_TexCoords[index], texCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( luxelCoord, m_LuxelCoords[bumpIndex][index] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( m_LuxelCoords[bumpIndex][index], luxelCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetLuxelCoords( int bumpIndex, Vector2D const luxelCoords[4] ) +{ + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + for( int i=0; i < 4; i++ ) + Vector2DCopy( luxelCoords[i], m_LuxelCoords[bumpIndex][i] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetLuxelCoords( int bumpIndex, Vector2D luxelCoords[4] ) const +{ + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + for( int i=0; i < 4; i++ ) + Vector2DCopy( m_LuxelCoords[bumpIndex][i], luxelCoords[i] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetAlpha( int index, float alpha ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + m_Alphas[index] = alpha; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispSurface::GetAlpha( int const index ) const +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + return m_Alphas[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetFlags( int flag ) +{ + m_Flags = flag; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetFlags( void ) +{ + return m_Flags; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetContents( int contents ) +{ + m_Contents = contents; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetContents( void ) +{ + return m_Contents; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetSAxis( Vector const &axis ) +{ + VectorCopy( axis, sAxis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetSAxis( Vector& axis ) +{ + VectorCopy( sAxis, axis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetTAxis( Vector const &axis ) +{ + VectorCopy( axis, tAxis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetTAxis( Vector& axis ) +{ + VectorCopy( tAxis, axis ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointStartIndex( int index ) +{ + Assert( index >= 0 ); + Assert( index < QUAD_POINT_COUNT ); + m_PointStartIndex = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispSurface::GetPointStartIndex( void ) +{ + return m_PointStartIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::SetPointStart( Vector const& pt ) +{ + VectorCopy( pt, m_PointStart ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetPointStart( Vector& pt ) +{ + VectorCopy( m_PointStart, pt ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispSurface::GetNormal( Vector& normal ) +{ + // + // calculate the displacement surface normal + // + Vector tmp[2]; + VectorSubtract( m_Points[1], m_Points[0], tmp[0] ); + VectorSubtract( m_Points[3], m_Points[0], tmp[1] ); + CrossProduct( tmp[1], tmp[0], normal ); + VectorNormalize( normal ); +} + + +//========================================================================= +// +// Node Class (for displacement quad-tree) +// +class CCoreDispNode +{ +public: + + enum { MAX_NEIGHBOR_NODE_COUNT = 4 }; + enum { MAX_NEIGHBOR_VERT_COUNT = 8 }; + enum { MAX_SURF_AT_NODE_COUNT = 8 }; + + //========================================================================= + // + // Initialization + // + void Init( void ); + + //========================================================================= + // + // + // + inline void SetBoundingBox( Vector const& bMin, Vector const& bMax ); + inline void GetBoundingBox( Vector& bMin, Vector& bMax ); + + inline void SetErrorTerm( float errorTerm ); + inline float GetErrorTerm( void ); + + inline void SetNeighborNodeIndex( int dir, int index ); + inline int GetNeighborNodeIndex( int dir ); + + inline void SetCenterVertIndex( int index ); + inline int GetCenterVertIndex( void ); + inline void SetNeighborVertIndex( int dir, int index ); + inline int GetNeighborVertIndex( int dir ); + + inline void SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ); + inline void GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ); + inline void SetTriPlane( int index, Vector const& normal, float dist ); + inline void GetTriPlane( int index, cplane_t *plane ); + + inline void SetRayBoundingBox( int index, Vector const& bMin, Vector const& bMax ); + inline void GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ); + + //========================================================================= + // + // Node Functions (friend functions) + // + friend int GetNodeLevel( int index ); + friend int GetNodeCount( int power ); + friend int GetNodeParent( int index ); + friend int GetNodeChild( int power, int index, int direction ); + friend int GetNodeNeighborNode( int power, int index, int direction, int level ); + friend int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient ); + friend int GetNodeMinNodeAtLevel( int level ); + + friend void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, float *v1, float *v2, float *v3 ); + + friend void GetComponentsFromNodeIndex( int index, int *x, int *y ); + friend int GetNodeIndexFromComponents( int x, int y ); + +protected: + + Vector m_BBox[2]; // displacement node bounding box (take into account size of children) + float m_ErrorTerm; // LOD error term (the "precision" of the representation of the surface at this node's level) + int m_VertIndex; // the node's vertex index (center vertex of node) + int m_NeighborVertIndices[MAX_NEIGHBOR_VERT_COUNT]; // all other vertex indices in node (maximally creates 8 trianglar surfaces) + Vector m_SurfBBoxes[MAX_SURF_AT_NODE_COUNT][2]; // surface bounding boxes - old method + cplane_t m_SurfPlanes[MAX_SURF_AT_NODE_COUNT]; // surface plane info - old method + + Vector m_RayBBoxes[4][2]; // bounding boxes for ray traces +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetBoundingBox( Vector const& bMin, Vector const& bMax ) +{ + VectorCopy( bMin, m_BBox[0] ); + VectorCopy( bMax, m_BBox[1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetBoundingBox( Vector& bMin, Vector& bMax ) +{ + VectorCopy( m_BBox[0], bMin ); + VectorCopy( m_BBox[1], bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetErrorTerm( float errorTerm ) +{ + m_ErrorTerm = errorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispNode::GetErrorTerm( void ) +{ + return m_ErrorTerm; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetCenterVertIndex( int index ) +{ + m_VertIndex = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispNode::GetCenterVertIndex( void ) +{ + return m_VertIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetNeighborVertIndex( int dir, int index ) +{ + Assert( dir >= 0 ); + Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); + m_NeighborVertIndices[dir] = index; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispNode::GetNeighborVertIndex( int dir ) +{ + Assert( dir >= 0 ); + Assert( dir < MAX_NEIGHBOR_VERT_COUNT ); + return m_NeighborVertIndices[dir]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetTriBoundingBox( int index, Vector const& bMin, Vector const& bMax ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( bMin, m_SurfBBoxes[index][0] ); + VectorCopy( bMax, m_SurfBBoxes[index][1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetTriBoundingBox( int index, Vector& bMin, Vector& bMax ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( m_SurfBBoxes[index][0], bMin ); + VectorCopy( m_SurfBBoxes[index][1], bMax ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetTriPlane( int index, Vector const &normal, float dist ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( normal, m_SurfPlanes[index].normal ); + m_SurfPlanes[index].dist = dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetTriPlane( int index, cplane_t *plane ) +{ + Assert( index >= 0 ); + Assert( index < MAX_SURF_AT_NODE_COUNT ); + VectorCopy( m_SurfPlanes[index].normal, plane->normal ); + plane->dist = m_SurfPlanes[index].dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::SetRayBoundingBox( int index, Vector const &bMin, Vector const &bMax ) +{ + Assert( index >= 0 ); + Assert( index < 4 ); + VectorCopy( bMin, m_RayBBoxes[index][0] ); + VectorCopy( bMax, m_RayBBoxes[index][1] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispNode::GetRayBoundingBox( int index, Vector& bMin, Vector& bMax ) +{ + Assert( index >= 0 ); + Assert( index < 4 ); + VectorCopy( m_RayBBoxes[index][0], bMin ); + VectorCopy( m_RayBBoxes[index][1], bMax ); +} + + +//============================================================================= +// +// CCoreInfoBuilder - the primary data necessay to derive a displacement surface +// used by WorldCraft (CMapFace, CMapDisp), VRAD (dface_t, ddispinto_t), +// and the engine (msurface_t, CDispInfo) +// + +struct CoreDispVert_t +{ + Vector m_FieldVector; // displacement vector field + float m_FieldDistance; // the distances along the displacement vector normal + + Vector m_SubdivNormal; + Vector m_SubdivPos; // used the create curvature of displacements + + // generated displacement surface data + Vector m_Vert; // displacement surface vertices + Vector m_FlatVert; + Vector m_Normal; // displacement surface normals + Vector m_TangentS; // use in calculating the tangent space axes + Vector m_TangentT; // use in calculating the tangent space axes + Vector2D m_TexCoord; // displacement surface texture coordinates + Vector2D m_LuxelCoords[NUM_BUMP_VECTS+1]; // displacement surface lightmap coordinates + + // additional per-vertex data + float m_Alpha; // displacement alpha values (per displacement vertex) + + Vector4D m_MultiBlend; + Vector4D m_AlphaBlend; + Vector m_vBlendColors[ 4 ]; +}; + +// New, need to use this at the node level +#define COREDISPTRI_TAG_WALKABLE (1<<0) +#define COREDISPTRI_TAG_FORCE_WALKABLE_BIT (1<<1) +#define COREDISPTRI_TAG_FORCE_WALKABLE_VAL (1<<2) +#define COREDISPTRI_TAG_BUILDABLE (1<<3) +#define COREDISPTRI_TAG_FORCE_BUILDABLE_BIT (1<<4) +#define COREDISPTRI_TAG_FORCE_BUILDABLE_VAL (1<<5) + +struct CoreDispTri_t +{ + unsigned short m_iIndex[3]; // the three indices that make up a triangle + unsigned short m_uiTags; // walkable, buildable, etc. +}; + +class CCoreDispInfo : public CDispUtilsHelper +{ +public: + + // + // tree and displacement surface directions + // + enum { WEST = 0, + NORTH = 1, + EAST = 2, + SOUTH = 3, + SOUTHWEST = 4, + SOUTHEAST = 5, + NORTHWEST = 6, + NORTHEAST = 7 }; + +#if 0 + // + // building parameters + // + enum { BUILD_NORMALS = 0x1, + BUILD_TEXCOORDS = 0x2, + BUILD_LIGHTCOORDS = 0x4, + BUILD_LODTREE = 0x8, + BUILD_COLLISION = 0x10, + BUILD_TANGENTSPACE = 0x20 }; +#endif + + // + // surface info flags + // + enum { SURF_BUMPED = 0x1, + SURF_NOPHYSICS_COLL = 0x2, + SURF_NOHULL_COLL = 0x4, + SURF_NORAY_COLL = 0x8 }; + + enum { MAX_DISP_POWER = MAX_MAP_DISP_POWER }; + enum { MAX_VERT_COUNT = MAX_DISPVERTS }; + enum { MAX_NODE_COUNT = 85 }; + + +// Convert from a CDispUtilsHelper. +public: + + static CCoreDispInfo* FromDispUtils( CDispUtilsHelper *p ) { return (CCoreDispInfo*)p; } + + +// CDispUtilsHelper implementation. +public: + + virtual CDispNeighbor* GetEdgeNeighbor( int index ); + virtual CDispCornerNeighbors* GetCornerNeighbors( int index ); + virtual const CPowerInfo* GetPowerInfo() const; + virtual CDispUtilsHelper* GetDispUtilsByIndex( int index ); + + +public: + + //========================================================================= + // + // Creation/Destruction + // + CCoreDispInfo(); + ~CCoreDispInfo(); + + void InitSurf( int parentIndex, Vector points[4], Vector normals[4], + Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags, + bool bGenerateSurfPointStart, Vector& startPoint, + bool bHasMappingAxes, Vector& uAxis, Vector& vAxis ); + + void InitDispInfo( int power, int minTess, float smoothingAngle, float *alphas, Vector *dispVectorField, float *dispDistances, int nFlags, const CDispMultiBlend *pvMultiBlends ); + + // This just unpacks the contents of the verts into arrays and calls InitDispInfo. + void InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris, int nFlags, const CDispMultiBlend *pvMultiBlends ); + +// bool Create( int creationFlags ); + bool Create( void ); + bool CreateWithoutLOD( void ); + + //========================================================================= + // + // Parameter "Wrappers" + // + CCoreDispSurface* GetSurface() { return &m_Surf; } + const CCoreDispSurface* GetSurface() const { return &m_Surf; } + + inline CCoreDispNode *GetNode( int index ); + + inline void SetPower( int power ); + inline int GetPower( void ) const; + inline int GetPostSpacing( void ); + inline int GetWidth( void ); + inline int GetHeight( void ); + inline int GetSize( void ) const; + inline int GetFlags( void ) const; + + // Use this disp as a CDispUtils. + void SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize ); + + void SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] ) { GetSurface()->SetNeighborData( edgeNeighbors, cornerNeighbors ); } + + // Get a corner point. Indexed by the CORNER_ defines. + const CVertIndex& GetCornerPointIndex( int index ) const { return GetPowerInfo()->GetCornerPointIndex( index ); } + const Vector& GetCornerPoint( int index ) const { return GetVert( VertIndexToInt( GetCornerPointIndex( index ) ) ); } + + inline void SetVert( int index, Vector const& vert ); + inline void GetVert( int index, Vector& vert ) const; + + inline const Vector& GetVert( int index ) const; + inline const Vector& GetVert( const CVertIndex &index ) const; + + inline void GetFlatVert( int index, Vector& vert ) const; + inline void SetFlatVert( int index, const Vector &vert ); + + inline void GetNormal( int index, Vector& normal ) const; + inline const Vector& GetNormal( int index ) const; + inline const Vector& GetNormal( const CVertIndex &index ) const; + inline void SetNormal( int index, Vector const& normal ); + inline void SetNormal( const CVertIndex &index, Vector const& normal ); + + inline void GetTangentS( int index, Vector& tangentS ) const; + inline const Vector &GetTangentS( int index ) const; + inline const Vector &GetTangentS( const CVertIndex &index ) const { return GetTangentS(VertIndexToInt(index)); } + inline void GetTangentT( int index, Vector& tangentT ) const; + inline void SetTangentS( int index, Vector const& vTangentS ) { m_pVerts[index].m_TangentS = vTangentS; } + inline void SetTangentT( int index, Vector const& vTangentT ) { m_pVerts[index].m_TangentT = vTangentT; } + + inline void SetTexCoord( int index, Vector2D const& texCoord ); + inline void GetTexCoord( int index, Vector2D& texCoord ) const; + + inline void SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ); + inline void GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const; + + inline void SetAlpha( int index, float alpha ); + inline float GetAlpha( int index ); + + inline void SetMultiBlend( int index, Vector4D &vBlend, Vector4D &vAlphaBlend, Vector &vColor1, Vector &vColor2, Vector &vColor3, Vector &vColor4 ); + inline void SetMultiBlendColor( int index, int nColorIndex, Vector &vColor ); + inline void SetMultiBlend( int index, Vector4D &vBlend ); + inline void SetAlphaBlend( int index, Vector4D &vAlphaBlend ); + inline void GetMultiBlend( int index, Vector4D &vBlend, Vector4D &vAlphaBlend, Vector &vColor1, Vector &vColor2, Vector &vColor3, Vector &vColor4 ) const; + inline void GetMultiBlend( int index, Vector4D &vBlend ) const; + inline void GetAlphaBlend( int index, Vector4D &vAlphaBlend ) const; + + int GetTriCount( void ); + void GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 ); + void SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 ); + void GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 ); + inline void SetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags |= nTag; } + inline void ResetTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags &= ~nTag; } + inline void ToggleTriTag( int iTri, unsigned short nTag ) { m_pTris[iTri].m_uiTags ^= nTag; } + inline bool IsTriTag( int iTri, unsigned short nTag ) { return ( ( m_pTris[iTri].m_uiTags & nTag ) != 0 ); } + inline unsigned short GetTriTagValue( int iTri ) { return m_pTris[iTri].m_uiTags; } + inline void SetTriTagValue( int iTri, unsigned short nVal ) { m_pTris[iTri].m_uiTags = nVal; } + + bool IsTriWalkable( int iTri ); + bool IsTriBuildable( int iTri ); + + inline void SetElevation( float elevation ); + inline float GetElevation( void ); + + inline void ResetFieldVectors( void ); + inline void SetFieldVector( int index, Vector const &v ); + inline void GetFieldVector( int index, Vector& v ); + inline void ResetFieldDistances( void ); + inline void SetFieldDistance( int index, float dist ); + inline float GetFieldDistance( int index ); + + inline void ResetSubdivPositions( void ); + inline void SetSubdivPosition( int ndx, Vector const &v ); + inline void GetSubdivPosition( int ndx, Vector& v ); + + inline void ResetSubdivNormals( void ); + inline void SetSubdivNormal( int ndx, Vector const &v ); + inline void GetSubdivNormal( int ndx, Vector &v ); + + inline void SetRenderIndexCount( int count ); + inline int GetRenderIndexCount( void ); + inline void SetRenderIndex( int index, int triIndex ); + inline int GetRenderIndex( int index ); + + inline CoreDispVert_t *GetDispVert( int iVert ) { return &m_pVerts[iVert]; } + inline CoreDispVert_t *GetDispVertList(); + inline unsigned short *GetRenderIndexList( void ); + + inline void SetTouched( bool touched ); + inline bool IsTouched( void ); + + void CalcDispSurfCoords( bool bLightMap, int lightmapID ); + void GetPositionOnSurface( float u, float v, Vector &vPos, Vector *pNormal, float *pAlpha ); + + void DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint, Vector *pNormal, float *pAlpha ); + void BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV ); + bool SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt ); + + const CDispCornerNeighbors* GetCornerNeighbors( int iCorner ) const { return GetSurface()->GetCornerNeighbors( iCorner ); } + const CDispNeighbor* GetEdgeNeighbor( int iEdge ) const { return GetSurface()->GetEdgeNeighbor( iEdge ); } + + void SetListIndex( int nIndex ) { m_nListIndex = nIndex; } + int GetListIndex( void ) { return m_nListIndex; } + + CBitVec& GetAllowedVerts() { return m_AllowedVerts; } + const CBitVec& GetAllowedVerts() const { return m_AllowedVerts; } + void AllowedVerts_Clear( void ) { m_AllowedVerts.SetAll(); } + int AllowedVerts_GetNumDWords() const { return m_AllowedVerts.GetNumDWords(); } + unsigned long AllowedVerts_GetDWord(int i) const { return m_AllowedVerts.GetDWord( i ); } + void AllowedVerts_SetDWord(int i, unsigned long val) { m_AllowedVerts.SetDWord( i, val ); } + + + void Position_Update( int iVert, Vector vecPos ); + + //========================================================================= + // + // friend functions + // + friend void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppCoreDispInfoList, int listSize ); + +private: + // be changed to match the paint normal next pass) + // LOD/collision node data + CCoreDispNode *m_Nodes; // LOD quad-tree nodes + + float m_Elevation; // distance along the subdivision normal (should + + // defines the size of the displacement surface + int m_Power; // "size" of the displacement map + + // base surface data + CCoreDispSurface m_Surf; // surface containing displacement data + // be changed to match the paint normal next pass) + // Vertex data.. + CoreDispVert_t *m_pVerts; + + // Triangle data.. + CoreDispTri_t *m_pTris; + + int m_nFlags; + + // render specific data + int m_RenderIndexCount; // number of indices used in rendering + unsigned short *m_RenderIndices; // rendering index list (list of triangles) + int m_RenderCounter; // counter to verify surfaces are renderered/collided with only once per frame + + // utility data + bool m_bTouched; // touched flag + CCoreDispInfo *m_pNext; // used for chaining + + // The list that this disp is in (used for CDispUtils::IHelper implementation). + CCoreDispInfo **m_ppListBase; + int m_ListSize; + + CBitVec m_AllowedVerts; // Built in VBSP. Defines which verts are allowed to exist based on what the neighbors are. + + int m_nListIndex; + + //========================================================================= + // + // Creation Functions + // + + void GenerateDispSurf( void ); + void GenerateDispSurfNormals( void ); + void GenerateDispSurfTangentSpaces( void ); + bool DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing ); + void CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4], Vector& normal ); + void CalcDispSurfAlphas( void ); + void GenerateLODTree( void ); + void CalcVertIndicesAtNodes( int nodeIndex ); + int GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction ); + void CalcNodeInfo( int nodeIndex, int terminationLevel ); + void CalcNeighborVertIndicesAtNode( int nodeIndex, int level ); + void CalcNeighborNodeIndicesAtNode( int nodeIndex, int level ); + void CalcErrorTermAtNode( int nodeIndex, int level ); + float GetMaxErrorFromChildren( int nodeIndex, int level ); + void CalcBoundingBoxAtNode( int nodeIndex ); + void CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax ); + void CalcTriSurfInfoAtNode( int nodeIndex ); + void CalcTriSurfIndices( int nodeIndex, int indices[8][3] ); + void CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] ); + void CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] ); + void CalcTriSurfPlanes( int nodeIndex, int indices[8][3] ); + void GenerateCollisionData( void ); + void GenerateCollisionSurface( void ); + + void CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count ); + + void DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); + void DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, float flU, float flV, const Vector &vecIntersectPoint ); + void DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + void DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint, int nSnapU, int nNextU, int nSnapV, int nNextV, Vector &vecPoint, Vector *pNormal, float *pAlpha, bool bBackup ); + + void GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] ); + + void BuildTriTLtoBR( int ndx ); + void BuildTriBLtoTR( int ndx ); + + void InitTris( void ); + void CreateTris( void ); +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetPower( int power ) +{ + m_Power = power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetPower( void ) const +{ + return m_Power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetPostSpacing( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetWidth( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetHeight( void ) +{ + return ( ( 1 << m_Power ) + 1 ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetSize( void ) const +{ + return ( ( ( 1 << m_Power ) + 1 ) * ( ( 1 << m_Power ) + 1 ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetFlags( void ) const +{ + return m_nFlags; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetVert( int index, Vector const &vert ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( vert, m_pVerts[index].m_Vert ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetVert( int index, Vector& vert ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_Vert, vert ); +} + + +inline const Vector& CCoreDispInfo::GetVert( int index ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Vert; +} + +inline const Vector& CCoreDispInfo::GetVert( const CVertIndex &index ) const +{ + return GetVert( VertIndexToInt( index ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetFlatVert( int index, Vector& vert ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_FlatVert, vert ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFlatVert( int index, const Vector &vert ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( vert, m_pVerts[index].m_FlatVert ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( normal, m_pVerts[index].m_Normal ); +} + + +inline void CCoreDispInfo::SetNormal( const CVertIndex &index, Vector const &normal ) +{ + SetNormal( VertIndexToInt( index ), normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetNormal( int index, Vector& normal ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_Normal, normal ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline const Vector& CCoreDispInfo::GetNormal( int index ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Normal; +} + + +inline const Vector& CCoreDispInfo::GetNormal( const CVertIndex &index ) const +{ + return GetNormal( VertIndexToInt( index ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTangentS( int index, Vector& tangentS ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + VectorCopy( m_pVerts[index].m_TangentS, tangentS ); +} + +inline const Vector &CCoreDispInfo::GetTangentS( int index ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + return m_pVerts[index].m_TangentS; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTangentT( int index, Vector& tangentT ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + VectorCopy( m_pVerts[index].m_TangentT, tangentT ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetTexCoord( int index, Vector2D const& texCoord ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Vector2DCopy( texCoord, m_pVerts[index].m_TexCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetTexCoord( int index, Vector2D& texCoord ) const +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Vector2DCopy( m_pVerts[index].m_TexCoord, texCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetLuxelCoord( int bumpIndex, int index, Vector2D const& luxelCoord ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( luxelCoord, m_pVerts[index].m_LuxelCoords[bumpIndex] ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetLuxelCoord( int bumpIndex, int index, Vector2D& luxelCoord ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + Assert( bumpIndex >= 0 ); + Assert( bumpIndex < NUM_BUMP_VECTS + 1 ); + Vector2DCopy( m_pVerts[index].m_LuxelCoords[bumpIndex], luxelCoord ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetAlpha( int index, float alpha ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + m_pVerts[index].m_Alpha = alpha; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetAlpha( int index ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + return m_pVerts[index].m_Alpha; +} + + +inline void CCoreDispInfo::SetMultiBlend( int index, Vector4D &vBlend, Vector4D &vAlphaBlend, Vector &vColor1, Vector &vColor2, Vector &vColor3, Vector &vColor4 ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + m_pVerts[ index ].m_MultiBlend = vBlend; + m_pVerts[ index ].m_AlphaBlend = vAlphaBlend; + m_pVerts[ index ].m_vBlendColors[ 0 ] = vColor1; + m_pVerts[ index ].m_vBlendColors[ 1 ] = vColor2; + m_pVerts[ index ].m_vBlendColors[ 2 ] = vColor3; + m_pVerts[ index ].m_vBlendColors[ 3 ] = vColor4; +} + + +void CCoreDispInfo::SetMultiBlendColor( int index, int nColorIndex, Vector &vColor ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + m_pVerts[ index ].m_vBlendColors[ nColorIndex ] = vColor; +} + + +inline void CCoreDispInfo::SetMultiBlend( int index, Vector4D &vBlend ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + m_pVerts[ index ].m_MultiBlend = vBlend; +} + + +inline void CCoreDispInfo::SetAlphaBlend( int index, Vector4D &vAlphaBlend ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + m_pVerts[ index ].m_AlphaBlend = vAlphaBlend; +} + + +inline void CCoreDispInfo::GetMultiBlend( int index, Vector4D &vBlend, Vector4D &vAlphaBlend, Vector &vColor1, Vector &vColor2, Vector &vColor3, Vector &vColor4 ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + vBlend = m_pVerts[ index ].m_MultiBlend; + vAlphaBlend = m_pVerts[ index ].m_AlphaBlend; + vColor1 = m_pVerts[ index ].m_vBlendColors[ 0 ]; + vColor2 = m_pVerts[ index ].m_vBlendColors[ 1 ]; + vColor3 = m_pVerts[ index ].m_vBlendColors[ 2 ]; + vColor4 = m_pVerts[ index ].m_vBlendColors[ 3 ]; +} + + +inline void CCoreDispInfo::GetMultiBlend( int index, Vector4D &vBlend ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + vBlend = m_pVerts[ index ].m_MultiBlend; +} + + +inline void CCoreDispInfo::GetAlphaBlend( int index, Vector4D &vAlphaBlend ) const +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + + vAlphaBlend = m_pVerts[ index ].m_AlphaBlend; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetElevation( float elevation ) +{ + m_Elevation = elevation; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetElevation( void ) +{ + return m_Elevation; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetFieldVectors( void ) +{ +// Vector normal; +// m_Surf.GetNormal( normal ); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldVector.Init(); +// m_FieldVectors[i] = normal; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFieldVector( int index, Vector const &v ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( v, m_pVerts[index].m_FieldVector ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetFieldVector( int index, Vector& v ) +{ + Assert( index >= 0 ); + Assert( index < MAX_VERT_COUNT ); + VectorCopy( m_pVerts[index].m_FieldVector, v ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetSubdivPositions( void ) +{ + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_SubdivPos.Init(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetSubdivPosition( int ndx, Vector const &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + m_pVerts[ndx].m_SubdivPos = v; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetSubdivPosition( int ndx, Vector& v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + v = m_pVerts[ndx].m_SubdivPos; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetSubdivNormals( void ) +{ + Vector normal; + m_Surf.GetNormal( normal ); + + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_SubdivNormal = normal; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetSubdivNormal( int ndx, Vector const &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + m_pVerts[ndx].m_SubdivNormal = v; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::GetSubdivNormal( int ndx, Vector &v ) +{ + Assert( ndx >= 0 ); + Assert( ndx < MAX_VERT_COUNT ); + v = m_pVerts[ndx].m_SubdivNormal; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::ResetFieldDistances( void ) +{ + int size = GetSize(); + for( int i = 0; i < size; i++ ) + { + m_pVerts[i].m_FieldDistance = 0.0f; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetFieldDistance( int index, float dist ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + m_pVerts[index].m_FieldDistance = dist; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline float CCoreDispInfo::GetFieldDistance( int index ) +{ + Assert( index >= 0 ); + Assert( index < GetSize() ); + return m_pVerts[index].m_FieldDistance; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetRenderIndexCount( int count ) +{ + m_RenderIndexCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetRenderIndexCount( void ) +{ + return m_RenderIndexCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetRenderIndex( int index, int triIndex ) +{ + Assert( index >= 0 ); + Assert( index < ( MAX_VERT_COUNT*2*3) ); + m_RenderIndices[index] = triIndex; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CCoreDispInfo::GetRenderIndex( int index ) +{ + Assert( index >= 0 ); + Assert( index < ( MAX_VERT_COUNT*2*3) ); + return m_RenderIndices[index]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CoreDispVert_t *CCoreDispInfo::GetDispVertList() +{ + return m_pVerts; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline unsigned short *CCoreDispInfo::GetRenderIndexList( void ) +{ + return &m_RenderIndices[0]; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CCoreDispInfo::SetTouched( bool touched ) +{ + m_bTouched = touched; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool CCoreDispInfo::IsTouched( void ) +{ + return m_bTouched; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline CCoreDispNode *CCoreDispInfo::GetNode( int index ) +{ + Assert( index >= 0 ); + Assert( index < MAX_NODE_COUNT ); + return &m_Nodes[index]; +} + +bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2, + Vector const &pt, float &c0, float &c1, float &c2 ); + +#endif // BUILDDISP_H diff --git a/public/captioncompiler.h b/public/captioncompiler.h new file mode 100644 index 0000000..b34f245 --- /dev/null +++ b/public/captioncompiler.h @@ -0,0 +1,77 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef CAPTIONCOMPILER_H +#define CAPTIONCOMPILER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "datamap.h" +#include "checksum_crc.h" + +#define MAX_BLOCK_BITS 13 + +#define MAX_BLOCK_SIZE (1<deleteThis() + virtual void ServerCmdKeyValues( KeyValues *pKeyValues ) = 0; + // Tells the engine what and where to paint + virtual void PaintSurface( const model_t *model, const Vector& position, const Color& color, float radius ) = 0; + // Enable paint in the engine for project Paint + virtual void EnablePaintmapRender() = 0; + virtual void TracePaintSurface( const model_t *model, const Vector& position, float radius, CUtlVector& surfColors ) = 0; + virtual void RemoveAllPaint() = 0; + + virtual bool IsActiveApp() = 0; + + // is this client running inside the same process as an active server? + virtual bool IsClientLocalToActiveServer() = 0; + + // Callback for LevelInit to tick the progress bar during time consuming operations + virtual void TickProgressBar() = 0; + + // Returns the requested input context + virtual InputContextHandle_t GetInputContext( EngineInputContextId_t id ) = 0; + + // let client lock mouse to the window bounds + virtual void SetMouseWindowLock( bool bLockToWindow ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Interface exposed from the client .dll back to the engine +//----------------------------------------------------------------------------- +abstract_class IBaseClientDLL +{ +public: + // Connect appsystem components, get global interfaces, don't run any other init code + virtual int Connect( CreateInterfaceFn appSystemFactory, CGlobalVarsBase *pGlobals ) = 0; + + // run other init code here + virtual int Init( CreateInterfaceFn appSystemFactory, CGlobalVarsBase *pGlobals ) = 0; + + virtual void PostInit() = 0; + + // Called once when the client DLL is being unloaded + virtual void Shutdown( void ) = 0; + + // Called at the start of each level change + virtual void LevelInitPreEntity( char const* pMapName ) = 0; + // Called at the start of a new level, after the entities have been received and created + virtual void LevelInitPostEntity( ) = 0; + // Called at the end of a level + virtual void LevelShutdown( void ) = 0; + + // Request a pointer to the list of client datatable classes + virtual ClientClass *GetAllClasses( void ) = 0; + + // Called once per level to re-initialize any hud element drawing stuff + virtual int HudVidInit( void ) = 0; + // Called by the engine when gathering user input + virtual void HudProcessInput( bool bActive ) = 0; + // Called oncer per frame to allow the hud elements to think + virtual void HudUpdate( bool bActive ) = 0; + // Reset the hud elements to their initial states + virtual void HudReset( void ) = 0; + // Display a hud text message + virtual void HudText( const char * message ) = 0; + + // Mouse Input Interfaces + // Activate the mouse (hides the cursor and locks it to the center of the screen) + virtual void IN_ActivateMouse( void ) = 0; + // Deactivates the mouse (shows the cursor and unlocks it) + virtual void IN_DeactivateMouse( void ) = 0; + // This is only called during extra sound updates and just accumulates mouse x, y offets and recenters the mouse. + // This call is used to try to prevent the mouse from appearing out of the side of a windowed version of the engine if + // rendering or other processing is taking too long + virtual void IN_Accumulate (void) = 0; + // Reset all key and mouse states to their initial, unpressed state + virtual void IN_ClearStates (void) = 0; + // If key is found by name, returns whether it's being held down in isdown, otherwise function returns false + virtual bool IN_IsKeyDown( const char *name, bool& isdown ) = 0; + // Raw keyboard signal, if the client .dll returns 1, the engine processes the key as usual, otherwise, + // if the client .dll returns 0, the key is swallowed. + virtual int IN_KeyEvent( int eventcode, ButtonCode_t keynum, const char *pszCurrentBinding ) = 0; + + // This function is called once per tick to create the player CUserCmd (used for prediction/physics simulation of the player) + // Because the mouse can be sampled at greater than the tick interval, there is a separate input_sample_frametime, which + // specifies how much additional mouse / keyboard simulation to perform. + virtual void CreateMove ( + int sequence_number, // sequence_number of this cmd + float input_sample_frametime, // Frametime for mouse input sampling + bool active ) = 0; // True if the player is active (not paused) + + // If the game is running faster than the tick_interval framerate, then we do extra mouse sampling to avoid jittery input + // This code path is much like the normal move creation code, except no move is created + virtual void ExtraMouseSample( float frametime, bool active ) = 0; + + // Encode the delta (changes) between the CUserCmd in slot from vs the one in slot to. The game code will have + // matching logic to read the delta. + virtual bool WriteUsercmdDeltaToBuffer( int nSlot, bf_write *buf, int from, int to, bool isnewcommand ) = 0; + // Demos need to be able to encode/decode CUserCmds to memory buffers, so these functions wrap that + virtual void EncodeUserCmdToBuffer( int nSlot, bf_write& buf, int slot ) = 0; + virtual void DecodeUserCmdFromBuffer( int nSlot, bf_read& buf, int slot ) = 0; + + // Set up and render one or more views (e.g., rear view window, etc.). This called into RenderView below + virtual void View_Render( vrect_t *rect ) = 0; + + // Allow engine to expressly render a view (e.g., during timerefresh) + // See IVRenderView.h, PushViewFlags_t for nFlags values + virtual void RenderView( const CViewSetup &view, int nClearFlags, int whatToDraw ) = 0; + + // Apply screen fade directly from engine + virtual void View_Fade( ScreenFade_t *pSF ) = 0; + + // The engine has parsed a crosshair angle message, this function is called to dispatch the new crosshair angle + virtual void SetCrosshairAngle( const QAngle& angle ) = 0; + + // Sprite (.spr) model handling code + // Load a .spr file by name + virtual void InitSprite( CEngineSprite *pSprite, const char *loadname ) = 0; + // Shutdown a .spr file + virtual void ShutdownSprite( CEngineSprite *pSprite ) = 0; + // Returns sizeof( CEngineSprite ) so the engine can allocate appropriate memory + virtual int GetSpriteSize( void ) const = 0; + + // Called when a player starts or stops talking. + // entindex is -1 to represent the local client talking (before the data comes back from the server). + // entindex is -2 to represent the local client's voice being acked by the server. + // entindex is GetPlayer() when the server acknowledges that the local client is talking. + virtual void VoiceStatus( int entindex, int iSsSlot, qboolean bTalking ) = 0; + + // Networked string table definitions have arrived, allow client .dll to + // hook string changes with a callback function ( see INetworkStringTableClient.h ) + virtual void InstallStringTableCallback( char const *tableName ) = 0; + + // Notification that we're moving into another stage during the frame. + virtual void FrameStageNotify( ClientFrameStage_t curStage ) = 0; + + // The engine has received the specified user message, this code is used to dispatch the message handler + virtual bool DispatchUserMessage( int msg_type, bf_read &msg_data ) = 0; + + // Save/restore system hooks + virtual CSaveRestoreData *SaveInit( int size ) = 0; + virtual void SaveWriteFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int ) = 0; + virtual void SaveReadFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int ) = 0; + virtual void PreSave( CSaveRestoreData * ) = 0; + virtual void Save( CSaveRestoreData * ) = 0; + virtual void WriteSaveHeaders( CSaveRestoreData * ) = 0; + virtual void ReadRestoreHeaders( CSaveRestoreData * ) = 0; + virtual void Restore( CSaveRestoreData *, bool ) = 0; + virtual void DispatchOnRestore() = 0; + + // Hand over the StandardRecvProxies in the client DLL's module. + virtual CStandardRecvProxies* GetStandardRecvProxies() = 0; + + // save game screenshot writing + virtual void WriteSaveGameScreenshot( const char *pFilename ) = 0; + + // Given a list of "S(wavname) S(wavname2)" tokens, look up the localized text and emit + // the appropriate close caption if running with closecaption = 1 + virtual void EmitSentenceCloseCaption( char const *tokenstream ) = 0; + // Emits a regular close caption by token name + virtual void EmitCloseCaption( char const *captionname, float duration ) = 0; + + // Returns true if the client can start recording a demo now. If the client returns false, + // an error message of up to length bytes should be returned in errorMsg. + virtual bool CanRecordDemo( char *errorMsg, int length ) const = 0; + + // Give the Client a chance to do setup/cleanup. + virtual void OnDemoRecordStart( char const* pDemoBaseName ) = 0; + virtual void OnDemoRecordStop() = 0; + virtual void OnDemoPlaybackStart( char const* pDemoBaseName ) = 0; + virtual void OnDemoPlaybackStop() = 0; + + // Demo polish callbacks. + virtual void RecordDemoPolishUserInput( int nCmdIndex ) = 0; + + // Cache replay ragdolls + virtual bool CacheReplayRagdolls( const char* pFilename, int nStartTick ) = 0; + + // Added interface + + // save game screenshot writing + virtual void WriteSaveGameScreenshotOfSize( const char *pFilename, int width, int height ) = 0; + + // Gets the current view + virtual bool GetPlayerView( CViewSetup &playerView ) = 0; + + virtual bool ShouldHideLoadingPlaque( void ) = 0; + + virtual void InvalidateMdlCache() = 0; + + virtual void IN_SetSampleTime( float frametime ) = 0; + + virtual void OnActiveSplitscreenPlayerChanged( int nNewSlot ) = 0; + // We are entering into/leaving split screen mode (or # of players is changing) + virtual void OnSplitScreenStateChanged() = 0; + + virtual void CenterStringOff() = 0; + + virtual void OnScreenSizeChanged( int nOldWidth, int nOldHeight ) = 0; + + virtual IMaterialProxy *InstantiateMaterialProxy( const char *proxyName ) = 0; + + virtual vgui::VPANEL GetFullscreenClientDLLVPanel( void ) = 0; + + // The engine wants to mark two entities as touching + virtual void MarkEntitiesAsTouching( IClientEntity *e1, IClientEntity *e2 ) = 0; + + virtual void OnKeyBindingChanged( ButtonCode_t buttonCode, char const *pchKeyName, char const *pchNewBinding ) = 0; + + virtual void SetBlurFade( float scale ) = 0; + + virtual void ResetHudCloseCaption() = 0; + + // Called by the engine to allow the new GameUI to handle key events + // Function must return true if the key event was handled + virtual bool HandleGameUIEvent( const InputEvent_t &event ) = 0; + + virtual bool SupportsRandomMaps() = 0; +}; + +#define CLIENT_DLL_INTERFACE_VERSION "VClient016" + +//----------------------------------------------------------------------------- +// Purpose: Interface exposed from the client .dll back to the engine for specifying shared .dll IAppSystems (e.g., ISoundEmitterSystem) +//----------------------------------------------------------------------------- +abstract_class IClientDLLSharedAppSystems +{ +public: + virtual int Count() = 0; + virtual char const *GetDllName( int idx ) = 0; + virtual char const *GetInterfaceName( int idx ) = 0; +}; + +#define CLIENT_DLL_SHARED_APPSYSTEMS "VClientDllSharedAppSystems001" + + +//----------------------------------------------------------------------------- +// Purpose: Interface exposed from client back to materialsystem, currently just for recording into tools +//----------------------------------------------------------------------------- +abstract_class IClientMaterialSystem +{ +public: + virtual HTOOLHANDLE GetCurrentRecordingEntity() = 0; + virtual void PostToolMessage( HTOOLHANDLE hEntity, KeyValues *pMsg ) = 0; +}; + +#define VCLIENTMATERIALSYSTEM_INTERFACE_VERSION "VCLIENTMATERIALSYSTEM001" + + +#endif // CDLL_INT_H diff --git a/public/chunkfile.cpp b/public/chunkfile.cpp new file mode 100644 index 0000000..326bf07 --- /dev/null +++ b/public/chunkfile.cpp @@ -0,0 +1,985 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements an interface for reading and writing heirarchical +// text files of key value pairs. The format of the file is as follows: +// +// chunkname0 +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// chunkname1 +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// } +// } +// ... +// chunknameN +// { +// "key0" "value0" +// "key1" "value1" +// ... +// "keyN" "valueN" +// } +// +// The chunk names are not necessarily unique, nor are the key names, unless the +// parsing application requires them to be. +// +// $NoKeywords: $ +//=============================================================================// + +#include +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include "chunkfile.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "tier1/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CChunkHandlerMap::CChunkHandlerMap(void) +{ + m_pHandlers = NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Frees handler list. +//----------------------------------------------------------------------------- +CChunkHandlerMap::~CChunkHandlerMap(void) +{ + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode != NULL) + { + ChunkHandlerInfoNode_t *pPrev = pNode; + pNode = pNode->pNext; + + delete pPrev; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a chunk handler to the handler list. +// Input : pszChunkName - Name of chunk to be handled. +// pfnHandler - Address of handler callback function. +// pData - Data to pass to the handler callback. +//----------------------------------------------------------------------------- +void CChunkHandlerMap::AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData) +{ + ChunkHandlerInfoNode_t *pNew = new ChunkHandlerInfoNode_t; + + Q_strncpy(pNew->Handler.szChunkName, pszChunkName, sizeof( pNew->Handler.szChunkName )); + pNew->Handler.pfnHandler = pfnHandler; + pNew->Handler.pData = pData; + pNew->pNext = NULL; + + if (m_pHandlers == NULL) + { + m_pHandlers = pNew; + } + else + { + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode->pNext != NULL) + { + pNode = pNode->pNext; + } + pNode->pNext = pNew; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the callback for error handling within this chunk's scope. +// Input : pfnHandler - +// pData - +//----------------------------------------------------------------------------- +void CChunkHandlerMap::SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData) +{ + m_pfnErrorHandler = pfnHandler; + m_pErrorData = pData; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : ppData - +// Output : ChunkErrorHandler_t +//----------------------------------------------------------------------------- +ChunkErrorHandler_t CChunkHandlerMap::GetErrorHandler(void **ppData) +{ + *ppData = m_pErrorData; + return(m_pfnErrorHandler); +} + + +//----------------------------------------------------------------------------- +// Purpose: Gets the handler for a given chunk name, if one has been set. +// Input : pszChunkName - Name of chunk. +// ppfnHandler - Receives the address of the callback function. +// ppData - Receives the context data for the given chunk. +// Output : Returns true if a handler was found, false if not. +//----------------------------------------------------------------------------- +ChunkHandler_t CChunkHandlerMap::GetHandler(const char *pszChunkName, void **ppData) +{ + ChunkHandlerInfoNode_t *pNode = m_pHandlers; + while (pNode != NULL) + { + if (!stricmp(pNode->Handler.szChunkName, pszChunkName)) + { + *ppData = pNode->Handler.pData; + return(pNode->Handler.pfnHandler); + } + + pNode = pNode->pNext; + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. Initializes data members. +//----------------------------------------------------------------------------- +CChunkFile::CChunkFile(void) +{ + m_hFile = NULL; + m_nCurrentDepth = 0; + m_szIndent[0] = '\0'; + m_nHandlerStackDepth = 0; + m_DefaultChunkHandler = 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Closes the file if it is currently open. +//----------------------------------------------------------------------------- +CChunkFile::~CChunkFile(void) +{ + if (m_hFile != NULL) + { + fclose(m_hFile); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszChunkName - +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::BeginChunk(const char *pszChunkName) +{ + // + // Write the chunk name and open curly. + // + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "%s\r\n%s{", pszChunkName, m_szIndent); + ChunkFileResult_t eResult = WriteLine(szBuf); + + // + // Update the indentation depth. + // + if (eResult == ChunkFile_Ok) + { + m_nCurrentDepth++; + BuildIndentString(m_szIndent, m_nCurrentDepth); + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChunkFile::BuildIndentString(char *pszDest, int nDepth) +{ + if (nDepth >= 0) + { + for (int i = 0; i < nDepth; i++) + { + pszDest[i] = '\t'; + } + + pszDest[nDepth] = '\0'; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::Close(void) +{ + if (m_hFile != NULL) + { + fclose(m_hFile); + m_hFile = NULL; + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::EndChunk(void) +{ + if (m_nCurrentDepth > 0) + { + m_nCurrentDepth--; + BuildIndentString(m_szIndent, m_nCurrentDepth); + } + + WriteLine("}"); + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a string explaining the last error that occurred. +//----------------------------------------------------------------------------- +const char *CChunkFile::GetErrorText(ChunkFileResult_t eResult) +{ + static char szError[MAX_KEYVALUE_LEN]; + + switch (eResult) + { + case ChunkFile_UnexpectedEOF: + { + Q_strncpy(szError, "unexpected end of file", sizeof( szError ) ); + break; + } + + case ChunkFile_UnexpectedSymbol: + { + Q_snprintf(szError, sizeof( szError ), "unexpected symbol '%s'", m_szErrorToken); + break; + } + + case ChunkFile_OpenFail: + { + Q_snprintf(szError, sizeof( szError ), "%s", strerror(errno)) ; + break; + } + + case ChunkFile_StringTooLong: + { + Q_strncpy(szError, "unterminated string or string too long", sizeof( szError ) ); + break; + } + + default: + { + Q_snprintf(szError, sizeof( szError ), "error %d", eResult); + } + } + + return(m_TokenReader.Error(szError)); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : eError - +//----------------------------------------------------------------------------- +void CChunkFile::HandleError(const char *szChunkName, ChunkFileResult_t eError) +{ + // UNDONE: dispatch errors to the error handler. + // - keep track of current chunkname for reporting errors + // - use the last non-NULL handler that was pushed onto the stack? + // - need a return code to determine whether to abort parsing? +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Output : ChunkFileResult_t +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::HandleChunk(const char *szChunkName) +{ + // See if the default handler wants it? + if( m_DefaultChunkHandler ) + { + ChunkFileResult_t testResult = m_DefaultChunkHandler( this, m_pDefaultChunkHandlerData, szChunkName ); + if( testResult == ChunkFile_Ok ) + { + return ChunkFile_Ok; + } + } + + // + // If there is an active handler map... + // + if (m_nHandlerStackDepth > 0) + { + CChunkHandlerMap *pHandler = m_HandlerStack[m_nHandlerStackDepth - 1]; + + // + // If a chunk handler was found in the handler map... + // + void *pData; + ChunkHandler_t pfnHandler = pHandler->GetHandler(szChunkName, &pData); + if (pfnHandler != NULL) + { + // Dispatch this chunk to the handler. + ChunkFileResult_t eResult = pfnHandler(this, pData); + if (eResult != ChunkFile_Ok) + { + return(eResult); + } + } + else + { + // + // No handler for this chunk. Skip to the matching close curly brace. + // + int nDepth = 1; + ChunkFileResult_t eResult; + + do + { + ChunkType_t eChunkType; + char szKey[MAX_KEYVALUE_LEN]; + char szValue[MAX_KEYVALUE_LEN]; + + while ((eResult = ReadNext(szKey, szValue, sizeof(szValue), eChunkType)) == ChunkFile_Ok) + { + if (eChunkType == ChunkType_Chunk) + { + nDepth++; + } + } + + if (eResult == ChunkFile_EndOfChunk) + { + eResult = ChunkFile_Ok; + nDepth--; + } + else if (eResult == ChunkFile_EOF) + { + return(ChunkFile_UnexpectedEOF); + } + + } while ((nDepth) && (eResult == ChunkFile_Ok)); + } + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Opens the chunk file for reading or writing. +// Input : pszFileName - Path of file to open. +// eMode - ChunkFile_Read or ChunkFile_Write. +// Output : Returns ChunkFile_Ok on success, ChunkFile_Fail on failure. +// UNDONE: boolean return value? +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::Open(const char *pszFileName, ChunkFileOpenMode_t eMode) +{ + if (eMode == ChunkFile_Read) + { + // UNDONE: TokenReader encapsulates file - unify reading and writing to use the same file I/O. + // UNDONE: Support in-memory parsing. + if (m_TokenReader.Open(pszFileName)) + { + m_nCurrentDepth = 0; + } + else + { + return(ChunkFile_OpenFail); + } + } + else if (eMode == ChunkFile_Write) + { + m_hFile = fopen(pszFileName, "wb"); + + if (m_hFile == NULL) + { + return(ChunkFile_OpenFail); + } + + m_nCurrentDepth = 0; + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: Removes the topmost set of chunk handlers. +//----------------------------------------------------------------------------- +void CChunkFile::PopHandlers(void) +{ + if (m_nHandlerStackDepth > 0) + { + m_nHandlerStackDepth--; + } +} + + +void CChunkFile::SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ) +{ + m_DefaultChunkHandler = pHandler; + m_pDefaultChunkHandlerData = pData;} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a set of chunk handlers to the top of the handler stack. +// Input : pHandlerMap - Object containing the list of chunk handlers. +//----------------------------------------------------------------------------- +void CChunkFile::PushHandlers(CChunkHandlerMap *pHandlerMap) +{ + if (m_nHandlerStackDepth < MAX_INDENT_DEPTH) + { + m_HandlerStack[m_nHandlerStackDepth] = pHandlerMap; + m_nHandlerStackDepth++; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads the next term from the chunk file. The type of term read is +// returned in the eChunkType parameter. +// Input : szName - Name of key or chunk. +// szValue - If eChunkType is ChunkType_Key, contains the value of the key. +// nValueSize - Size of the buffer pointed to by szValue. +// eChunkType - ChunkType_Key or ChunkType_Chunk. +// Output : Returns ChunkFile_Ok on success, an error code if a parsing error occurs. +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::ReadNext(char *szName, char *szValue, int nValueSize, ChunkType_t &eChunkType) +{ + // HACK: pass in buffer sizes? + trtoken_t eTokenType = m_TokenReader.NextToken(szName, MAX_KEYVALUE_LEN); + + if (eTokenType != TOKENEOF) + { + switch (eTokenType) + { + case IDENT: + case STRING: + { + char szNext[MAX_KEYVALUE_LEN]; + trtoken_t eNextTokenType; + + // + // Read the next token to determine what we have. + // + eNextTokenType = m_TokenReader.NextToken(szNext, sizeof(szNext)); + + switch (eNextTokenType) + { + case OPERATOR: + { + if (!stricmp(szNext, "{")) + { + // Beginning of new chunk. + m_nCurrentDepth++; + eChunkType = ChunkType_Chunk; + szValue[0] = '\0'; + return(ChunkFile_Ok); + } + else + { + // Unexpected symbol. + Q_strncpy(m_szErrorToken, szNext, sizeof( m_szErrorToken ) ); + return(ChunkFile_UnexpectedSymbol); + } + } + + case STRING: + case IDENT: + { + // Key value pair. + Q_strncpy(szValue, szNext, nValueSize ); + eChunkType = ChunkType_Key; + return(ChunkFile_Ok); + } + + case TOKENEOF: + { + // Unexpected end of file. + return(ChunkFile_UnexpectedEOF); + } + + case TOKENSTRINGTOOLONG: + { + // String too long or unterminated string. + return ChunkFile_StringTooLong; + } + } + } + + case OPERATOR: + { + if (!stricmp(szName, "}")) + { + // End of current chunk. + m_nCurrentDepth--; + return(ChunkFile_EndOfChunk); + } + else + { + // Unexpected symbol. + Q_strncpy(m_szErrorToken, szName, sizeof( m_szErrorToken ) ); + return(ChunkFile_UnexpectedSymbol); + } + } + + case TOKENSTRINGTOOLONG: + { + return ChunkFile_StringTooLong; + } + } + } + + if (m_nCurrentDepth != 0) + { + // End of file while within the scope of a chunk. + return(ChunkFile_UnexpectedEOF); + } + + return(ChunkFile_EOF); +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads the current chunk and dispatches keys and sub-chunks to the +// appropriate handler callbacks. +// Input : pfnKeyHandler - Callback for any key values in this chunk. +// pData - Data to pass to the key value callback function. +// Output : Normally returns ChunkFile_Ok or ChunkFile_EOF. Otherwise, returns +// a ChunkFile_xxx error code. +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::ReadChunk(KeyHandler_t pfnKeyHandler, void *pData) +{ + // + // Read the keys and sub-chunks. + // + ChunkFileResult_t eResult; + do + { + char szName[MAX_KEYVALUE_LEN]; + char szValue[MAX_KEYVALUE_LEN]; + ChunkType_t eChunkType; + + eResult = ReadNext(szName, szValue, sizeof(szValue), eChunkType); + + if (eResult == ChunkFile_Ok) + { + if (eChunkType == ChunkType_Chunk) + { + // + // Dispatch sub-chunks to the appropriate handler. + // + eResult = HandleChunk(szName); + } + else if ((eChunkType == ChunkType_Key) && (pfnKeyHandler != NULL)) + { + // + // Dispatch keys to the key value handler. + // + eResult = pfnKeyHandler(szName, szValue, pData); + } + } + } while (eResult == ChunkFile_Ok); + + // + // Cover up ChunkFile_EndOfChunk results because the caller doesn't want to see them. + // + if (eResult == ChunkFile_EndOfChunk) + { + eResult = ChunkFile_Ok; + } + + // + // Dispatch errors to the handler. + // + if ((eResult != ChunkFile_Ok) && (eResult != ChunkFile_EOF)) + { + //HandleError("chunkname", eResult); + } + + return(eResult); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pbBool - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueBool(const char *pszValue, bool &bBool) +{ + int nValue = atoi(pszValue); + + if (nValue > 0) + { + bBool = true; + } + else + { + bBool = false; + } + + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfFloat - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueFloat(const char *pszValue, float &flFloat) +{ + flFloat = (float)atof(pszValue); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pnInt - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueInt(const char *pszValue, int &nInt) +{ + nInt = atoi(pszValue); + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// r - +// g - +// b - +// Output : +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue) +{ + if (pszValue != NULL) + { + int r; + int g; + int b; + + if (sscanf(pszValue, "%d %d %d", &r, &g, &b) == 3) + { + chRed = r; + chGreen = g; + chBlue = b; + + return(true); + } + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfPoint - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValuePoint(const char *pszValue, Vector &Point) +{ + if (pszValue != NULL) + { + return(sscanf(pszValue, "(%f %f %f)", &Point.x, &Point.y, &Point.z) == 3); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector2(const char *pszValue, Vector2D &vec) +{ + if (pszValue != NULL) + { + return ( sscanf( pszValue, "[%f %f]", &vec.x, &vec.y) == 2 ); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector3(const char *pszValue, Vector &vec) +{ + if (pszValue != NULL) + { + return(sscanf(pszValue, "[%f %f %f]", &vec.x, &vec.y, &vec.z) == 3); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszValue - +// pfVector - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CChunkFile::ReadKeyValueVector4(const char *pszValue, Vector4D &vec) +{ + if( pszValue != NULL ) + { + return(sscanf(pszValue, "[%f %f %f %f]", &vec[0], &vec[1], &vec[2], &vec[3]) == 4); + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszLine - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValue(const char *pszKey, const char *pszValue) +{ + if ((pszKey != NULL) && (pszValue != NULL)) + { + char szTemp[MAX_KEYVALUE_LEN]; + Q_snprintf(szTemp, sizeof( szTemp ), "\"%s\" \"%s\"", pszKey, pszValue); + return(WriteLine(szTemp)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// bValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueBool(const char *pszKey, bool bValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, (int)bValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// nValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueInt(const char *pszKey, int nValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d\"", pszKey, nValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fValue - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueFloat(const char *pszKey, float fValue) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%g\"", pszKey, (double)fValue); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// r - +// g - +// b - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"%d %d %d\"", pszKey, (int)r, (int)g, (int)b); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fVector - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValuePoint(const char *pszKey, const Vector &Point) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"(%g %g %g)\"", pszKey, (double)Point[0], (double)Point[1], (double)Point[2]); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector2(const char *pszKey, const Vector2D &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf( szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g]\"", pszKey, (double)vec.x, (double)vec.y ); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector3(const char *pszKey, const Vector &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z); + return(WriteLine(szBuf)); + } + + return(ChunkFile_Ok); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pszKey - +// fVector - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteKeyValueVector4(const char *pszKey, const Vector4D &vec) +{ + if (pszKey != NULL) + { + char szBuf[MAX_KEYVALUE_LEN]; + Q_snprintf(szBuf, sizeof( szBuf ), "\"%s\" \"[%g %g %g %g]\"", pszKey, (double)vec.x, (double)vec.y, (double)vec.z, (double)vec.w); + return( WriteLine( szBuf ) ); + } + + return( ChunkFile_Ok ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pszLine - +// Output : +//----------------------------------------------------------------------------- +ChunkFileResult_t CChunkFile::WriteLine(const char *pszLine) +{ + if (pszLine != NULL) + { + // + // Write the indentation string. + // + if (m_nCurrentDepth > 0) + { + int nWritten = fwrite(m_szIndent, 1, m_nCurrentDepth, m_hFile); + if (nWritten != m_nCurrentDepth) + { + return(ChunkFile_Fail); + } + } + + // + // Write the string. + // + int nLen = strlen(pszLine); + int nWritten = fwrite(pszLine, 1, nLen, m_hFile); + if (nWritten != nLen) + { + return(ChunkFile_Fail); + } + + // + // Write the linefeed. + // + if (fwrite("\r\n", 1, 2, m_hFile) != 2) + { + return(ChunkFile_Fail); + } + } + + return(ChunkFile_Ok); +} + diff --git a/public/chunkfile.h b/public/chunkfile.h new file mode 100644 index 0000000..d0e8fec --- /dev/null +++ b/public/chunkfile.h @@ -0,0 +1,197 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef CHUNKFILE_H +#define CHUNKFILE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier2/tokenreader.h" + + +#define MAX_INDENT_DEPTH 80 +#define MAX_KEYVALUE_LEN 1024 + + +class CChunkFile; +class Vector2D; +class Vector; +class Vector4D; + + +// +// Modes for Open. +// +enum ChunkFileOpenMode_t +{ + ChunkFile_Read = 0, + ChunkFile_Write, +}; + + +// +// Return codes. +// +enum ChunkFileResult_t +{ + ChunkFile_Ok = 0, + ChunkFile_Fail, + ChunkFile_OpenFail, + ChunkFile_EndOfChunk, + ChunkFile_EOF, + ChunkFile_UnexpectedEOF, + ChunkFile_UnexpectedSymbol, + ChunkFile_OutOfMemory, + ChunkFile_StringTooLong, + ChunkFile_NotHandled +}; + + +enum ChunkType_t +{ + ChunkType_Key = 0, + ChunkType_Chunk, +}; + + +typedef ChunkFileResult_t (*DefaultChunkHandler_t)(CChunkFile *pFile, void *pData, char const *pChunkName); + +typedef ChunkFileResult_t (*ChunkHandler_t)(CChunkFile *pFile, void *pData); +typedef ChunkFileResult_t (*KeyHandler_t)(const char *szKey, const char *szValue, void *pData); +typedef bool (*ChunkErrorHandler_t)(CChunkFile *pFile, const char *szChunkName, void *pData); + + +struct ChunkHandlerInfo_t +{ + char szChunkName[80]; + ChunkHandler_t pfnHandler; + void *pData; +}; + + +struct ChunkHandlerInfoNode_t +{ + ChunkHandlerInfo_t Handler; + struct ChunkHandlerInfoNode_t *pNext; +}; + + +// +// Consider handling chunks with handler objects instead of callbacks. +// +//class CChunkHandler +//{ +// virtual ChunkFileResult_t HandleChunk(const char *szName); +// virtual ChunkFileResult_t HandleKey(const char *szKey, const char *szValue); +// virtual bool HandleError(const char *szChunkName, ChunkFileResult_t eError); +//}; + + +class CChunkHandlerMap +{ + public: + + CChunkHandlerMap(void); + ~CChunkHandlerMap(void); + + void AddHandler(const char *pszChunkName, ChunkHandler_t pfnHandler, void *pData); + ChunkHandler_t GetHandler(const char *pszChunkName, void **pData); + + void SetErrorHandler(ChunkErrorHandler_t pfnHandler, void *pData); + ChunkErrorHandler_t GetErrorHandler(void **pData); + + protected: + + ChunkHandlerInfoNode_t *m_pHandlers; + ChunkErrorHandler_t m_pfnErrorHandler; + void *m_pErrorData; +}; + + +class CChunkFile +{ + public: + + CChunkFile(void); + ~CChunkFile(void); + + ChunkFileResult_t Open(const char *pszFileName, ChunkFileOpenMode_t eMode); + ChunkFileResult_t Close(void); + const char *GetErrorText(ChunkFileResult_t eResult); + + // + // Functions for writing chunk files. + // + ChunkFileResult_t BeginChunk(const char *pszChunkName); + ChunkFileResult_t EndChunk(void); + + ChunkFileResult_t WriteKeyValue(const char *pszKey, const char *pszValue); + ChunkFileResult_t WriteKeyValueBool(const char *pszKey, bool bValue); + ChunkFileResult_t WriteKeyValueColor(const char *pszKey, unsigned char r, unsigned char g, unsigned char b); + ChunkFileResult_t WriteKeyValueFloat(const char *pszKey, float fValue); + ChunkFileResult_t WriteKeyValueInt(const char *pszKey, int nValue); + ChunkFileResult_t WriteKeyValuePoint(const char *pszKey, const Vector &Point); + ChunkFileResult_t WriteKeyValueVector2(const char *pszKey, const Vector2D &vec); + ChunkFileResult_t WriteKeyValueVector3(const char *pszKey, const Vector &vec); + ChunkFileResult_t WriteKeyValueVector4( const char *pszKey, const Vector4D &vec); + + ChunkFileResult_t WriteLine(const char *pszLine); + + // + // Functions for reading chunk files. + // + ChunkFileResult_t ReadChunk(KeyHandler_t pfnKeyHandler = NULL, void *pData = NULL); + ChunkFileResult_t ReadNext(char *szKey, char *szValue, int nValueSize, ChunkType_t &eChunkType); + ChunkFileResult_t HandleChunk(const char *szChunkName); + void HandleError(const char *szChunkName, ChunkFileResult_t eError); + + // These functions should more really be named Parsexxx and possibly moved elsewhere. + static bool ReadKeyValueBool(const char *pszValue, bool &bBool); + static bool ReadKeyValueColor(const char *pszValue, unsigned char &chRed, unsigned char &chGreen, unsigned char &chBlue); + static bool ReadKeyValueInt(const char *pszValue, int &nInt); + static bool ReadKeyValueFloat(const char *pszValue, float &flFloat); + static bool ReadKeyValuePoint(const char *pszValue, Vector &Point); + static bool ReadKeyValueVector2(const char *pszValue, Vector2D &vec); + static bool ReadKeyValueVector3(const char *pszValue, Vector &vec); + static bool ReadKeyValueVector4( const char *pszValue, Vector4D &vec); + + // The default chunk handler gets called before any other chunk handlers. + // + // If the handler returns ChunkFile_Ok, then it goes into the chunk. + // If the handler returns ChunkFile_NotHandled, then the chunk is + // passed to the regular handlers. + // + // If you pass NULL in here, then it disables the default chunk handler. + void SetDefaultChunkHandler( DefaultChunkHandler_t pHandler, void *pData ); + + void PushHandlers(CChunkHandlerMap *pHandlerMap); + void PopHandlers(void); + + protected: + + void BuildIndentString(char *pszDest, int nDepth); + + TokenReader m_TokenReader; + + FILE *m_hFile; + char m_szErrorToken[80]; + char m_szIndent[MAX_INDENT_DEPTH]; + int m_nCurrentDepth; + + // See SetDefaultChunkHandler.. + DefaultChunkHandler_t m_DefaultChunkHandler; + void *m_pDefaultChunkHandlerData; + + CChunkHandlerMap *m_HandlerStack[MAX_INDENT_DEPTH]; + int m_nHandlerStackDepth; +}; + + +#endif // CHUNKFILE_H diff --git a/public/client_class.cpp b/public/client_class.cpp new file mode 100644 index 0000000..9eecf43 --- /dev/null +++ b/public/client_class.cpp @@ -0,0 +1,16 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "client_class.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ClientClass *g_pClientClassHead=0; + + diff --git a/public/client_class.h b/public/client_class.h new file mode 100644 index 0000000..07296b3 --- /dev/null +++ b/public/client_class.h @@ -0,0 +1,180 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( CLIENT_CLASS_H ) +#define CLIENT_CLASS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "dt_recv.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- + +class Vector; +class CMouthInfo; + + +//----------------------------------------------------------------------------- +// represents a handle used only by the client DLL +//----------------------------------------------------------------------------- + +#include "iclientrenderable.h" +#include "iclientnetworkable.h" + + +class ClientClass; +// Linked list of all known client classes +extern ClientClass *g_pClientClassHead; + +// The serial number that gets passed in is used for ehandles. +typedef IClientNetworkable* (*CreateClientClassFn)( int entnum, int serialNum ); +typedef IClientNetworkable* (*CreateEventFn)(); + +//----------------------------------------------------------------------------- +// Purpose: Client side class definition +//----------------------------------------------------------------------------- +class ClientClass +{ +public: + ClientClass( char *pNetworkName, CreateClientClassFn createFn, CreateEventFn createEventFn, RecvTable *pRecvTable ) + { + m_pNetworkName = pNetworkName; + m_pCreateFn = createFn; + m_pCreateEventFn= createEventFn; + m_pRecvTable = pRecvTable; + + // Link it in + m_pNext = g_pClientClassHead; + g_pClientClassHead = this; + } + + const char* GetName() + { + return m_pNetworkName; + } + +public: + CreateClientClassFn m_pCreateFn; + CreateEventFn m_pCreateEventFn; // Only called for event objects. + char *m_pNetworkName; + RecvTable *m_pRecvTable; + ClientClass *m_pNext; + int m_ClassID; // Managed by the engine. +}; + +#define DECLARE_CLIENTCLASS() \ + virtual int YouForgotToImplementOrDeclareClientClass();\ + virtual ClientClass* GetClientClass();\ + static RecvTable *m_pClassRecvTable; \ + DECLARE_CLIENTCLASS_NOBASE() + + +// This can be used to give all datatables access to protected and private members of the class. +#define ALLOW_DATATABLES_PRIVATE_ACCESS() \ + template friend int ClientClassInit(T *); + + +#define DECLARE_CLIENTCLASS_NOBASE ALLOW_DATATABLES_PRIVATE_ACCESS + +// This macro adds a ClientClass to the linked list in g_pClientClassHead (so +// the list can be given to the engine). +// Use this macro to expose your client class to the engine. +// networkName must match the network name of a class registered on the server. +#define IMPLEMENT_CLIENTCLASS(clientClassName, dataTable, serverClassName) \ + INTERNAL_IMPLEMENT_CLIENTCLASS_PROLOGUE(clientClassName, dataTable, serverClassName) \ + static IClientNetworkable* _##clientClassName##_CreateObject( int entnum, int serialNum ) \ + { \ + clientClassName *pRet = new clientClassName; \ + if ( !pRet ) \ + return 0; \ + pRet->Init( entnum, serialNum ); \ + return pRet; \ + } \ + ClientClass __g_##clientClassName##ClientClass(#serverClassName, \ + _##clientClassName##_CreateObject, \ + NULL,\ + &dataTable::g_RecvTable); + +// Implement a client class and provide a factory so you can allocate and delete it yourself +// (or make it a singleton). +#define IMPLEMENT_CLIENTCLASS_FACTORY(clientClassName, dataTable, serverClassName, factory) \ + INTERNAL_IMPLEMENT_CLIENTCLASS_PROLOGUE(clientClassName, dataTable, serverClassName) \ + ClientClass __g_##clientClassName##ClientClass(#serverClassName, \ + factory, \ + NULL,\ + &dataTable::g_RecvTable); + +// The IMPLEMENT_CLIENTCLASS_DT macros do IMPLEMENT_CLIENT_CLASS and also do BEGIN_RECV_TABLE. +#define IMPLEMENT_CLIENTCLASS_DT(clientClassName, dataTable, serverClassName)\ + IMPLEMENT_CLIENTCLASS(clientClassName, dataTable, serverClassName)\ + BEGIN_RECV_TABLE(clientClassName, dataTable) + +#define IMPLEMENT_CLIENTCLASS_DT_NOBASE(clientClassName, dataTable, serverClassName)\ + IMPLEMENT_CLIENTCLASS(clientClassName, dataTable, serverClassName)\ + BEGIN_RECV_TABLE_NOBASE(clientClassName, dataTable) + + +// Using IMPLEMENT_CLIENTCLASS_EVENT means the engine thinks the entity is an event so the entity +// is responsible for freeing itself. +#define IMPLEMENT_CLIENTCLASS_EVENT(clientClassName, dataTable, serverClassName)\ + INTERNAL_IMPLEMENT_CLIENTCLASS_PROLOGUE(clientClassName, dataTable, serverClassName)\ + static clientClassName __g_##clientClassName; \ + static IClientNetworkable* _##clientClassName##_CreateObject() {return &__g_##clientClassName;}\ + ClientClass __g_##clientClassName##ClientClass(#serverClassName, \ + NULL,\ + _##clientClassName##_CreateObject, \ + &dataTable::g_RecvTable); + +#define IMPLEMENT_CLIENTCLASS_EVENT_DT(clientClassName, dataTable, serverClassName)\ + namespace dataTable {extern RecvTable g_RecvTable;}\ + IMPLEMENT_CLIENTCLASS_EVENT(clientClassName, dataTable, serverClassName)\ + BEGIN_RECV_TABLE(clientClassName, dataTable) + + +// Register a client event singleton but specify a pointer to give to the engine rather than +// have a global instance. This is useful if you're using Initializers and your object's constructor +// uses some other global object (so you must use Initializers so you're constructed afterwards). +#define IMPLEMENT_CLIENTCLASS_EVENT_POINTER(clientClassName, dataTable, serverClassName, ptr)\ + INTERNAL_IMPLEMENT_CLIENTCLASS_PROLOGUE(clientClassName, dataTable, serverClassName)\ + static IClientNetworkable* _##clientClassName##_CreateObject() {return ptr;}\ + ClientClass __g_##clientClassName##ClientClass(#serverClassName, \ + NULL,\ + _##clientClassName##_CreateObject, \ + &dataTable::g_RecvTable); + +#define IMPLEMENT_CLIENTCLASS_EVENT_NONSINGLETON(clientClassName, dataTable, serverClassName)\ + static IClientNetworkable* _##clientClassName##_CreateObject() \ + { \ + clientClassName *p = new clientClassName; \ + if ( p ) \ + p->Init( -1, 0 ); \ + return p; \ + } \ + ClientClass __g_##clientClassName##ClientClass(#serverClassName, \ + NULL,\ + _##clientClassName##_CreateObject, \ + &dataTable::g_RecvTable); + + +// Used internally.. +#define INTERNAL_IMPLEMENT_CLIENTCLASS_PROLOGUE(clientClassName, dataTable, serverClassName) \ + namespace dataTable {extern RecvTable g_RecvTable;}\ + extern ClientClass __g_##clientClassName##ClientClass;\ + RecvTable* clientClassName::m_pClassRecvTable = &dataTable::g_RecvTable;\ + int clientClassName::YouForgotToImplementOrDeclareClientClass() {return 0;}\ + ClientClass* clientClassName::GetClientClass() {return &__g_##clientClassName##ClientClass;} + +#endif // CLIENT_CLASS_H diff --git a/public/client_render_handle.h b/public/client_render_handle.h new file mode 100644 index 0000000..9b7ab92 --- /dev/null +++ b/public/client_render_handle.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CLIENT_RENDER_HANDLE_H +#define CLIENT_RENDER_HANDLE_H +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Foward declarations +//----------------------------------------------------------------------------- +class IClientRenderable; + + +//----------------------------------------------------------------------------- +// Handle to an renderable in the client leaf system +//----------------------------------------------------------------------------- +typedef unsigned short ClientRenderHandle_t; + +enum +{ + INVALID_CLIENT_RENDER_HANDLE = (ClientRenderHandle_t)0xffff, +}; + + +#endif // CLIENT_RENDER_HANDLE_H diff --git a/public/client_textmessage.h b/public/client_textmessage.h new file mode 100644 index 0000000..5559133 --- /dev/null +++ b/public/client_textmessage.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CLIENT_TEXTMESSAGE_H +#define CLIENT_TEXTMESSAGE_H +#ifdef _WIN32 +#pragma once +#endif + +struct client_textmessage_t +{ + int effect; + byte r1, g1, b1, a1; // 2 colors for effects + byte r2, g2, b2, a2; + float x; + float y; + float fadein; + float fadeout; + float holdtime; + float fxtime; + const char *pVGuiSchemeFontName; // If null, use default font for messages + const char *pName; + const char *pMessage; + bool bRoundedRectBackdropBox; + float flBoxSize; // as a function of font height + byte boxcolor[4]; + char const *pClearMessage; // message to clear +}; + +#endif // CLIENT_TEXTMESSAGE_H diff --git a/public/clientstats.h b/public/clientstats.h new file mode 100644 index 0000000..c4290b8 --- /dev/null +++ b/public/clientstats.h @@ -0,0 +1,198 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined( CLIENTSTATS_H ) +#define CLIENTSTATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include +#include "tier0/dbg.h" + +#define INTERFACEVERSION_CLIENTSTATS "ClientStats004" + +//----------------------------------------------------------------------------- +// An interface used to help the client stats implementation tell time +//----------------------------------------------------------------------------- + +struct IClientStatsTime +{ + virtual float GetTime() = 0; +}; + +//----------------------------------------------------------------------------- +// Allows clients to draw their own stats text, will be passed by the +// engine into DisplayStats of the IClientStats interface. +//----------------------------------------------------------------------------- + +struct IClientStatsTextDisplay +{ + // Draws the stats + virtual void DrawStatsText( const char *fmt, ... ) = 0; + + virtual void SetDrawColor( unsigned char r, unsigned char g, unsigned char b ) = 0; + + // Sets a color based on a value and its max acceptable limit + virtual void SetDrawColorFromStatValues( float limit, float value ) = 0; +}; + + +//----------------------------------------------------------------------------- +// This will exist as a singleton within the client DLL and will be hooked into +// the engine to allow clients to render their own stats. +//----------------------------------------------------------------------------- + +abstract_class IClientStats +{ +public: + // This is called at startup to tell the stats about time + virtual void Init( IClientStatsTime* pTime ) = 0; + + // These methods are called at the beginning and the end of each run + virtual void BeginRun() = 0; + virtual void EndRun() = 0; + + // These methods are called at the beginning and the end of each frame + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + + // --------------------------------------------------------------- + // All this stuff is used to prop stats for gathering r_speeds data during timedemo. + // --------------------------------------------------------------- + virtual int GetNumTimesStats( void ) const = 0; + + // returns timed stats + virtual double TimedStatInFrame( int statID ) const = 0; + virtual double TotalTimedStat( int statID ) const = 0; +}; + + +//----------------------------------------------------------------------------- +// This is a templatized implementation which can be instantiated anywhere +// Note that you still have to install it and display it though. +//----------------------------------------------------------------------------- + +template +abstract_class CBaseClientStats : public IClientStats +{ +public: + void Init( IClientStatsTime* pTime ); + void BeginRun(); + void EndRun(); + void BeginFrame(); + void EndFrame(); + + // Timed stat gathering + void BeginTimedStat( int stat ); + void EndTimedStat( int stat ); + + // --------------------------------------------------------------- + // All this stuff is used to prop stats for gathering r_speeds data during timedemo. + // --------------------------------------------------------------- + // returns timed stats + double TimedStatInFrame( int statID ) const + { + Assert( statID >= 0 && statID < timedStatCount ); + Assert( m_StatFrameTime[statID] >= 0.0 ); + return m_StatFrameTime[statID]; + } + + double TotalTimedStat( int statID ) const + { + Assert( statID >= 0 && statID < timedStatCount ); + return m_TotalStatTime[statID]; + } + virtual const char *GetCountedStatName( int statID ) const = 0; + virtual const char *GetTimedStatName( int statID ) const = 0; + +protected: + + // Timed statistics + double m_StatFrameTime[timedStatCount]; + double m_StatStartTime[timedStatCount]; + double m_TotalStatTime[timedStatCount]; + +private: + IClientStatsTime* m_pTime; +}; + + +//----------------------------------------------------------------------------- +// Initializes client stats +//----------------------------------------------------------------------------- + +template +void CBaseClientStats::Init( IClientStatsTime* pTime ) +{ + Assert( pTime ); + m_pTime = pTime; +} + +//----------------------------------------------------------------------------- +// These methods are called at the beginning and the end of each run +//----------------------------------------------------------------------------- + +template +void CBaseClientStats::BeginRun() +{ + int i; + + for (i = 0; i < timedStatCount; ++i) + m_TotalStatTime[i] = 0.0; + +} + +template +void CBaseClientStats::EndRun() +{ +} + + +//----------------------------------------------------------------------------- +// These methods are called at the beginning and the end of each frame +//----------------------------------------------------------------------------- + +template +void CBaseClientStats::BeginFrame() +{ + int i; + for (i = 0; i < timedStatCount; ++i) + m_StatFrameTime[i] = 0.0; +} + +template +void CBaseClientStats::EndFrame() +{ + int i; + for (i = 0; i < timedStatCount; ++i) + m_TotalStatTime[i] += m_StatFrameTime[i]; +} + + +//----------------------------------------------------------------------------- +// Inlined stat gathering methods +//----------------------------------------------------------------------------- + +template +void CBaseClientStats::BeginTimedStat( int stat ) +{ + if (m_pTime) + m_StatStartTime[stat] = m_pTime->GetTime(); +} + +template +void CBaseClientStats::EndTimedStat( int stat ) +{ + if (m_pTime) + m_StatFrameTime[stat] += m_pTime->GetTime() - m_StatStartTime[stat]; +} + + +#endif // CLIENTSTATS_H diff --git a/public/closedcaptions.cpp b/public/closedcaptions.cpp new file mode 100644 index 0000000..287a03a --- /dev/null +++ b/public/closedcaptions.cpp @@ -0,0 +1,58 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "closedcaptions.h" +#include "filesystem.h" +#include "tier1/utlbuffer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// Assumed to be set up by calling code +bool AsyncCaption_t::LoadFromFile( const char *pRelativePath ) +{ + char pRelativePathFixed[MAX_PATH]; + Q_strncpy( pRelativePathFixed, pRelativePath, sizeof(pRelativePathFixed) ); + Q_FixSlashes( pRelativePathFixed ); + Q_strlower( pRelativePathFixed ); + pRelativePath = pRelativePathFixed; + + if ( Q_IsAbsolutePath( pRelativePath ) ) + { + Warning( "AsyncCaption_t::LoadFromFile: Fullpath encountered! %s\n", pRelativePath ); + } + + FileHandle_t fh = g_pFullFileSystem->Open( pRelativePath, "rb", "GAME" ); + if ( FILESYSTEM_INVALID_HANDLE == fh ) + return false; + + MEM_ALLOC_CREDIT(); + + CUtlBuffer dirbuffer; + + // Read the header + g_pFullFileSystem->Read( &m_Header, sizeof( m_Header ), fh ); + if ( m_Header.magic != COMPILED_CAPTION_FILEID ) + Error( "Invalid file id for %s\n", pRelativePath ); + if ( m_Header.version != COMPILED_CAPTION_VERSION ) + Error( "Invalid file version for %s\n", pRelativePath ); + if ( m_Header.directorysize < 0 || m_Header.directorysize > 64 * 1024 ) + Error( "Invalid directory size %d for %s\n", m_Header.directorysize, pRelativePath ); + //if ( m_Header.blocksize != MAX_BLOCK_SIZE ) + // Error( "Invalid block size %d, expecting %d for %s\n", m_Header.blocksize, MAX_BLOCK_SIZE, pchFullPath ); + + int directoryBytes = m_Header.directorysize * sizeof( CaptionLookup_t ); + m_CaptionDirectory.EnsureCapacity( m_Header.directorysize ); + dirbuffer.EnsureCapacity( directoryBytes ); + + g_pFullFileSystem->Read( dirbuffer.Base(), directoryBytes, fh ); + g_pFullFileSystem->Close( fh ); + + m_CaptionDirectory.CopyArray( (const CaptionLookup_t *)dirbuffer.PeekGet(), m_Header.directorysize ); + m_CaptionDirectory.RedoSort( true ); + + m_DataBaseFile = pRelativePath; + return true; +} diff --git a/public/closedcaptions.h b/public/closedcaptions.h new file mode 100644 index 0000000..56b97d3 --- /dev/null +++ b/public/closedcaptions.h @@ -0,0 +1,70 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef CLOSEDCAPTIONS_H +#define CLOSEDCAPTIONS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "captioncompiler.h" +#include "tier1/utlsymbol.h" +#include "tier1/UtlSortVector.h" + +FORWARD_DECLARE_HANDLE( memhandle_t ); + +typedef CUtlSortVector< CaptionLookup_t, CCaptionLookupLess > CaptionDictionary_t; +struct AsyncCaption_t +{ + AsyncCaption_t() : + m_DataBaseFile( UTL_INVAL_SYMBOL ), + m_RequestedBlocks( 0, 0, BlockInfo_t::Less ) + { + Q_memset( &m_Header, 0, sizeof( m_Header ) ); + } + + struct BlockInfo_t + { + int fileindex; + int blocknum; + memhandle_t handle; + + static bool Less( const BlockInfo_t& lhs, const BlockInfo_t& rhs ) + { + if ( lhs.fileindex != rhs.fileindex ) + return lhs.fileindex < rhs.fileindex; + + return lhs.blocknum < rhs.blocknum; + } + }; + + AsyncCaption_t& operator =( const AsyncCaption_t& rhs ) + { + if ( this == &rhs ) + return *this; + + m_CaptionDirectory = rhs.m_CaptionDirectory; + m_Header = rhs.m_Header; + m_DataBaseFile = rhs.m_DataBaseFile; + + for ( int i = rhs.m_RequestedBlocks.FirstInorder(); i != rhs.m_RequestedBlocks.InvalidIndex(); i = rhs.m_RequestedBlocks.NextInorder( i ) ) + { + m_RequestedBlocks.Insert( rhs.m_RequestedBlocks[ i ] ); + } + + return *this; + } + + bool LoadFromFile( char const *pchFullPath ); + + CUtlRBTree< BlockInfo_t, unsigned short > m_RequestedBlocks; + + CaptionDictionary_t m_CaptionDirectory; + CompiledCaptionHeader_t m_Header; + CUtlSymbol m_DataBaseFile; +}; + +#endif // CLOSEDCAPTIONS_H diff --git a/public/cmodel.h b/public/cmodel.h new file mode 100644 index 0000000..c97c737 --- /dev/null +++ b/public/cmodel.h @@ -0,0 +1,133 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef CMODEL_H +#define CMODEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "trace.h" +#include "tier0/dbg.h" +#include "basehandle.h" + +struct edict_t; +struct model_t; + +/* +============================================================== + +COLLISION DETECTION + +============================================================== +*/ + +#include "bspflags.h" +//#include "mathlib/vector.h" + +// gi.BoxEdicts() can return a list of either solid or trigger entities +// FIXME: eliminate AREA_ distinction? +#define AREA_SOLID 1 +#define AREA_TRIGGERS 2 + +#include "vcollide.h" + +struct cmodel_t +{ + Vector mins, maxs; + Vector origin; // for sounds or lights + int headnode; + + vcollide_t vcollisionData; +}; + +struct csurface_t +{ + const char *name; + short surfaceProps; + unsigned short flags; // BUGBUG: These are declared per surface, not per material, but this database is per-material now +}; + +//----------------------------------------------------------------------------- +// A ray... +//----------------------------------------------------------------------------- +struct Ray_t +{ + VectorAligned m_Start; // starting point, centered within the extents + VectorAligned m_Delta; // direction + length of the ray + VectorAligned m_StartOffset; // Add this to m_Start to get the actual ray start + VectorAligned m_Extents; // Describes an axis aligned box extruded along a ray + const matrix3x4_t *m_pWorldAxisTransform; + bool m_IsRay; // are the extents zero? + bool m_IsSwept; // is delta != 0? + + Ray_t() : m_pWorldAxisTransform( NULL ) {} + + void Init( Vector const& start, Vector const& end ) + { + Assert( &end ); + VectorSubtract( end, start, m_Delta ); + + m_IsSwept = (m_Delta.LengthSqr() != 0); + + VectorClear( m_Extents ); + m_pWorldAxisTransform = NULL; + m_IsRay = true; + + // Offset m_Start to be in the center of the box... + VectorClear( m_StartOffset ); + VectorCopy( start, m_Start ); + } + + void Init( Vector const& start, Vector const& end, Vector const& mins, Vector const& maxs ) + { + Assert( &end ); + VectorSubtract( end, start, m_Delta ); + + m_pWorldAxisTransform = NULL; + m_IsSwept = (m_Delta.LengthSqr() != 0); + + VectorSubtract( maxs, mins, m_Extents ); + m_Extents *= 0.5f; + m_IsRay = (m_Extents.LengthSqr() < 1e-6); + + // Offset m_Start to be in the center of the box... + VectorAdd( mins, maxs, m_StartOffset ); + m_StartOffset *= 0.5f; + VectorAdd( start, m_StartOffset, m_Start ); + m_StartOffset *= -1.0f; + } + + // compute inverse delta + Vector InvDelta() const + { + Vector vecInvDelta; + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + if ( m_Delta[iAxis] != 0.0f ) + { + vecInvDelta[iAxis] = 1.0f / m_Delta[iAxis]; + } + else + { + vecInvDelta[iAxis] = FLT_MAX; + } + } + return vecInvDelta; + } + +private: +}; + + +#endif // CMODEL_H + + +#include "gametrace.h" + diff --git a/public/collisionutils.cpp b/public/collisionutils.cpp new file mode 100644 index 0000000..abafefc --- /dev/null +++ b/public/collisionutils.cpp @@ -0,0 +1,3406 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Common collision utility methods +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include "collisionutils.h" +#include "cmodel.h" +#include "mathlib/mathlib.h" +#include "mathlib/vector.h" +#include "tier0/dbg.h" +#include +#include "mathlib/vector4d.h" +#include "trace.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define UNINIT -99999.0 + +//----------------------------------------------------------------------------- +// Clears the trace +//----------------------------------------------------------------------------- +static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace ) +{ + pTrace->startpos = vecRayStart; + pTrace->endpos = vecRayStart; + pTrace->endpos += vecRayDelta; + pTrace->startsolid = false; + pTrace->allsolid = false; + pTrace->fraction = 1.0f; + pTrace->contents = 0; +} + + +//----------------------------------------------------------------------------- +// Compute the offset in t along the ray that we'll use for the collision +//----------------------------------------------------------------------------- +static float ComputeBoxOffset( const Ray_t& ray ) +{ + if (ray.m_IsRay) + return 1e-3f; + + // Find the projection of the box diagonal along the ray... + float offset = FloatMakePositive(ray.m_Extents[0] * ray.m_Delta[0]) + + FloatMakePositive(ray.m_Extents[1] * ray.m_Delta[1]) + + FloatMakePositive(ray.m_Extents[2] * ray.m_Delta[2]); + + // We need to divide twice: Once to normalize the computation above + // so we get something in units of extents, and the second to normalize + // that with respect to the entire raycast. + offset *= InvRSquared( ray.m_Delta ); + + // 1e-3 is an epsilon + return offset + 1e-3; +} + + +//----------------------------------------------------------------------------- +// Intersects a swept box against a triangle +//----------------------------------------------------------------------------- +float IntersectRayWithTriangle( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, bool oneSided ) +{ + // This is cute: Use barycentric coordinates to represent the triangle + // Vo(1-u-v) + V1u + V2v and intersect that with a line Po + Dt + // This gives us 3 equations + 3 unknowns, which we can solve with + // Cramer's rule... + // E1x u + E2x v - Dx t = Pox - Vox + // There's a couple of other optimizations, Cramer's rule involves + // computing the determinant of a matrix which has been constructed + // by three vectors. It turns out that + // det | A B C | = -( A x C ) dot B or -(C x B) dot A + // which we'll use below.. + + Vector edge1, edge2, org; + VectorSubtract( v2, v1, edge1 ); + VectorSubtract( v3, v1, edge2 ); + + // Cull out one-sided stuff + if (oneSided) + { + Vector normal; + CrossProduct( edge1, edge2, normal ); + if (DotProduct( normal, ray.m_Delta ) >= 0.0f) + return -1.0f; + } + + // FIXME: This is inaccurate, but fast for boxes + // We want to do a fast separating axis implementation here + // with a swept triangle along the reverse direction of the ray. + + // Compute some intermediary terms + Vector dirCrossEdge2, orgCrossEdge1; + CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); + + // Compute the denominator of Cramer's rule: + // | -Dx E1x E2x | + // det | -Dy E1y E2y | = (D x E2) dot E1 + // | -Dz E1z E2z | + float denom = DotProduct( dirCrossEdge2, edge1 ); + if( FloatMakePositive( denom ) < 1e-6 ) + return -1.0f; + denom = 1.0f / denom; + + // Compute u. It's gotta lie in the range of 0 to 1. + // | -Dx orgx E2x | + // u = denom * det | -Dy orgy E2y | = (D x E2) dot org + // | -Dz orgz E2z | + VectorSubtract( ray.m_Start, v1, org ); + float u = DotProduct( dirCrossEdge2, org ) * denom; + if ((u < 0.0f) || (u > 1.0f)) + return -1.0f; + + // Compute t and v the same way... + // In barycentric coords, u + v < 1 + CrossProduct( org, edge1, orgCrossEdge1 ); + float v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; + if ((v < 0.0f) || (v + u > 1.0f)) + return -1.0f; + + // Compute the distance along the ray direction that we need to fudge + // when using swept boxes + float boxt = ComputeBoxOffset( ray ); + float t = DotProduct( orgCrossEdge1, edge2 ) * denom; + if ((t < -boxt) || (t > 1.0f + boxt)) + return -1.0f; + + return clamp( t, 0, 1 ); +} + +//----------------------------------------------------------------------------- +// computes the barycentric coordinates of an intersection +//----------------------------------------------------------------------------- + +bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, + float *t ) +{ + Vector edge1, edge2, org; + VectorSubtract( v2, v1, edge1 ); + VectorSubtract( v3, v1, edge2 ); + + // Compute some intermediary terms + Vector dirCrossEdge2, orgCrossEdge1; + CrossProduct( ray.m_Delta, edge2, dirCrossEdge2 ); + + // Compute the denominator of Cramer's rule: + // | -Dx E1x E2x | + // det | -Dy E1y E2y | = (D x E2) dot E1 + // | -Dz E1z E2z | + float denom = DotProduct( dirCrossEdge2, edge1 ); + if( FloatMakePositive( denom ) < 1e-6 ) + return false; + denom = 1.0f / denom; + + // Compute u. It's gotta lie in the range of 0 to 1. + // | -Dx orgx E2x | + // u = denom * det | -Dy orgy E2y | = (D x E2) dot org + // | -Dz orgz E2z | + VectorSubtract( ray.m_Start, v1, org ); + u = DotProduct( dirCrossEdge2, org ) * denom; + + // Compute t and v the same way... + // In barycentric coords, u + v < 1 + CrossProduct( org, edge1, orgCrossEdge1 ); + v = DotProduct( orgCrossEdge1, ray.m_Delta ) * denom; + + // Compute the distance along the ray direction that we need to fudge + // when using swept boxes + if( t ) + { + float boxt = ComputeBoxOffset( ray ); + *t = DotProduct( orgCrossEdge1, edge2 ) * denom; + if( ( *t < -boxt ) || ( *t > 1.0f + boxt ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Intersects a plane with a triangle (requires barycentric definition) +//----------------------------------------------------------------------------- + +int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, + const Vector& edgeV, const Vector4D& plane, Vector2D* pIntersection ) +{ + // This uses a barycentric method, since we need that to determine + // interpolated points, alphas, and normals + // Given the plane equation P dot N + d = 0 + // and the barycentric coodinate equation P = Org + EdgeU * u + EdgeV * v + // Plug em in. Intersection occurs at u = 0 or v = 0 or u + v = 1 + + float orgDotNormal = DotProduct( org, plane.AsVector3D() ); + float edgeUDotNormal = DotProduct( edgeU, plane.AsVector3D() ); + float edgeVDotNormal = DotProduct( edgeV, plane.AsVector3D() ); + + int ptIdx = 0; + + // u = 0 + if ( edgeVDotNormal != 0.0f ) + { + pIntersection[ptIdx].x = 0.0f; + pIntersection[ptIdx].y = - ( orgDotNormal - plane.w ) / edgeVDotNormal; + if ((pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) + ++ptIdx; + } + + // v = 0 + if ( edgeUDotNormal != 0.0f ) + { + pIntersection[ptIdx].x = - ( orgDotNormal - plane.w ) / edgeUDotNormal; + pIntersection[ptIdx].y = 0.0f; + if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f)) + ++ptIdx; + } + + // u + v = 1 + if (ptIdx == 2) + return ptIdx; + + if ( edgeVDotNormal != edgeUDotNormal ) + { + pIntersection[ptIdx].x = - ( orgDotNormal - plane.w + edgeVDotNormal) / + ( edgeUDotNormal - edgeVDotNormal); + pIntersection[ptIdx].y = 1.0f - pIntersection[ptIdx].x;; + if ((pIntersection[ptIdx].x >= 0.0f) && (pIntersection[ptIdx].x <= 1.0f) && + (pIntersection[ptIdx].y >= 0.0f) && (pIntersection[ptIdx].y <= 1.0f)) + ++ptIdx; + } + + Assert( ptIdx < 3 ); + return ptIdx; +} + + +//----------------------------------------------------------------------------- +// Returns true if a box intersects with a sphere +//----------------------------------------------------------------------------- +bool IsSphereIntersectingSphere( const Vector& center1, float radius1, + const Vector& center2, float radius2 ) +{ + Vector delta; + VectorSubtract( center2, center1, delta ); + float distSq = delta.LengthSqr(); + float radiusSum = radius1 + radius2; + return (distSq <= (radiusSum * radiusSum)); +} + + +//----------------------------------------------------------------------------- +// Returns true if a box intersects with a sphere +//----------------------------------------------------------------------------- +bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, + const Vector& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta; + + // Unrolled the loop.. this is a big cycle stealer... + if (center[0] < boxMin[0]) + { + flDelta = center[0] - boxMin[0]; + dmin += flDelta * flDelta; + } + else if (center[0] > boxMax[0]) + { + flDelta = boxMax[0] - center[0]; + dmin += flDelta * flDelta; + } + + if (center[1] < boxMin[1]) + { + flDelta = center[1] - boxMin[1]; + dmin += flDelta * flDelta; + } + else if (center[1] > boxMax[1]) + { + flDelta = boxMax[1] - center[1]; + dmin += flDelta * flDelta; + } + + if (center[2] < boxMin[2]) + { + flDelta = center[2] - boxMin[2]; + dmin += flDelta * flDelta; + } + else if (center[2] > boxMax[2]) + { + flDelta = boxMax[2] - center[2]; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + +bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, + const Vector& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta, flDiff; + + // Unrolled the loop.. this is a big cycle stealer... + flDiff = FloatMakePositive( center.x - boxCenter.x ); + if (flDiff > boxHalfDiag.x) + { + flDelta = flDiff - boxHalfDiag.x; + dmin += flDelta * flDelta; + } + + flDiff = FloatMakePositive( center.y - boxCenter.y ); + if (flDiff > boxHalfDiag.y) + { + flDelta = flDiff - boxHalfDiag.y; + dmin += flDelta * flDelta; + } + + flDiff = FloatMakePositive( center.z - boxCenter.z ); + if (flDiff > boxHalfDiag.z) + { + flDelta = flDiff - boxHalfDiag.z; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + + +//----------------------------------------------------------------------------- +// Returns true if a rectangle intersects with a circle +//----------------------------------------------------------------------------- +bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, + const Vector2D& center, float radius ) +{ + // See Graphics Gems, box-sphere intersection + float dmin = 0.0f; + float flDelta; + + if (center[0] < boxMin[0]) + { + flDelta = center[0] - boxMin[0]; + dmin += flDelta * flDelta; + } + else if (center[0] > boxMax[0]) + { + flDelta = boxMax[0] - center[0]; + dmin += flDelta * flDelta; + } + + if (center[1] < boxMin[1]) + { + flDelta = center[1] - boxMin[1]; + dmin += flDelta * flDelta; + } + else if (center[1] > boxMax[1]) + { + flDelta = boxMax[1] - center[1]; + dmin += flDelta * flDelta; + } + + return dmin < radius * radius; +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between ray and sphere +//----------------------------------------------------------------------------- +bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector& vecCenter, float flRadius, float flTolerance ) +{ + // For this algorithm, find a point on the ray which is closest to the sphere origin + // Do this by making a plane passing through the sphere origin + // whose normal is parallel to the ray. Intersect that plane with the ray. + // Plane: N dot P = I, N = D (ray direction), I = C dot N = C dot D + // Ray: P = O + D * t + // D dot ( O + D * t ) = C dot D + // D dot O + D dot D * t = C dot D + // t = (C - O) dot D / D dot D + // Clamp t to (0,1) + // Find distance of the point on the ray to the sphere center. + Assert( flTolerance >= 0.0f ); + flRadius += flTolerance; + + Vector vecRayToSphere; + VectorSubtract( vecCenter, vecRayOrigin, vecRayToSphere ); + float flNumerator = DotProduct( vecRayToSphere, vecRayDelta ); + + float t; + if (flNumerator <= 0.0f) + { + t = 0.0f; + } + else + { + float flDenominator = DotProduct( vecRayDelta, vecRayDelta ); + if ( flNumerator > flDenominator ) + t = 1.0f; + else + t = flNumerator / flDenominator; + } + + Vector vecClosestPoint; + VectorMA( vecRayOrigin, t, vecRayDelta, vecClosestPoint ); + return ( vecClosestPoint.DistToSqr( vecCenter ) <= flRadius * flRadius ); + + // NOTE: This in an alternate algorithm which I didn't use because I'd have to use a sqrt + // So it's probably faster to do this other algorithm. I'll leave the comments here + // for how to go back if we want to + + // Solve using the ray equation + the sphere equation + // P = o + dt + // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 + // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 + // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + + // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + + // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 + // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + + // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 + // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a + // a = DotProduct( vecRayDelta, vecRayDelta ); + // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) + // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; + // Valid solutions are possible only if b^2 - 4ac >= 0 + // Therefore, compute that value + see if we got it +} + + +//----------------------------------------------------------------------------- +// +// IntersectInfiniteRayWithSphere +// +// Returns whether or not there was an intersection. +// Returns the two intersection points +// +//----------------------------------------------------------------------------- +bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) +{ + // Solve using the ray equation + the sphere equation + // P = o + dt + // (x - xc)^2 + (y - yc)^2 + (z - zc)^2 = r^2 + // (ox + dx * t - xc)^2 + (oy + dy * t - yc)^2 + (oz + dz * t - zc)^2 = r^2 + // (ox - xc)^2 + 2 * (ox-xc) * dx * t + dx^2 * t^2 + + // (oy - yc)^2 + 2 * (oy-yc) * dy * t + dy^2 * t^2 + + // (oz - zc)^2 + 2 * (oz-zc) * dz * t + dz^2 * t^2 = r^2 + // (dx^2 + dy^2 + dz^2) * t^2 + 2 * ((ox-xc)dx + (oy-yc)dy + (oz-zc)dz) t + + // (ox-xc)^2 + (oy-yc)^2 + (oz-zc)^2 - r^2 = 0 + // or, t = (-b +/- sqrt( b^2 - 4ac)) / 2a + // a = DotProduct( vecRayDelta, vecRayDelta ); + // b = 2 * DotProduct( vecRayOrigin - vecCenter, vecRayDelta ) + // c = DotProduct(vecRayOrigin - vecCenter, vecRayOrigin - vecCenter) - flRadius * flRadius; + + Vector vecSphereToRay; + VectorSubtract( vecRayOrigin, vecSphereCenter, vecSphereToRay ); + + float a = DotProduct( vecRayDelta, vecRayDelta ); + + // This would occur in the case of a zero-length ray + if ( a == 0.0f ) + { + *pT1 = *pT2 = 0.0f; + return vecSphereToRay.LengthSqr() <= flRadius * flRadius; + } + + float b = 2 * DotProduct( vecSphereToRay, vecRayDelta ); + float c = DotProduct( vecSphereToRay, vecSphereToRay ) - flRadius * flRadius; + float flDiscrim = b * b - 4 * a * c; + if ( flDiscrim < 0.0f ) + return false; + + flDiscrim = sqrt( flDiscrim ); + float oo2a = 0.5f / a; + *pT1 = ( - b - flDiscrim ) * oo2a; + *pT2 = ( - b + flDiscrim ) * oo2a; + return true; +} + + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithSphere +// +// Returns whether or not there was an intersection. +// Returns the two intersection points, clamped to (0,1) +// +//----------------------------------------------------------------------------- +bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ) +{ + if ( !IntersectInfiniteRayWithSphere( vecRayOrigin, vecRayDelta, vecSphereCenter, flRadius, pT1, pT2 ) ) + return false; + + if (( *pT1 > 1.0f ) || ( *pT2 < 0.0f )) + return false; + + // Clamp it! + if ( *pT1 < 0.0f ) + *pT1 = 0.0f; + if ( *pT2 > 1.0f ) + *pT2 = 1.0f; + + return true; +} + + +// returns true if the sphere and cone intersect +// NOTE: cone sine/cosine are the half angle of the cone +bool IsSphereIntersectingCone( const Vector &sphereCenter, float sphereRadius, const Vector &coneOrigin, const Vector &coneNormal, float coneSine, float coneCosine ) +{ + Vector backCenter = coneOrigin - (sphereRadius / coneSine) * coneNormal; + Vector delta = sphereCenter - backCenter; + float deltaLen = delta.Length(); + if ( DotProduct(coneNormal, delta) >= deltaLen*coneCosine ) + { + delta = sphereCenter - coneOrigin; + deltaLen = delta.Length(); + if ( -DotProduct(coneNormal, delta) >= deltaLen * coneSine ) + { + return ( deltaLen <= sphereRadius ) ? true : false; + } + return true; + } + return false; +} + + + +//----------------------------------------------------------------------------- +// returns true if the point is in the box +//----------------------------------------------------------------------------- +bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ) +{ + Assert( boxMin[0] <= boxMax[0] ); + Assert( boxMin[1] <= boxMax[1] ); + Assert( boxMin[2] <= boxMax[2] ); + + // on x360, force use of SIMD version. + if (IsX360()) + { + return IsPointInBox( LoadUnaligned3SIMD(pt.Base()), LoadUnaligned3SIMD(boxMin.Base()), LoadUnaligned3SIMD(boxMax.Base()) ) ; + } + + if ( (pt[0] > boxMax[0]) || (pt[0] < boxMin[0]) ) + return false; + if ( (pt[1] > boxMax[1]) || (pt[1] < boxMin[1]) ) + return false; + if ( (pt[2] > boxMax[2]) || (pt[2] < boxMin[2]) ) + return false; + return true; +} + + +bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ) +{ + Vector delta = pt - origin; + float dist = VectorNormalize( delta ); + float dot = DotProduct( delta, axis ); + if ( dot < cosAngle ) + return false; + if ( dist * dot > length ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between two boxes +//----------------------------------------------------------------------------- +bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, + const Vector& boxMin2, const Vector& boxMax2 ) +{ + Assert( boxMin1[0] <= boxMax1[0] ); + Assert( boxMin1[1] <= boxMax1[1] ); + Assert( boxMin1[2] <= boxMax1[2] ); + Assert( boxMin2[0] <= boxMax2[0] ); + Assert( boxMin2[1] <= boxMax2[1] ); + Assert( boxMin2[2] <= boxMax2[2] ); + + if ( (boxMin1[0] > boxMax2[0]) || (boxMax1[0] < boxMin2[0]) ) + return false; + if ( (boxMin1[1] > boxMax2[1]) || (boxMax1[1] < boxMin2[1]) ) + return false; + if ( (boxMin1[2] > boxMax2[2]) || (boxMax1[2] < boxMin2[2]) ) + return false; + return true; +} + +bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, + const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ) +{ + Vector vecDelta, vecSize; + VectorSubtract( boxCenter1, boxCenter2, vecDelta ); + VectorAdd( boxHalfDiagonal1, boxHalfDiagonal2, vecSize ); + return ( FloatMakePositive( vecDelta.x ) <= vecSize.x ) && + ( FloatMakePositive( vecDelta.y ) <= vecSize.y ) && + ( FloatMakePositive( vecDelta.z ) <= vecSize.z ); +} + + +//----------------------------------------------------------------------------- +// +// IsOBBIntersectingOBB +// +// returns true if there's an intersection between two OBBs +// +//----------------------------------------------------------------------------- +bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, + const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance ) +{ + // FIXME: Simple case AABB check doesn't work because the min and max extents are not oriented based on the angle + // this fast check would only be good for cubes. + /*if ( vecAngles1 == vecAngles2 ) + { + const Vector &vecDelta = vecOrigin2 - vecOrigin1; + Vector vecOtherMins, vecOtherMaxs; + VectorAdd( boxMin2, vecDelta, vecOtherMins ); + VectorAdd( boxMax2, vecDelta, vecOtherMaxs ); + return IsBoxIntersectingBox( boxMin1, boxMax1, vecOtherMins, vecOtherMaxs ); + }*/ + + // OBB test... + cplane_t plane; + bool bFoundPlane = ComputeSeparatingPlane( vecOrigin1, vecAngles1, boxMin1, boxMax1, + vecOrigin2, vecAngles2, boxMin2, boxMax2, flTolerance, &plane ); + return (bFoundPlane == false); +} + +// NOTE: This is only very slightly faster on high end PCs and x360 +#define USE_SIMD_RAY_CHECKS 1 +//----------------------------------------------------------------------------- +// returns true if there's an intersection between box and ray +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& vecDelta, float flTolerance ) +{ + +#if USE_SIMD_RAY_CHECKS + // Load the unaligned ray/box parameters into SIMD registers + fltx4 start = LoadUnaligned3SIMD(origin.Base()); + fltx4 delta = LoadUnaligned3SIMD(vecDelta.Base()); + fltx4 boxMins = LoadUnaligned3SIMD( boxMin.Base() ); + fltx4 boxMaxs = LoadUnaligned3SIMD( boxMax.Base() ); + fltx4 epsilon = ReplicateX4(flTolerance); + // compute the mins/maxs of the box expanded by the ray extents + // relocate the problem so that the ray start is at the origin. + fltx4 offsetMins = SubSIMD(boxMins, start); + fltx4 offsetMaxs = SubSIMD(boxMaxs, start); + fltx4 offsetMinsExpanded = SubSIMD(offsetMins, epsilon); + fltx4 offsetMaxsExpanded = AddSIMD(offsetMaxs, epsilon); + + // Check to see if both the origin (start point) and the end point (delta) are on the front side + // of any of the box sides - if so there can be no intersection + fltx4 startOutMins = CmpLtSIMD(Four_Zeros, offsetMinsExpanded); + fltx4 endOutMins = CmpLtSIMD(delta,offsetMinsExpanded); + fltx4 minsMask = AndSIMD( startOutMins, endOutMins ); + fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, offsetMaxsExpanded); + fltx4 endOutMaxs = CmpGtSIMD(delta,offsetMaxsExpanded); + fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs ); + if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask)))) + return false; + + // now build the per-axis interval of t for intersections + fltx4 invDelta = ReciprocalSaturateSIMD(delta); + fltx4 tmins = MulSIMD( offsetMinsExpanded, invDelta ); + fltx4 tmaxs = MulSIMD( offsetMaxsExpanded, invDelta ); + fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs)); + + // only consider axes where we crossed a plane + tmins = MaskedAssign( crossPlane, tmins, Four_Negative_FLT_MAX ); + tmaxs = MaskedAssign( crossPlane, tmaxs, Four_FLT_MAX ); + + // now sort the interval per axis + fltx4 mint = MinSIMD( tmins, tmaxs ); + fltx4 maxt = MaxSIMD( tmins, tmaxs ); + + // now find the intersection of the intervals on all axes + fltx4 firstOut = FindLowestSIMD3(maxt); + fltx4 lastIn = FindHighestSIMD3(mint); + // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut] + firstOut = MinSIMD(firstOut, Four_Ones); + lastIn = MaxSIMD(lastIn, Four_Zeros); + + // If the final interval is valid lastIn boxMax[i] + flTolerance) ) + return false; + + continue; + } + + // non-parallel case + // Find the t's corresponding to the entry and exit of + // the ray along x, y, and z. The find the furthest entry + // point, and the closest exit point. Once that is done, + // we know we don't collide if the closest exit point + // is behind the starting location. We also don't collide if + // the closest exit point is in front of the furthest entry point + + float invDelta = 1.0f / vecDelta[i]; + float t1 = (boxMin[i] - flTolerance - origin[i]) * invDelta; + float t2 = (boxMax[i] + flTolerance - origin[i]) * invDelta; + if (t1 > t2) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + if (t1 > tmin) + tmin = t1; + if (t2 < tmax) + tmax = t2; + if (tmin > tmax) + return false; + if (tmax < 0) + return false; + if (tmin > 1) + return false; + } + + return true; +#endif +} + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between box and ray +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& vecDelta, + const Vector& vecInvDelta, float flTolerance ) +{ +#if USE_SIMD_RAY_CHECKS + // Load the unaligned ray/box parameters into SIMD registers + fltx4 start = LoadUnaligned3SIMD(origin.Base()); + fltx4 delta = LoadUnaligned3SIMD(vecDelta.Base()); + fltx4 boxMins = LoadUnaligned3SIMD( boxMin.Base() ); + fltx4 boxMaxs = LoadUnaligned3SIMD( boxMax.Base() ); + // compute the mins/maxs of the box expanded by the ray extents + // relocate the problem so that the ray start is at the origin. + boxMins = SubSIMD(boxMins, start); + boxMaxs = SubSIMD(boxMaxs, start); + + // Check to see if both the origin (start point) and the end point (delta) are on the front side + // of any of the box sides - if so there can be no intersection + fltx4 startOutMins = CmpLtSIMD(Four_Zeros, boxMins); + fltx4 endOutMins = CmpLtSIMD(delta,boxMins); + fltx4 minsMask = AndSIMD( startOutMins, endOutMins ); + fltx4 startOutMaxs = CmpGtSIMD(Four_Zeros, boxMaxs); + fltx4 endOutMaxs = CmpGtSIMD(delta,boxMaxs); + fltx4 maxsMask = AndSIMD( startOutMaxs, endOutMaxs ); + if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(minsMask,maxsMask)))) + return false; + + // now build the per-axis interval of t for intersections + fltx4 epsilon = ReplicateX4(flTolerance); + fltx4 invDelta = LoadUnaligned3SIMD(vecInvDelta.Base()); + boxMins = SubSIMD(boxMins, epsilon); + boxMaxs = AddSIMD(boxMaxs, epsilon); + + boxMins = MulSIMD( boxMins, invDelta ); + boxMaxs = MulSIMD( boxMaxs, invDelta ); + + fltx4 crossPlane = OrSIMD(XorSIMD(startOutMins,endOutMins), XorSIMD(startOutMaxs,endOutMaxs)); + // only consider axes where we crossed a plane + boxMins = MaskedAssign( crossPlane, boxMins, Four_Negative_FLT_MAX ); + boxMaxs = MaskedAssign( crossPlane, boxMaxs, Four_FLT_MAX ); + + // now sort the interval per axis + fltx4 mint = MinSIMD( boxMins, boxMaxs ); + fltx4 maxt = MaxSIMD( boxMins, boxMaxs ); + + // now find the intersection of the intervals on all axes + fltx4 firstOut = FindLowestSIMD3(maxt); + fltx4 lastIn = FindHighestSIMD3(mint); + // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut] + firstOut = MinSIMD(firstOut, Four_Ones); + lastIn = MaxSIMD(lastIn, Four_Zeros); + + // If the final interval is valid lastIn boxMax[i] + flTolerance ) ) + return false; + + continue; + } + + // Non-parallel case + // Find the t's corresponding to the entry and exit of + // the ray along x, y, and z. The find the furthest entry + // point, and the closest exit point. Once that is done, + // we know we don't collide if the closest exit point + // is behind the starting location. We also don't collide if + // the closest exit point is in front of the furthest entry point + float t1 = ( boxMin[i] - flTolerance - origin[i] ) * vecInvDelta[i]; + float t2 = ( boxMax[i] + flTolerance - origin[i] ) * vecInvDelta[i]; + if ( t1 > t2 ) + { + float temp = t1; + t1 = t2; + t2 = temp; + } + + if (t1 > tmin) + tmin = t1; + + if (t2 < tmax) + tmax = t2; + + if (tmin > tmax) + return false; + + if (tmax < 0) + return false; + + if (tmin > 1) + return false; + } + + return true; +#endif +} + +//----------------------------------------------------------------------------- +// Intersects a ray with a aabb, return true if they intersect +//----------------------------------------------------------------------------- +bool FASTCALL IsBoxIntersectingRay( const Vector& vecBoxMin, const Vector& vecBoxMax, const Ray_t& ray, float flTolerance ) +{ + // On the x360, we force use of the SIMD functions. +#if defined(_X360) + if (IsX360()) + { + return IsBoxIntersectingRay( + LoadUnaligned3SIMD(vecBoxMin.Base()), LoadUnaligned3SIMD(vecBoxMax.Base()), + ray, flTolerance); + } +#endif + + if ( !ray.m_IsSwept ) + { + Vector rayMins, rayMaxs; + VectorSubtract( ray.m_Start, ray.m_Extents, rayMins ); + VectorAdd( ray.m_Start, ray.m_Extents, rayMaxs ); + if ( flTolerance != 0.0f ) + { + rayMins.x -= flTolerance; rayMins.y -= flTolerance; rayMins.z -= flTolerance; + rayMaxs.x += flTolerance; rayMaxs.y += flTolerance; rayMaxs.z += flTolerance; + } + return IsBoxIntersectingBox( vecBoxMin, vecBoxMax, rayMins, rayMaxs ); + } + + Vector vecExpandedBoxMin, vecExpandedBoxMax; + VectorSubtract( vecBoxMin, ray.m_Extents, vecExpandedBoxMin ); + VectorAdd( vecBoxMax, ray.m_Extents, vecExpandedBoxMax ); + return IsBoxIntersectingRay( vecExpandedBoxMin, vecExpandedBoxMax, ray.m_Start, ray.m_Delta, flTolerance ); +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between box and ray (SIMD version) +//----------------------------------------------------------------------------- + + +#ifdef _X360 +bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax, + fltx4 origin, fltx4 delta, fltx4 invDelta, // ray parameters + fltx4 vTolerance ///< eg from ReplicateX4(flTolerance) + ) +#else +bool FASTCALL IsBoxIntersectingRay( const fltx4 &inBoxMin, const fltx4 & inBoxMax, + const fltx4 & origin, const fltx4 & delta, const fltx4 & invDelta, // ray parameters + const fltx4 & vTolerance ///< eg from ReplicateX4(flTolerance) + ) +#endif +{ + // Load the unaligned ray/box parameters into SIMD registers + // compute the mins/maxs of the box expanded by the ray extents + // relocate the problem so that the ray start is at the origin. + +#ifdef _X360 + boxMin = SubSIMD(boxMin, origin); + boxMax = SubSIMD(boxMax, origin); +#else + fltx4 boxMin = SubSIMD(inBoxMin, origin); + fltx4 boxMax = SubSIMD(inBoxMax, origin); +#endif + + // Check to see if the origin (start point) and the end point (delta) are on the same side + // of any of the box sides - if so there can be no intersection + fltx4 startOutMins = AndSIMD( CmpLtSIMD(Four_Zeros, boxMin), CmpLtSIMD(delta,boxMin) ); + fltx4 startOutMaxs = AndSIMD( CmpGtSIMD(Four_Zeros, boxMax), CmpGtSIMD(delta,boxMax) ); + if ( IsAnyNegative(SetWToZeroSIMD(OrSIMD(startOutMaxs,startOutMins)))) + return false; + + // now build the per-axis interval of t for intersections + boxMin = SubSIMD(boxMin, vTolerance); + boxMax = AddSIMD(boxMax, vTolerance); + + boxMin = MulSIMD( boxMin, invDelta ); + boxMax = MulSIMD( boxMax, invDelta ); + + // now sort the interval per axis + fltx4 mint = MinSIMD( boxMin, boxMax ); + fltx4 maxt = MaxSIMD( boxMin, boxMax ); + + // now find the intersection of the intervals on all axes + fltx4 firstOut = FindLowestSIMD3(maxt); + fltx4 lastIn = FindHighestSIMD3(mint); + // NOTE: This is really a scalar quantity now [t0,t1] == [lastIn,firstOut] + firstOut = MinSIMD(firstOut, Four_Ones); + lastIn = MaxSIMD(lastIn, Four_Zeros); + + // If the final interval is valid lastInt1 = -1.0f; + pTrace->t2 = 1.0f; + pTrace->hitside = -1; + + // UNDONE: This makes this code a little messy + pTrace->startsolid = true; + + for ( i = 0; i < 6; ++i ) + { + if ( i >= 3 ) + { + d1 = vecRayStart[i-3] - boxMaxs[i-3]; + d2 = d1 + vecRayDelta[i-3]; + } + else + { + d1 = -vecRayStart[i] + boxMins[i]; + d2 = d1 - vecRayDelta[i]; + } + + // if completely in front of face, no intersection + if (d1 > 0 && d2 > 0) + { + // UNDONE: Have to revert this in case it's still set + // UNDONE: Refactor to have only 2 return points (true/false) from this function + pTrace->startsolid = false; + return false; + } + + // completely inside, check next face + if (d1 <= 0 && d2 <= 0) + continue; + + if (d1 > 0) + { + pTrace->startsolid = false; + } + + // crosses face + if (d1 > d2) + { + f = d1 - flTolerance; + if ( f < 0 ) + { + f = 0; + } + f = f / (d1-d2); + if (f > pTrace->t1) + { + pTrace->t1 = f; + pTrace->hitside = i; + } + } + else + { + // leave + f = (d1 + flTolerance) / (d1-d2); + if (f < pTrace->t2) + { + pTrace->t2 = f; + } + } + } + + return pTrace->startsolid || (pTrace->t1 < pTrace->t2 && pTrace->t1 >= 0.0f); +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, + const Vector &boxMins, const Vector &boxMaxs, float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid ) +{ + Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); + + BoxTraceInfo_t trace; + + if ( IntersectRayWithBox( vecRayStart, vecRayDelta, boxMins, boxMaxs, flTolerance, &trace ) ) + { + pTrace->startsolid = trace.startsolid; + if (trace.t1 < trace.t2 && trace.t1 >= 0.0f) + { + pTrace->fraction = trace.t1; + VectorMA( pTrace->startpos, trace.t1, vecRayDelta, pTrace->endpos ); + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.normal = vec3_origin; + if ( trace.hitside >= 3 ) + { + trace.hitside -= 3; + pTrace->plane.dist = boxMaxs[trace.hitside]; + pTrace->plane.normal[trace.hitside] = 1.0f; + pTrace->plane.type = trace.hitside; + } + else + { + pTrace->plane.dist = -boxMins[trace.hitside]; + pTrace->plane.normal[trace.hitside] = -1.0f; + pTrace->plane.type = trace.hitside; + } + return true; + } + + if ( pTrace->startsolid ) + { + pTrace->allsolid = (trace.t2 <= 0.0f) || (trace.t2 >= 1.0f); + pTrace->fraction = 0; + if ( pFractionLeftSolid ) + { + *pFractionLeftSolid = trace.t2; + } + pTrace->endpos = pTrace->startpos; + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.dist = pTrace->startpos[0]; + pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); + pTrace->plane.type = 0; + pTrace->startpos = vecRayStart + (trace.t2 * vecRayDelta); + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, + float flTolerance, CBaseTrace *pTrace, float *pFractionLeftSolid ) +{ + if ( !ray.m_IsRay ) + { + Vector vecExpandedMins = boxMins - ray.m_Extents; + Vector vecExpandedMaxs = boxMaxs + ray.m_Extents; + bool bIntersects = IntersectRayWithBox( ray.m_Start, ray.m_Delta, vecExpandedMins, vecExpandedMaxs, flTolerance, pTrace, pFractionLeftSolid ); + pTrace->startpos += ray.m_StartOffset; + pTrace->endpos += ray.m_StartOffset; + return bIntersects; + } + return IntersectRayWithBox( ray.m_Start, ray.m_Delta, boxMins, boxMaxs, flTolerance, pTrace, pFractionLeftSolid ); +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB, returns t1 and t2 +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, BoxTraceInfo_t *pTrace ) +{ + // FIXME: Two transforms is pretty expensive. Should we optimize this? + Vector start, delta; + VectorITransform( vecRayStart, matOBBToWorld, start ); + VectorIRotate( vecRayDelta, matOBBToWorld, delta ); + + return IntersectRayWithBox( start, delta, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, CBaseTrace *pTrace ) +{ + Collision_ClearTrace( vecRayStart, vecRayDelta, pTrace ); + + // FIXME: Make it work with tolerance + Assert( flTolerance == 0.0f ); + + // OPTIMIZE: Store this in the box instead of computing it here + // compute center in local space + Vector vecBoxExtents = (vecOBBMins + vecOBBMaxs) * 0.5; + Vector vecBoxCenter; + + // transform to world space + VectorTransform( vecBoxExtents, matOBBToWorld, vecBoxCenter ); + + // calc extents from local center + vecBoxExtents = vecOBBMaxs - vecBoxExtents; + + // OPTIMIZE: This is optimized for world space. If the transform is fast enough, it may make more + // sense to just xform and call UTIL_ClipToBox() instead. MEASURE THIS. + + // save the extents of the ray along + Vector extent, uextent; + Vector segmentCenter = vecRayStart + vecRayDelta - vecBoxCenter; + + extent.Init(); + + // check box axes for separation + for ( int j = 0; j < 3; j++ ) + { + extent[j] = vecRayDelta.x * matOBBToWorld[0][j] + vecRayDelta.y * matOBBToWorld[1][j] + vecRayDelta.z * matOBBToWorld[2][j]; + uextent[j] = fabsf(extent[j]); + float coord = segmentCenter.x * matOBBToWorld[0][j] + segmentCenter.y * matOBBToWorld[1][j] + segmentCenter.z * matOBBToWorld[2][j]; + coord = fabsf(coord); + + if ( coord > (vecBoxExtents[j] + uextent[j]) ) + return false; + } + + // now check cross axes for separation + float tmp, cextent; + Vector cross = vecRayDelta.Cross( segmentCenter ); + cextent = cross.x * matOBBToWorld[0][0] + cross.y * matOBBToWorld[1][0] + cross.z * matOBBToWorld[2][0]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[1]*uextent[2] + vecBoxExtents[2]*uextent[1]; + if ( cextent > tmp ) + return false; + + cextent = cross.x * matOBBToWorld[0][1] + cross.y * matOBBToWorld[1][1] + cross.z * matOBBToWorld[2][1]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[0]*uextent[2] + vecBoxExtents[2]*uextent[0]; + if ( cextent > tmp ) + return false; + + cextent = cross.x * matOBBToWorld[0][2] + cross.y * matOBBToWorld[1][2] + cross.z * matOBBToWorld[2][2]; + cextent = fabsf(cextent); + tmp = vecBoxExtents[0]*uextent[1] + vecBoxExtents[1]*uextent[0]; + if ( cextent > tmp ) + return false; + + // !!! We hit this box !!! compute intersection point and return + // Compute ray start in bone space + Vector start; + VectorITransform( vecRayStart, matOBBToWorld, start ); + + // extent is ray.m_Delta in bone space, recompute delta in bone space + extent *= 2.0f; + + // delta was prescaled by the current t, so no need to see if this intersection + // is closer + trace_t boxTrace; + if ( !IntersectRayWithBox( start, extent, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ) ) + return false; + + // Fix up the start/end pos and fraction + Vector vecTemp; + VectorTransform( pTrace->endpos, matOBBToWorld, vecTemp ); + pTrace->endpos = vecTemp; + + pTrace->startpos = vecRayStart; + pTrace->fraction *= 2.0f; + + // Fix up the plane information + float flSign = pTrace->plane.normal[ pTrace->plane.type ]; + pTrace->plane.normal[0] = flSign * matOBBToWorld[0][pTrace->plane.type]; + pTrace->plane.normal[1] = flSign * matOBBToWorld[1][pTrace->plane.type]; + pTrace->plane.normal[2] = flSign * matOBBToWorld[2][pTrace->plane.type]; + pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal ); + pTrace->plane.type = 3; + + return true; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if (angBoxRotation == vec3_angle) + { + Vector vecAbsMins, vecAbsMaxs; + VectorAdd( vecBoxOrigin, vecOBBMins, vecAbsMins ); + VectorAdd( vecBoxOrigin, vecOBBMaxs, vecAbsMaxs ); + return IntersectRayWithBox( vecRayOrigin, vecRayDelta, vecAbsMins, vecAbsMaxs, flTolerance, pTrace ); + } + + matrix3x4_t obbToWorld; + AngleMatrix( angBoxRotation, vecBoxOrigin, obbToWorld ); + return IntersectRayWithOBB( vecRayOrigin, vecRayDelta, obbToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + +//----------------------------------------------------------------------------- +// Box support map +//----------------------------------------------------------------------------- +inline void ComputeSupportMap( const Vector &vecDirection, const Vector &vecBoxMins, + const Vector &vecBoxMaxs, float pDist[2] ) +{ + int nIndex = (vecDirection.x > 0.0f); + pDist[nIndex] = vecBoxMaxs.x * vecDirection.x; + pDist[1 - nIndex] = vecBoxMins.x * vecDirection.x; + + nIndex = (vecDirection.y > 0.0f); + pDist[nIndex] += vecBoxMaxs.y * vecDirection.y; + pDist[1 - nIndex] += vecBoxMins.y * vecDirection.y; + + nIndex = (vecDirection.z > 0.0f); + pDist[nIndex] += vecBoxMaxs.z * vecDirection.z; + pDist[1 - nIndex] += vecBoxMins.z * vecDirection.z; +} + +inline void ComputeSupportMap( const Vector &vecDirection, int i1, int i2, + const Vector &vecBoxMins, const Vector &vecBoxMaxs, float pDist[2] ) +{ + int nIndex = (vecDirection[i1] > 0.0f); + pDist[nIndex] = vecBoxMaxs[i1] * vecDirection[i1]; + pDist[1 - nIndex] = vecBoxMins[i1] * vecDirection[i1]; + + nIndex = (vecDirection[i2] > 0.0f); + pDist[nIndex] += vecBoxMaxs[i2] * vecDirection[i2]; + pDist[1 - nIndex] += vecBoxMins[i2] * vecDirection[i2]; +} + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +static int s_ExtIndices[3][2] = +{ + { 2, 1 }, + { 0, 2 }, + { 0, 1 }, +}; + +static int s_MatIndices[3][2] = +{ + { 1, 2 }, + { 2, 0 }, + { 1, 0 }, +}; + +bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if ( ray.m_IsRay ) + { + return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, matOBBToWorld, + vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); + } + + Collision_ClearTrace( ray.m_Start + ray.m_StartOffset, ray.m_Delta, pTrace ); + + // Compute a bounding sphere around the bloated OBB + Vector vecOBBCenter; + VectorAdd( vecOBBMins, vecOBBMaxs, vecOBBCenter ); + vecOBBCenter *= 0.5f; + vecOBBCenter.x += matOBBToWorld[0][3]; + vecOBBCenter.y += matOBBToWorld[1][3]; + vecOBBCenter.z += matOBBToWorld[2][3]; + + Vector vecOBBHalfDiagonal; + VectorSubtract( vecOBBMaxs, vecOBBMins, vecOBBHalfDiagonal ); + vecOBBHalfDiagonal *= 0.5f; + + float flRadius = vecOBBHalfDiagonal.Length() + ray.m_Extents.Length(); + if ( !IsRayIntersectingSphere( ray.m_Start, ray.m_Delta, vecOBBCenter, flRadius, flTolerance ) ) + return false; + + // Ok, we passed the trivial reject, so lets do the dirty deed. + // Basically we're going to do the GJK thing explicitly. We'll shrink the ray down + // to a point, and bloat the OBB by the ray's extents. This will generate facet + // planes which are perpendicular to all of the separating axes typically seen in + // a standard seperating axis implementation. + + // We're going to create a number of planes through various vertices in the OBB + // which represent all of the separating planes. Then we're going to bloat the planes + // by the ray extents. + + // We're going to do all work in OBB-space because it's easier to do the + // support-map in this case + + // First, transform the ray into the space of the OBB + Vector vecLocalRayOrigin, vecLocalRayDirection; + VectorITransform( ray.m_Start, matOBBToWorld, vecLocalRayOrigin ); + VectorIRotate( ray.m_Delta, matOBBToWorld, vecLocalRayDirection ); + + // Next compute all separating planes + Vector pPlaneNormal[15]; + float ppPlaneDist[15][2]; + + int i; + for ( i = 0; i < 3; ++i ) + { + // Each plane needs to be bloated an amount = to the abs dot product of + // the ray extents with the plane normal + // For the OBB planes, do it in world space; + // and use the direction of the OBB (the ith column of matOBBToWorld) in world space vs extents + pPlaneNormal[i].Init( ); + pPlaneNormal[i][i] = 1.0f; + + float flExtentDotNormal = + FloatMakePositive( matOBBToWorld[0][i] * ray.m_Extents.x ) + + FloatMakePositive( matOBBToWorld[1][i] * ray.m_Extents.y ) + + FloatMakePositive( matOBBToWorld[2][i] * ray.m_Extents.z ); + + ppPlaneDist[i][0] = vecOBBMins[i] - flExtentDotNormal; + ppPlaneDist[i][1] = vecOBBMaxs[i] + flExtentDotNormal; + + // For the ray-extents planes, they are bloated by the extents + // Use the support map to determine which + VectorCopy( matOBBToWorld[i], pPlaneNormal[i+3].Base() ); + ComputeSupportMap( pPlaneNormal[i+3], vecOBBMins, vecOBBMaxs, ppPlaneDist[i+3] ); + ppPlaneDist[i+3][0] -= ray.m_Extents[i]; + ppPlaneDist[i+3][1] += ray.m_Extents[i]; + + // Now the edge cases... (take the cross product of x,y,z axis w/ ray extent axes + // given by the rows of the obb to world matrix. + // Compute the ray extent bloat in world space because it's easier... + + // These are necessary to compute the world-space versions of + // the edges so we can compute the extent dot products + float flRayExtent0 = ray.m_Extents[s_ExtIndices[i][0]]; + float flRayExtent1 = ray.m_Extents[s_ExtIndices[i][1]]; + const float *pMatRow0 = matOBBToWorld[s_MatIndices[i][0]]; + const float *pMatRow1 = matOBBToWorld[s_MatIndices[i][1]]; + + // x axis of the OBB + world ith axis + pPlaneNormal[i+6].Init( 0.0f, -matOBBToWorld[i][2], matOBBToWorld[i][1] ); + ComputeSupportMap( pPlaneNormal[i+6], 1, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+6] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[0] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[0] ) * flRayExtent1; + ppPlaneDist[i+6][0] -= flExtentDotNormal; + ppPlaneDist[i+6][1] += flExtentDotNormal; + + // y axis of the OBB + world ith axis + pPlaneNormal[i+9].Init( matOBBToWorld[i][2], 0.0f, -matOBBToWorld[i][0] ); + ComputeSupportMap( pPlaneNormal[i+9], 0, 2, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+9] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[1] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[1] ) * flRayExtent1; + ppPlaneDist[i+9][0] -= flExtentDotNormal; + ppPlaneDist[i+9][1] += flExtentDotNormal; + + // z axis of the OBB + world ith axis + pPlaneNormal[i+12].Init( -matOBBToWorld[i][1], matOBBToWorld[i][0], 0.0f ); + ComputeSupportMap( pPlaneNormal[i+12], 0, 1, vecOBBMins, vecOBBMaxs, ppPlaneDist[i+12] ); + flExtentDotNormal = + FloatMakePositive( pMatRow0[2] ) * flRayExtent0 + + FloatMakePositive( pMatRow1[2] ) * flRayExtent1; + ppPlaneDist[i+12][0] -= flExtentDotNormal; + ppPlaneDist[i+12][1] += flExtentDotNormal; + } + + float enterfrac, leavefrac; + float d1[2], d2[2]; + float f; + + int hitplane = -1; + int hitside = -1; + enterfrac = -1.0f; + leavefrac = 1.0f; + + pTrace->startsolid = true; + + Vector vecLocalRayEnd; + VectorAdd( vecLocalRayOrigin, vecLocalRayDirection, vecLocalRayEnd ); + + for ( i = 0; i < 15; ++i ) + { + // FIXME: Not particularly optimal since there's a lot of 0's in the plane normals + float flStartDot = DotProduct( pPlaneNormal[i], vecLocalRayOrigin ); + float flEndDot = DotProduct( pPlaneNormal[i], vecLocalRayEnd ); + + // NOTE: Negative here is because the plane normal + dist + // are defined in negative terms for the far plane (plane dist index 0) + d1[0] = -(flStartDot - ppPlaneDist[i][0]); + d2[0] = -(flEndDot - ppPlaneDist[i][0]); + + d1[1] = flStartDot - ppPlaneDist[i][1]; + d2[1] = flEndDot - ppPlaneDist[i][1]; + + int j; + for ( j = 0; j < 2; ++j ) + { + // if completely in front near plane or behind far plane no intersection + if (d1[j] > 0 && d2[j] > 0) + return false; + + // completely inside, check next plane set + if (d1[j] <= 0 && d2[j] <= 0) + continue; + + if (d1[j] > 0) + { + pTrace->startsolid = false; + } + + // crosses face + float flDenom = 1.0f / (d1[j] - d2[j]); + if (d1[j] > d2[j]) + { + f = d1[j] - flTolerance; + if ( f < 0 ) + { + f = 0; + } + f *= flDenom; + if (f > enterfrac) + { + enterfrac = f; + hitplane = i; + hitside = j; + } + } + else + { + // leave + f = (d1[j] + flTolerance) * flDenom; + if (f < leavefrac) + { + leavefrac = f; + } + } + } + } + + if (enterfrac < leavefrac && enterfrac >= 0.0f) + { + pTrace->fraction = enterfrac; + VectorMA( pTrace->startpos, enterfrac, ray.m_Delta, pTrace->endpos ); + pTrace->contents = CONTENTS_SOLID; + + // Need to transform the plane into world space... + cplane_t temp; + temp.normal = pPlaneNormal[hitplane]; + temp.dist = ppPlaneDist[hitplane][hitside]; + if (hitside == 0) + { + temp.normal *= -1.0f; + temp.dist *= -1.0f; + } + temp.type = 3; + + MatrixITransformPlane( matOBBToWorld, temp, pTrace->plane ); + return true; + } + + if ( pTrace->startsolid ) + { + pTrace->allsolid = (leavefrac <= 0.0f) || (leavefrac >= 1.0f); + pTrace->fraction = 0; + pTrace->endpos = pTrace->startpos; + pTrace->contents = CONTENTS_SOLID; + pTrace->plane.dist = pTrace->startpos[0]; + pTrace->plane.normal.Init( 1.0f, 0.0f, 0.0f ); + pTrace->plane.type = 0; + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Intersects a ray against an OBB +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ) +{ + if ( angBoxRotation == vec3_angle ) + { + Vector vecWorldMins, vecWorldMaxs; + VectorAdd( vecBoxOrigin, vecOBBMins, vecWorldMins ); + VectorAdd( vecBoxOrigin, vecOBBMaxs, vecWorldMaxs ); + return IntersectRayWithBox( ray, vecWorldMins, vecWorldMaxs, flTolerance, pTrace ); + } + + if ( ray.m_IsRay ) + { + return IntersectRayWithOBB( ray.m_Start, ray.m_Delta, vecBoxOrigin, angBoxRotation, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); + } + + matrix3x4_t matOBBToWorld; + AngleMatrix( angBoxRotation, vecBoxOrigin, matOBBToWorld ); + return IntersectRayWithOBB( ray, matOBBToWorld, vecOBBMins, vecOBBMaxs, flTolerance, pTrace ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void GetNonMajorAxes( const Vector &vNormal, Vector2D &axes ) +{ + axes[0] = 0; + axes[1] = 1; + + if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.y ) ) + { + if( FloatMakePositive( vNormal.x ) > FloatMakePositive( vNormal.z ) ) + { + axes[0] = 1; + axes[1] = 2; + } + } + else + { + if( FloatMakePositive( vNormal.y ) > FloatMakePositive( vNormal.z ) ) + { + axes[0] = 0; + axes[1] = 2; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +QuadBarycentricRetval_t QuadWithParallelEdges( const Vector &vecOrigin, + const Vector &vecU, float lengthU, const Vector &vecV, float lengthV, + const Vector &pt, Vector2D &vecUV ) +{ + Ray_t rayAxis; + Ray_t rayPt; + + // + // handle the u axis + // + rayAxis.m_Start = vecOrigin; + rayAxis.m_Delta = vecU; + rayAxis.m_IsRay = true; + + rayPt.m_Start = pt; + rayPt.m_Delta = vecV * -( lengthV * 10.0f ); + rayPt.m_IsRay = true; + + float s, t; + IntersectRayWithRay( rayAxis, rayPt, t, s ); + vecUV[0] = t / lengthU; + + // + // handle the v axis + // + rayAxis.m_Delta = vecV; + + rayPt.m_Delta = vecU * -( lengthU * 10.0f ); + + IntersectRayWithRay( rayAxis, rayPt, t, s ); + vecUV[1] = t / lengthV; + + // inside of the quad?? + if( ( vecUV[0] < 0.0f ) || ( vecUV[0] > 1.0f ) || + ( vecUV[1] < 0.0f ) || ( vecUV[1] > 1.0f ) ) + return BARY_QUADRATIC_FALSE; + + return BARY_QUADRATIC_TRUE; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void ResolveQuadratic( double tPlus, double tMinus, + const Vector axisU0, const Vector axisU1, + const Vector axisV0, const Vector axisV1, + const Vector axisOrigin, const Vector pt, + int projU, double &s, double &t ) +{ + // calculate the sPlus, sMinus pair(s) + double sDenomPlus = ( axisU0[projU] * ( 1 - tPlus ) ) + ( axisU1[projU] * tPlus ); + double sDenomMinus = ( axisU0[projU] * ( 1 - tMinus ) ) + ( axisU1[projU] * tMinus ); + + double sPlus = UNINIT, sMinus = UNINIT; + if( FloatMakePositive( sDenomPlus ) >= 1e-5 ) + { + sPlus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tPlus ) ) / sDenomPlus; + } + + if( FloatMakePositive( sDenomMinus ) >= 1e-5 ) + { + sMinus = ( pt[projU] - axisOrigin[projU] - ( axisV0[projU] * tMinus ) ) / sDenomMinus; + } + + if( ( tPlus >= 0.0 ) && ( tPlus <= 1.0 ) && ( sPlus >= 0.0 ) && ( sPlus <= 1.0 ) ) + { + s = sPlus; + t = tPlus; + return; + } + + if( ( tMinus >= 0.0 ) && ( tMinus <= 1.0 ) && ( sMinus >= 0.0 ) && ( sMinus <= 1.0 ) ) + { + s = sMinus; + t = tMinus; + return; + } + + double s0, t0, s1, t1; + + s0 = sPlus; + t0 = tPlus; + if( s0 >= 1.0 ) { s0 -= 1.0; } + if( t0 >= 1.0 ) { t0 -= 1.0; } + + s1 = sMinus; + t1 = tMinus; + if( s1 >= 1.0 ) { s1 -= 1.0; } + if( t1 >= 1.0 ) { t1 -= 1.0; } + + s0 = FloatMakePositive( s0 ); + t0 = FloatMakePositive( t0 ); + s1 = FloatMakePositive( s1 ); + t1 = FloatMakePositive( t1 ); + + double max0, max1; + max0 = s0; + if( t0 > max0 ) { max0 = t0; } + max1 = s1; + if( t1 > max1 ) { max1 = t1; } + + if( max0 > max1 ) + { + s = sMinus; + t = tMinus; + } + else + { + s = sPlus; + t = tPlus; + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, + const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ) +{ +#define PIQ_TEXTURE_EPSILON 0.001 +#define PIQ_PLANE_EPSILON 0.1 +#define PIQ_DOT_EPSILON 0.99f + + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // u0 = v2 - v1 + // u1 = v3 - v4 + // v0 = v4 - v1 + // v1 = v3 - v2 + // + Vector axisU[2], axisV[2]; + Vector axisUNorm[2], axisVNorm[2]; + axisU[0] = axisUNorm[0] = v2 - v1; + axisU[1] = axisUNorm[1] = v3 - v4; + axisV[0] = axisVNorm[0] = v4 - v1; + axisV[1] = axisVNorm[1] = v3 - v2; + + float lengthU[2], lengthV[2]; + lengthU[0] = VectorNormalize( axisUNorm[0] ); + lengthU[1] = VectorNormalize( axisUNorm[1] ); + lengthV[0] = VectorNormalize( axisVNorm[0] ); + lengthV[1] = VectorNormalize( axisVNorm[1] ); + + // + // check for an early out - parallel opposite edges! + // NOTE: quad property if 1 set of opposite edges is parallel and equal + // in length, then the other set of edges is as well + // + if( axisUNorm[0].Dot( axisUNorm[1] ) > PIQ_DOT_EPSILON ) + { + if( FloatMakePositive( lengthU[0] - lengthU[1] ) < PIQ_PLANE_EPSILON ) + { + return QuadWithParallelEdges( v1, axisUNorm[0], lengthU[0], axisVNorm[0], lengthV[0], point, uv ); + } + } + + // + // since we are solving for s in our equations below we need to ensure that + // the v axes are non-parallel + // + bool bFlipped = false; + if( axisVNorm[0].Dot( axisVNorm[1] ) > PIQ_DOT_EPSILON ) + { + Vector tmp[2]; + tmp[0] = axisV[0]; + tmp[1] = axisV[1]; + axisV[0] = axisU[0]; + axisV[1] = axisU[1]; + axisU[0] = tmp[0]; + axisU[1] = tmp[1]; + bFlipped = true; + } + + // + // get the "projection" axes + // + Vector2D projAxes; + Vector vNormal = axisU[0].Cross( axisV[0] ); + GetNonMajorAxes( vNormal, projAxes ); + + // + // NOTE: axisU[0][projAxes[0]] < axisU[0][projAxes[1]], + // this is done to decrease error when dividing later + // + if( FloatMakePositive( axisU[0][projAxes[0]] ) < FloatMakePositive( axisU[0][projAxes[1]] ) ) + { + int tmp = projAxes[0]; + projAxes[0] = projAxes[1]; + projAxes[1] = tmp; + } + + // Here's how we got these equations: + // + // Given the points and u,v line segments above... + // + // Then: + // + // (1.0) PT = P0 + U0 * s + V * t + // + // where + // + // (1.1) V = V0 + s * (V1 - V0) + // (1.2) U = U0 + t * (U1 - U0) + // + // Therefore (from 1.1 + 1.0): + // PT - P0 = U0 * s + (V0 + s * (V1-V0)) * t + // Group s's: + // PT - P0 - t * V0 = s * (U0 + t * (V1-V0)) + // Two equations and two unknowns in x and y get you the following quadratic: + // + // solve the quadratic + // + double s = 0.0, t = 0.0; + double A, negB, C; + + A = ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) - + ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ) - + ( axisU[1][projAxes[1]] * axisV[0][projAxes[0]] ) + + ( axisU[1][projAxes[0]] * axisV[0][projAxes[1]] ); + C = ( v1[projAxes[1]] * axisU[0][projAxes[0]] ) - + ( point[projAxes[1]] * axisU[0][projAxes[0]] ) - + ( v1[projAxes[0]] * axisU[0][projAxes[1]] ) + + ( point[projAxes[0]] * axisU[0][projAxes[1]] ); + negB = C - + ( v1[projAxes[1]] * axisU[1][projAxes[0]] ) + + ( point[projAxes[1]] * axisU[1][projAxes[0]] ) + + ( v1[projAxes[0]] * axisU[1][projAxes[1]] ) - + ( point[projAxes[0]] * axisU[1][projAxes[1]] ) + + ( axisU[0][projAxes[1]] * axisV[0][projAxes[0]] ) - + ( axisU[0][projAxes[0]] * axisV[0][projAxes[1]] ); + + if( ( A > -PIQ_PLANE_EPSILON ) && ( A < PIQ_PLANE_EPSILON ) ) + { + // shouldn't be here -- this should have been take care of in the "early out" +// Assert( 0 ); + + Vector vecUAvg, vecVAvg; + vecUAvg = ( axisUNorm[0] + axisUNorm[1] ) * 0.5f; + vecVAvg = ( axisVNorm[0] + axisVNorm[1] ) * 0.5f; + + float fLengthUAvg = ( lengthU[0] + lengthU[1] ) * 0.5f; + float fLengthVAvg = ( lengthV[0] + lengthV[1] ) * 0.5f; + + return QuadWithParallelEdges( v1, vecUAvg, fLengthUAvg, vecVAvg, fLengthVAvg, point, uv ); + +#if 0 + // legacy code -- kept here for completeness! + + // not a quadratic -- solve linearly + t = C / negB; + + // See (1.2) above + float ui = axisU[0][projAxes[0]] + t * ( axisU[1][projAxes[0]] - axisU[0][projAxes[0]] ); + if( FloatMakePositive( ui ) >= 1e-5 ) + { + // See (1.0) above + s = ( point[projAxes[0]] - v1[projAxes[0]] - axisV[0][projAxes[0]] * t ) / ui; + } +#endif + } + else + { + // (-b +/- sqrt( b^2 - 4ac )) / 2a + double discriminant = (negB*negB) - (4.0f * A * C); + if( discriminant < 0.0f ) + { + uv[0] = -99999.0f; + uv[1] = -99999.0f; + return BARY_QUADRATIC_NEGATIVE_DISCRIMINANT; + } + + double quad = sqrt( discriminant ); + double QPlus = ( negB + quad ) / ( 2.0f * A ); + double QMinus = ( negB - quad ) / ( 2.0f * A ); + + ResolveQuadratic( QPlus, QMinus, axisU[0], axisU[1], axisV[0], axisV[1], v1, point, projAxes[0], s, t ); + } + + if( !bFlipped ) + { + uv[0] = ( float )s; + uv[1] = ( float )t; + } + else + { + uv[0] = ( float )t; + uv[1] = ( float )s; + } + + // inside of the quad?? + if( ( uv[0] < 0.0f ) || ( uv[0] > 1.0f ) || ( uv[1] < 0.0f ) || ( uv[1] > 1.0f ) ) + return BARY_QUADRATIC_FALSE; + + return BARY_QUADRATIC_TRUE; + +#undef PIQ_TEXTURE_EPSILON +#undef PIQ_PLANE_EPSILON +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, + const Vector2D &uv, Vector &point ) +{ + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // find the ray from v0 edge to v1 edge at v + // + Vector vPts[2]; + VectorLerp( v1, v4, uv[1], vPts[0] ); + VectorLerp( v2, v3, uv[1], vPts[1] ); + VectorLerp( vPts[0], vPts[1], uv[0], point ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, + const Vector2D &uv, Vector2D &texCoord ) +{ + // + // Think of a quad with points v1, v2, v3, v4 and u, v line segments + // find the ray from v0 edge to v1 edge at v + // + Vector2D vCoords[2]; + Vector2DLerp( v1, v4, uv[1], vCoords[0] ); + Vector2DLerp( v2, v3, uv[1], vCoords[1] ); + Vector2DLerp( vCoords[0], vCoords[1], uv[0], texCoord ); +} + + +//----------------------------------------------------------------------------- +// Compute point from barycentric specification +// Edge u goes from v0 to v1, edge v goes from v0 to v2 +//----------------------------------------------------------------------------- +void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, + float u, float v, Vector& pt ) +{ + Vector edgeU, edgeV; + VectorSubtract( v1, v0, edgeU ); + VectorSubtract( v2, v0, edgeV ); + VectorMA( v0, u, edgeU, pt ); + VectorMA( pt, v, edgeV, pt ); +} + +void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, + float u, float v, Vector2D& pt ) +{ + Vector2D edgeU, edgeV; + Vector2DSubtract( v1, v0, edgeU ); + Vector2DSubtract( v2, v0, edgeV ); + Vector2DMA( v0, u, edgeU, pt ); + Vector2DMA( pt, v, edgeV, pt ); +} + + +//----------------------------------------------------------------------------- +// Compute a matrix that has the correct orientation but which has an origin at +// the center of the bounds +//----------------------------------------------------------------------------- +static void ComputeCenterMatrix( const Vector& origin, const QAngle& angles, + const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) +{ + Vector centroid; + VectorAdd( mins, maxs, centroid ); + centroid *= 0.5f; + AngleMatrix( angles, matrix ); + + Vector worldCentroid; + VectorRotate( centroid, matrix, worldCentroid ); + worldCentroid += origin; + MatrixSetColumn( worldCentroid, 3, matrix ); +} + +static void ComputeCenterIMatrix( const Vector& origin, const QAngle& angles, + const Vector& mins, const Vector& maxs, matrix3x4_t& matrix ) +{ + Vector centroid; + VectorAdd( mins, maxs, centroid ); + centroid *= -0.5f; + AngleIMatrix( angles, matrix ); + + // For the translational component here, note that the origin in world space + // is T = R * C + O, (R = rotation matrix, C = centroid in local space, O = origin in world space) + // The IMatrix translation = - transpose(R) * T = -C - transpose(R) * 0 + Vector localOrigin; + VectorRotate( origin, matrix, localOrigin ); + centroid -= localOrigin; + MatrixSetColumn( centroid, 3, matrix ); +} + + +//----------------------------------------------------------------------------- +// Compute a matrix which is the absolute value of another +//----------------------------------------------------------------------------- +static inline void ComputeAbsMatrix( const matrix3x4_t& in, matrix3x4_t& out ) +{ + FloatBits(out[0][0]) = FloatAbsBits(in[0][0]); + FloatBits(out[0][1]) = FloatAbsBits(in[0][1]); + FloatBits(out[0][2]) = FloatAbsBits(in[0][2]); + FloatBits(out[1][0]) = FloatAbsBits(in[1][0]); + FloatBits(out[1][1]) = FloatAbsBits(in[1][1]); + FloatBits(out[1][2]) = FloatAbsBits(in[1][2]); + FloatBits(out[2][0]) = FloatAbsBits(in[2][0]); + FloatBits(out[2][1]) = FloatAbsBits(in[2][1]); + FloatBits(out[2][2]) = FloatAbsBits(in[2][2]); +} + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +static bool ComputeSeparatingPlane( const matrix3x4_t &worldToBox1, const matrix3x4_t &box2ToWorld, + const Vector& box1Size, const Vector& box2Size, float tolerance, cplane_t* pPlane ) +{ + // The various separating planes can be either + // 1) A plane parallel to one of the box face planes + // 2) A plane parallel to the cross-product of an edge from each box + + // First, compute the basis of second box in the space of the first box + // NOTE: These basis place the origin at the centroid of each box! + matrix3x4_t box2ToBox1; + ConcatTransforms( worldToBox1, box2ToWorld, box2ToBox1 ); + + // We're going to be using the origin of box2 in the space of box1 alot, + // lets extract it from the matrix.... + Vector box2Origin; + MatrixGetColumn( box2ToBox1, 3, box2Origin ); + + // Next get the absolute values of these entries and store in absbox2ToBox1. + matrix3x4_t absBox2ToBox1; + ComputeAbsMatrix( box2ToBox1, absBox2ToBox1 ); + + // There are 15 tests to make. The first 3 involve trying planes parallel + // to the faces of the first box. + + // NOTE: The algorithm here involves finding the projections of the two boxes + // onto a particular line. If the projections on the line do not overlap, + // that means that there's a plane perpendicular to the line which separates + // the two boxes; and we've therefore found a separating plane. + + // The way we check for overlay is we find the projections of the two boxes + // onto the line, and add them up. We compare the sum with the projection + // of the relative center of box2 onto the same line. + + Vector tmp; + float boxProjectionSum; + float originProjection; + + // NOTE: For these guys, we're taking advantage of the fact that the ith + // row of the box2ToBox1 is the direction of the box1 (x,y,z)-axis + // transformed into the space of box2. + + // First side of box 1 + boxProjectionSum = box1Size.x + MatrixRowDotProduct( absBox2ToBox1, 0, box2Size ); + originProjection = FloatMakePositive( box2Origin.x ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[0], pPlane->normal.Base() ); + return true; + } + + // Second side of box 1 + boxProjectionSum = box1Size.y + MatrixRowDotProduct( absBox2ToBox1, 1, box2Size ); + originProjection = FloatMakePositive( box2Origin.y ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[1], pPlane->normal.Base() ); + return true; + } + + // Third side of box 1 + boxProjectionSum = box1Size.z + MatrixRowDotProduct( absBox2ToBox1, 2, box2Size ); + originProjection = FloatMakePositive( box2Origin.z ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + VectorCopy( worldToBox1[2], pPlane->normal.Base() ); + return true; + } + + // The next three involve checking splitting planes parallel to the + // faces of the second box. + + // NOTE: For these guys, we're taking advantage of the fact that the 0th + // column of the box2ToBox1 is the direction of the box2 x-axis + // transformed into the space of box1. + // Here, we're determining the distance of box2's center from box1's center + // by projecting it onto a line parallel to box2's axis + + // First side of box 2 + boxProjectionSum = box2Size.x + MatrixColumnDotProduct( absBox2ToBox1, 0, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 0, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, pPlane->normal ); + return true; + } + + // Second side of box 2 + boxProjectionSum = box2Size.y + MatrixColumnDotProduct( absBox2ToBox1, 1, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 1, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, pPlane->normal ); + return true; + } + + // Third side of box 2 + boxProjectionSum = box2Size.z + MatrixColumnDotProduct( absBox2ToBox1, 2, box1Size ); + originProjection = FloatMakePositive( MatrixColumnDotProduct( box2ToBox1, 2, box2Origin ) ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, pPlane->normal ); + return true; + } + + // Next check the splitting planes which are orthogonal to the pairs + // of edges, one from box1 and one from box2. As only direction matters, + // there are 9 pairs since each box has 3 distinct edge directions. + + // Here, we take advantage of the fact that the edges from box 1 are all + // axis aligned; therefore the crossproducts are simplified. Let's walk through + // the example of b1e1 x b2e1: + + // In this example, the line to check is perpendicular to b1e1 + b2e2 + // we can compute this line by taking the cross-product: + // + // [ i j k ] + // [ 1 0 0 ] = - ez j + ey k = l1 + // [ ex ey ez ] + + // Where ex, ey, ez is the components of box2's x axis in the space of box 1, + // which is == to the 0th column of of box2toBox1 + + // The projection of box1 onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( box1Size, l1 ) = abs( -ez * box1.y ) + abs( ey * box1.z ) + + // To compute the projection of box2 onto this line, we'll do it in the space of box 2 + // + // [ i j k ] + // [ fx fy fz ] = fz j - fy k = l2 + // [ 1 0 0 ] + + // Where fx, fy, fz is the components of box1's x axis in the space of box 2, + // which is == to the 0th row of of box2toBox1 + + // The projection of box2 onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( box2Size, l2 ) = abs( fz * box2.y ) + abs ( fy * box2.z ) + + // The projection of the relative origin position on this line is done in the + // space of box 1: + // + // originProjection = DotProduct( <-ez j + ey k>, box2Origin ) = + // -ez * box2Origin.y + ey * box2Origin.z + + // NOTE: These checks can be bogus if both edges are parallel. The if + // checks at the beginning of each block are designed to catch that case + + // b1e1 x b2e1 + if ( absBox2ToBox1[0][0] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[1][0] + + box2Size.y * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][1]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][0] + box2Origin.z * box2ToBox1[1][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e1 x b2e2 + if ( absBox2ToBox1[0][1] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[1][1] + + box2Size.x * absBox2ToBox1[0][2] + box2Size.z * absBox2ToBox1[0][0]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][1] + box2Origin.z * box2ToBox1[1][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e1 x b2e3 + if ( absBox2ToBox1[0][2] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.y * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[1][2] + + box2Size.x * absBox2ToBox1[0][1] + box2Size.y * absBox2ToBox1[0][0]; + originProjection = FloatMakePositive( -box2Origin.y * box2ToBox1[2][2] + box2Origin.z * box2ToBox1[1][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[0], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e2 x b2e1 + if ( absBox2ToBox1[1][0] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][0] + box1Size.z * absBox2ToBox1[0][0] + + box2Size.y * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][1]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][0] - box2Origin.z * box2ToBox1[0][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e2 x b2e2 + if ( absBox2ToBox1[1][1] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][1] + box1Size.z * absBox2ToBox1[0][1] + + box2Size.x * absBox2ToBox1[1][2] + box2Size.z * absBox2ToBox1[1][0]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][1] - box2Origin.z * box2ToBox1[0][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e2 x b2e3 + if ( absBox2ToBox1[1][2] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[2][2] + box1Size.z * absBox2ToBox1[0][2] + + box2Size.x * absBox2ToBox1[1][1] + box2Size.y * absBox2ToBox1[1][0]; + originProjection = FloatMakePositive( box2Origin.x * box2ToBox1[2][2] - box2Origin.z * box2ToBox1[0][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[1], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e3 x b2e1 + if ( absBox2ToBox1[2][0] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][0] + box1Size.y * absBox2ToBox1[0][0] + + box2Size.y * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][1]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][0] + box2Origin.y * box2ToBox1[0][0] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 0, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e3 x b2e2 + if ( absBox2ToBox1[2][1] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][1] + box1Size.y * absBox2ToBox1[0][1] + + box2Size.x * absBox2ToBox1[2][2] + box2Size.z * absBox2ToBox1[2][0]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][1] + box2Origin.y * box2ToBox1[0][1] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 1, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + + // b1e3 x b2e3 + if ( absBox2ToBox1[2][2] < 1.0f - 1e-3f ) + { + boxProjectionSum = + box1Size.x * absBox2ToBox1[1][2] + box1Size.y * absBox2ToBox1[0][2] + + box2Size.x * absBox2ToBox1[2][1] + box2Size.y * absBox2ToBox1[2][0]; + originProjection = FloatMakePositive( -box2Origin.x * box2ToBox1[1][2] + box2Origin.y * box2ToBox1[0][2] ) + tolerance; + if ( FloatBits(originProjection) > FloatBits(boxProjectionSum) ) + { + MatrixGetColumn( box2ToWorld, 2, tmp ); + CrossProduct( worldToBox1[2], tmp.Base(), pPlane->normal.Base() ); + return true; + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, + const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, + float tolerance, cplane_t* pPlane ) +{ + matrix3x4_t worldToBox1, box2ToWorld; + ComputeCenterIMatrix( org1, angles1, min1, max1, worldToBox1 ); + ComputeCenterMatrix( org2, angles2, min2, max2, box2ToWorld ); + + // Then compute the size of the two boxes + Vector box1Size, box2Size; + VectorSubtract( max1, min1, box1Size ); + VectorSubtract( max2, min2, box2Size ); + box1Size *= 0.5f; + box2Size *= 0.5f; + + return ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, tolerance, pPlane ); +} + + +//----------------------------------------------------------------------------- +// Swept OBB test +//----------------------------------------------------------------------------- +bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, + const Vector& mins, const Vector& maxs ) +{ + if ( angles == vec3_angle ) + { + Vector vecWorldMins, vecWorldMaxs; + VectorAdd( org, mins, vecWorldMins ); + VectorAdd( org, maxs, vecWorldMaxs ); + return IsBoxIntersectingRay( vecWorldMins, vecWorldMaxs, ray ); + } + + if ( ray.m_IsRay ) + { + matrix3x4_t worldToBox; + AngleIMatrix( angles, org, worldToBox ); + + Ray_t rotatedRay; + VectorTransform( ray.m_Start, worldToBox, rotatedRay.m_Start ); + VectorRotate( ray.m_Delta, worldToBox, rotatedRay.m_Delta ); + rotatedRay.m_StartOffset = vec3_origin; + rotatedRay.m_Extents = vec3_origin; + rotatedRay.m_IsRay = ray.m_IsRay; + rotatedRay.m_IsSwept = ray.m_IsSwept; + + return IsBoxIntersectingRay( mins, maxs, rotatedRay ); + } + + if ( !ray.m_IsSwept ) + { + cplane_t plane; + return ComputeSeparatingPlane( ray.m_Start, vec3_angle, -ray.m_Extents, ray.m_Extents, + org, angles, mins, maxs, 0.0f, &plane ) == false; + } + + // NOTE: See the comments in ComputeSeparatingPlane to understand this math + + // First, compute the basis of box in the space of the ray + // NOTE: These basis place the origin at the centroid of each box! + matrix3x4_t worldToBox1, box2ToWorld; + ComputeCenterMatrix( org, angles, mins, maxs, box2ToWorld ); + + // Find the center + extents of an AABB surrounding the ray + Vector vecRayCenter; + VectorMA( ray.m_Start, 0.5, ray.m_Delta, vecRayCenter ); + vecRayCenter *= -1.0f; + SetIdentityMatrix( worldToBox1 ); + MatrixSetColumn( vecRayCenter, 3, worldToBox1 ); + + Vector box1Size; + box1Size.x = ray.m_Extents.x + FloatMakePositive( ray.m_Delta.x ) * 0.5f; + box1Size.y = ray.m_Extents.y + FloatMakePositive( ray.m_Delta.y ) * 0.5f; + box1Size.z = ray.m_Extents.z + FloatMakePositive( ray.m_Delta.z ) * 0.5f; + + // Then compute the size of the box + Vector box2Size; + VectorSubtract( maxs, mins, box2Size ); + box2Size *= 0.5f; + + // Do an OBB test of the box with the AABB surrounding the ray + cplane_t plane; + if ( ComputeSeparatingPlane( worldToBox1, box2ToWorld, box1Size, box2Size, 0.0f, &plane ) ) + return false; + + // Now deal with the planes which are the cross products of the ray sweep direction vs box edges + Vector vecRayDirection = ray.m_Delta; + VectorNormalize( vecRayDirection ); + + // Need a vector between ray center vs box center measured in the space of the ray (world) + Vector vecCenterDelta; + vecCenterDelta.x = box2ToWorld[0][3] - ray.m_Start.x; + vecCenterDelta.y = box2ToWorld[1][3] - ray.m_Start.y; + vecCenterDelta.z = box2ToWorld[2][3] - ray.m_Start.z; + + // Rotate the ray direction into the space of the OBB + Vector vecAbsRayDirBox2; + VectorIRotate( vecRayDirection, box2ToWorld, vecAbsRayDirBox2 ); + + // Make abs versions of the ray in world space + ray in box2 space + VectorAbs( vecAbsRayDirBox2, vecAbsRayDirBox2 ); + + // Now do the work for the planes which are perpendicular to the edges of the AABB + // and the sweep direction edges... + + // In this example, the line to check is perpendicular to box edge x + ray delta + // we can compute this line by taking the cross-product: + // + // [ i j k ] + // [ 1 0 0 ] = - dz j + dy k = l1 + // [ dx dy dz ] + + // Where dx, dy, dz is the ray delta (normalized) + + // The projection of the box onto this line = the absolute dot product of the box size + // against the line, which = + // AbsDot( vecBoxHalfDiagonal, l1 ) = abs( -dz * vecBoxHalfDiagonal.y ) + abs( dy * vecBoxHalfDiagonal.z ) + + // Because the plane contains the sweep direction, the sweep will produce + // no extra projection onto the line normal to the plane. + // Therefore all we need to do is project the ray extents onto this line also: + // AbsDot( ray.m_Extents, l1 ) = abs( -dz * ray.m_Extents.y ) + abs( dy * ray.m_Extents.z ) + + Vector vecPlaneNormal; + + // box x x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][0], box2ToWorld[1][0], box2ToWorld[2][0] ), vecPlaneNormal ); + float flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + float flBoxProjectionSum = + vecAbsRayDirBox2.z * box2Size.y + vecAbsRayDirBox2.y * box2Size.z + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + // box y x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][1], box2ToWorld[1][1], box2ToWorld[2][1] ), vecPlaneNormal ); + flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + flBoxProjectionSum = + vecAbsRayDirBox2.z * box2Size.x + vecAbsRayDirBox2.x * box2Size.z + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + // box z x ray delta + CrossProduct( vecRayDirection, Vector( box2ToWorld[0][2], box2ToWorld[1][2], box2ToWorld[2][2] ), vecPlaneNormal ); + flCenterDeltaProjection = FloatMakePositive( DotProduct( vecPlaneNormal, vecCenterDelta ) ); + flBoxProjectionSum = + vecAbsRayDirBox2.y * box2Size.x + vecAbsRayDirBox2.x * box2Size.y + + DotProductAbs( vecPlaneNormal, ray.m_Extents ); + if ( FloatBits(flCenterDeltaProjection) > FloatBits(flBoxProjectionSum) ) + return false; + + return true; +} + +//-------------------------------------------------------------------------- +// Purpose: +// +// NOTE: +// triangle points are given in clockwise order (aabb-triangle test) +// +// 1 edge0 = 1 - 0 +// | \ edge1 = 2 - 1 +// | \ edge2 = 0 - 2 +// | \ . +// | \ . +// 0-----2 . +// +//-------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: find the minima and maxima of the 3 given values +//----------------------------------------------------------------------------- +inline void FindMinMax( float v1, float v2, float v3, float &min, float &max ) +{ + min = max = v1; + if ( v2 < min ) { min = v2; } + if ( v2 > max ) { max = v2; } + if ( v3 < min ) { min = v3; } + if ( v3 > max ) { max = v3; } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossX2( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y + // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z + float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; + float flDist3 = flEdgeZ * p3.y - flEdgeY * p3.z; + + // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +// Purpose: +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossX3( float flEdgeZ, float flEdgeY, float flAbsEdgeZ, float flAbsEdgeY, + const Vector &p1, const Vector &p2, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialX(1,0,0) x edge ): x = 0.0f, y = edge.z, z = -edge.y + // Triangle Point Distances: dist(x) = normal.y * pt(x).y + normal.z * pt(x).z + float flDist1 = flEdgeZ * p1.y - flEdgeY * p1.z; + float flDist2 = flEdgeZ * p2.y - flEdgeY * p2.z; + + // Extents are symmetric: dist = abs( normal.y ) * extents.y + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.y + flAbsEdgeY * vecExtents.z; + + // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist2 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossY2( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z + float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; + float flDist3 = -flEdgeZ * p3.x + flEdgeX * p3.z; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossY3( float flEdgeZ, float flEdgeX, float flAbsEdgeZ, float flAbsEdgeX, + const Vector &p1, const Vector &p2, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialY(0,1,0) x edge ): x = -edge.z, y = 0.0f, z = edge.x + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.z * pt(x).z + float flDist1 = -flEdgeZ * p1.x + flEdgeX * p1.z; + float flDist2 = -flEdgeZ * p2.x + flEdgeX * p2.z; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.z ) * extents.z + float flDistBox = flAbsEdgeZ * vecExtents.x + flAbsEdgeX * vecExtents.z; + + // Either dist1, dist2 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist2 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossZ1( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, + const Vector &p2, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y + float flDist2 = flEdgeY * p2.x - flEdgeX * p2.y; + float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y + float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; + + // Either dist2, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist3 < flDist2 ) + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist2 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist2 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline bool AxisTestEdgeCrossZ2( float flEdgeY, float flEdgeX, float flAbsEdgeY, float flAbsEdgeX, + const Vector &p1, const Vector &p3, const Vector &vecExtents, + float flTolerance ) +{ + // Cross Product( axialZ(0,0,1) x edge ): x = edge.y, y = -edge.x, z = 0.0f + // Triangle Point Distances: dist(x) = normal.x * pt(x).x + normal.y * pt(x).y + float flDist1 = flEdgeY * p1.x - flEdgeX * p1.y; + float flDist3 = flEdgeY * p3.x - flEdgeX * p3.y; + + // Extents are symmetric: dist = abs( normal.x ) * extents.x + abs( normal.y ) * extents.y + float flDistBox = flAbsEdgeY * vecExtents.x + flAbsEdgeX * vecExtents.y; + + // Either dist1, dist3 is the closest point to the box, determine which and test of overlap with box(AABB). + if ( flDist1 < flDist3 ) + { + if ( ( flDist1 > ( flDistBox + flTolerance ) ) || ( flDist3 < -( flDistBox + flTolerance ) ) ) + return false; + } + else + { + if ( ( flDist3 > ( flDistBox + flTolerance ) ) || ( flDist1 < -( flDistBox + flTolerance ) ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Test for an intersection (overlap) between an axial-aligned bounding +// box (AABB) and a triangle. +// +// Using the "Separating-Axis Theorem" to test for intersections between +// a triangle and an axial-aligned bounding box (AABB). +// 1. 3 Axis Planes - x, y, z +// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial +// planes (x, y, z) +// 3. 1 Face Plane - the triangle plane (cplane_t plane below) +// Output: false = separating axis (no intersection) +// true = intersection +//----------------------------------------------------------------------------- +bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, + const Vector &v1, const Vector &v2, const Vector &v3, + const cplane_t &plane, float flTolerance ) +{ + // Test the axial planes (x,y,z) against the min, max of the triangle. + float flMin, flMax; + Vector p1, p2, p3; + + // x plane + p1.x = v1.x - vecBoxCenter.x; + p2.x = v2.x - vecBoxCenter.x; + p3.x = v3.x - vecBoxCenter.x; + FindMinMax( p1.x, p2.x, p3.x, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.x + flTolerance ) ) || ( flMax < -( vecBoxExtents.x + flTolerance ) ) ) + return false; + + // y plane + p1.y = v1.y - vecBoxCenter.y; + p2.y = v2.y - vecBoxCenter.y; + p3.y = v3.y - vecBoxCenter.y; + FindMinMax( p1.y, p2.y, p3.y, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.y + flTolerance ) ) || ( flMax < -( vecBoxExtents.y + flTolerance ) ) ) + return false; + + // z plane + p1.z = v1.z - vecBoxCenter.z; + p2.z = v2.z - vecBoxCenter.z; + p3.z = v3.z - vecBoxCenter.z; + FindMinMax( p1.z, p2.z, p3.z, flMin, flMax ); + if ( ( flMin > ( vecBoxExtents.z + flTolerance ) ) || ( flMax < -( vecBoxExtents.z + flTolerance ) ) ) + return false; + + // Test the 9 edge cases. + Vector vecEdge, vecAbsEdge; + + // edge 0 (cross x,y,z) + vecEdge = p2 - p1; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) + return false; + + // edge 1 (cross x,y,z) + vecEdge = p3 - p2; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX2( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY2( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ2( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p1, p3, vecBoxExtents, flTolerance ) ) + return false; + + // edge 2 (cross x,y,z) + vecEdge = p1 - p3; + vecAbsEdge.y = FloatMakePositive( vecEdge.y ); + vecAbsEdge.z = FloatMakePositive( vecEdge.z ); + if ( !AxisTestEdgeCrossX3( vecEdge.z, vecEdge.y, vecAbsEdge.z, vecAbsEdge.y, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + vecAbsEdge.x = FloatMakePositive( vecEdge.x ); + if ( !AxisTestEdgeCrossY3( vecEdge.z, vecEdge.x, vecAbsEdge.z, vecAbsEdge.x, p1, p2, vecBoxExtents, flTolerance ) ) + return false; + + if ( !AxisTestEdgeCrossZ1( vecEdge.y, vecEdge.x, vecAbsEdge.y, vecAbsEdge.x, p2, p3, vecBoxExtents, flTolerance ) ) + return false; + + // Test against the triangle face plane. + Vector vecMin, vecMax; + VectorSubtract( vecBoxCenter, vecBoxExtents, vecMin ); + VectorAdd( vecBoxCenter, vecBoxExtents, vecMax ); + if ( BoxOnPlaneSide( vecMin, vecMax, &plane ) != 3 ) + return false; + + return true; +} + +// NOTE: JAY: This is untested code based on Real-time Collision Detection by Ericson +#if 0 +Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ) +{ + Vector e0 = v1 - v0; + Vector e1 = v2 - v0; + Vector p0 = P - v0; + + // voronoi region of v0 + float d1 = DotProduct( e0, p0 ); + float d2 = DotProduct( e1, p0 ); + if (d1 <= 0.0f && d2 <= 0.0f) + return v0; + + // voronoi region of v1 + Vector p1 = P - v1; + float d3 = DotProduct( e0, p1 ); + float d4 = DotProduct( e1, p1 ); + if (d3 >=0.0f && d4 <= d3) + return v1; + + // voronoi region of e0 (v0-v1) + float ve2 = d1*d4 - d3*d2; + if ( ve2 <= 0.0f && d1 >= 0.0f && d3 <= 0.0f ) + { + float v = d1 / (d1-d3); + return v0 + v * e0; + } + // voronoi region of v2 + Vector p2 = P - v2; + float d5 = DotProduct( e0, p2 ); + float d6 = DotProduct( e1, p2 ); + if (d6 >= 0.0f && d5 <= d6) + return v2; + // voronoi region of e1 + float ve1 = d5*d2 - d1*d6; + if (ve1 <= 0.0f && d2 >= 0.0f && d6 >= 0.0f) + { + float w = d2 / (d2-d6); + return v0 + w * e1; + } + // voronoi region on e2 + float ve0 = d3*d6 - d5*d4; + if ( ve0 <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f ) + { + float w = (d4-d3)/((d4-d3) + (d5-d6)); + return v1 + w * (v2-v1); + } + // voronoi region of v0v1v2 triangle + float denom = 1.0f / (ve0+ve1+ve2); + float v = ve1*denom; + float w = ve2 * denom; + return v0 + e0 * v + e1 * w; +} +#endif + + +bool OBBHasFullyContainedIntersectionWithQuad( const Vector &vOBBExtent1_Scaled, const Vector &vOBBExtent2_Scaled, const Vector &vOBBExtent3_Scaled, const Vector &ptOBBCenter, + const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, + const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, + const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length ) +{ + Vector ptOBB[8]; //this specific ordering helps us web out from a point to its 3 connecting points with some bit math (most importantly, no if's) + ptOBB[0] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled; + ptOBB[1] = ptOBBCenter - vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled; + ptOBB[2] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled; + ptOBB[3] = ptOBBCenter - vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled; + ptOBB[4] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled - vOBBExtent3_Scaled; + ptOBB[5] = ptOBBCenter + vOBBExtent1_Scaled - vOBBExtent2_Scaled + vOBBExtent3_Scaled; + ptOBB[6] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled + vOBBExtent3_Scaled; + ptOBB[7] = ptOBBCenter + vOBBExtent1_Scaled + vOBBExtent2_Scaled - vOBBExtent3_Scaled; + + float fDists[8]; + for( int i = 0; i != 8; ++i ) + fDists[i] = vQuadNormal.Dot( ptOBB[i] ) - fQuadPlaneDist; + + int iSides[8]; + int iSideMask = 0; + for( int i = 0; i != 8; ++i ) + { + if( fDists[i] > 0.0f ) + { + iSides[i] = 1; + iSideMask |= 1; + } + else + { + iSides[i] = 2; + iSideMask |= 2; + } + } + + if( iSideMask != 3 ) //points reside entirely on one side of the quad's plane + return false; + + Vector ptPlaneIntersections[12]; //only have 12 lines, can only possibly generate 12 split points + int iPlaneIntersectionsCount = 0; + + for( int i = 0; i != 8; ++i ) + { + if( iSides[i] == 2 ) //point behind the plane + { + int iAxisCrossings[3]; + iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses vOBBExtent1 axis + iAxisCrossings[1] = ((i + 1) & 3) + (i & 4); //cycle to the next element while staying within the upper 4 or lower 4, this will cross either vOBBExtent2 or vOBBExtent3 axis, we don't care which + iAxisCrossings[2] = ((i - 1) & 3) + (i & 4); //cylce to the previous element while staying within the upper 4 or lower 4, this will cross the axis iAxisCrossings[1] didn't cross + + for( int j = 0; j != 3; ++j ) + { + if( iSides[iAxisCrossings[j]] == 1 ) //point in front of the plane + { + //line between ptOBB[i] and ptOBB[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing + float fTotalDist = fDists[iAxisCrossings[j]] - fDists[i]; //remember that fDists[i] is a negative value + ptPlaneIntersections[iPlaneIntersectionsCount] = (ptOBB[iAxisCrossings[j]] * (-fDists[i]/fTotalDist)) + (ptOBB[i] * (fDists[iAxisCrossings[j]]/fTotalDist)); + + Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane + + ++iPlaneIntersectionsCount; + } + } + } + } + + Assert( iPlaneIntersectionsCount != 0 ); + + for( int i = 0; i != iPlaneIntersectionsCount; ++i ) + { + //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents + Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter; + + float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter ); + if( fabs( fExt1Dist ) > fQuadExtent1Length ) + return false; //point is outside boundaries + + //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads + + float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter ); + if( fabs( fExt2Dist ) > fQuadExtent2Length ) + return false; //point is outside boundaries + } + + return true; //there were lines crossing the quad plane, and every line crossing that plane had its intersection with the plane within the quad's boundaries +} + +//----------------------------------------------------------------------------- +// Compute if the Ray intersects the quad plane, and whether the entire +// Ray/Quad intersection is contained within the quad itself +// +// False if no intersection exists, or if part of the intersection is +// outside the quad's extents +//----------------------------------------------------------------------------- +bool RayHasFullyContainedIntersectionWithQuad( const Ray_t &ray, + const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, + const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, + const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length ) +{ + Vector ptPlaneIntersections[(12 + 12 + 8)]; //absolute max possible: 12 lines to connect the start box, 12 more to connect the end box, 8 to connect the boxes to eachother + + //8 points to make an AABB, 8 lines to connect each point from it's start to end point along the ray, 8 possible intersections + int iPlaneIntersectionsCount = 0; + + if( ray.m_IsRay ) + { + //just 1 line + if( ray.m_IsSwept ) + { + Vector ptEndPoints[2]; + ptEndPoints[0] = ray.m_Start; + ptEndPoints[1] = ptEndPoints[0] + ray.m_Delta; + + int i; + float fDists[2]; + for( i = 0; i != 2; ++i ) + fDists[i] = vQuadNormal.Dot( ptEndPoints[i] ) - fQuadPlaneDist; + + for( i = 0; i != 2; ++i ) + { + if( fDists[i] <= 0.0f ) + { + int j = 1-i; + if( fDists[j] >= 0.0f ) + { + float fInvTotalDist = 1.0f / (fDists[j] - fDists[i]); //fDists[i] <= 0, ray is swept so no chance that the denom was 0 + ptPlaneIntersections[0] = (ptEndPoints[i] * (fDists[j] * fInvTotalDist)) - (ptEndPoints[j] * (fDists[i] * fInvTotalDist)); //fDists[i] <= 0 + Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane + iPlaneIntersectionsCount = 1; + } + else + { + return false; + } + break; + } + } + + if( i == 2 ) + return false; + } + else //not swept, so this is actually a point on quad question + { + if( fabs( vQuadNormal.Dot( ray.m_Start ) - fQuadPlaneDist ) < 1e-6 ) + { + ptPlaneIntersections[0] = ray.m_Start; + iPlaneIntersectionsCount = 1; + } + else + { + return false; + } + } + } + else + { + Vector ptEndPoints[2][8]; + //this specific ordering helps us web out from a point to its 3 connecting points with some bit math (most importantly, no if's) + ptEndPoints[0][0] = ray.m_Start; ptEndPoints[0][0].x -= ray.m_Extents.x; ptEndPoints[0][0].y -= ray.m_Extents.y; ptEndPoints[0][0].z -= ray.m_Extents.z; + ptEndPoints[0][1] = ray.m_Start; ptEndPoints[0][1].x -= ray.m_Extents.x; ptEndPoints[0][1].y -= ray.m_Extents.y; ptEndPoints[0][1].z += ray.m_Extents.z; + ptEndPoints[0][2] = ray.m_Start; ptEndPoints[0][2].x -= ray.m_Extents.x; ptEndPoints[0][2].y += ray.m_Extents.y; ptEndPoints[0][2].z += ray.m_Extents.z; + ptEndPoints[0][3] = ray.m_Start; ptEndPoints[0][3].x -= ray.m_Extents.x; ptEndPoints[0][3].y += ray.m_Extents.y; ptEndPoints[0][3].z -= ray.m_Extents.z; + ptEndPoints[0][4] = ray.m_Start; ptEndPoints[0][4].x += ray.m_Extents.x; ptEndPoints[0][4].y -= ray.m_Extents.y; ptEndPoints[0][4].z -= ray.m_Extents.z; + ptEndPoints[0][5] = ray.m_Start; ptEndPoints[0][5].x += ray.m_Extents.x; ptEndPoints[0][5].y -= ray.m_Extents.y; ptEndPoints[0][5].z += ray.m_Extents.z; + ptEndPoints[0][6] = ray.m_Start; ptEndPoints[0][6].x += ray.m_Extents.x; ptEndPoints[0][6].y += ray.m_Extents.y; ptEndPoints[0][6].z += ray.m_Extents.z; + ptEndPoints[0][7] = ray.m_Start; ptEndPoints[0][7].x += ray.m_Extents.x; ptEndPoints[0][7].y += ray.m_Extents.y; ptEndPoints[0][7].z -= ray.m_Extents.z; + + float fDists[2][8]; + int iSides[2][8]; + int iSideMask[2] = { 0, 0 }; + for( int i = 0; i != 8; ++i ) + { + fDists[0][i] = vQuadNormal.Dot( ptEndPoints[0][i] ) - fQuadPlaneDist; + if( fDists[0][i] > 0.0f ) + { + iSides[0][i] = 1; + iSideMask[0] |= 1; + } + else + { + iSides[0][i] = 2; + iSideMask[0] |= 2; + } + } + + if( ray.m_IsSwept ) + { + for( int i = 0; i != 8; ++i ) + ptEndPoints[1][i] = ptEndPoints[0][i] + ray.m_Delta; + + for( int i = 0; i != 8; ++i ) + { + fDists[1][i] = vQuadNormal.Dot( ptEndPoints[1][i] ) - fQuadPlaneDist; + if( fDists[1][i] > 0.0f ) + { + iSides[1][i] = 1; + iSideMask[1] |= 1; + } + else + { + iSides[1][i] = 2; + iSideMask[1] |= 2; + } + } + } + + if( (iSideMask[0] | iSideMask[1]) != 3 ) + { + //Assert( (iSideMask[0] | iSideMask[1]) != 2 ); + return false; //all points resides entirely on one side of the quad + } + + + //generate intersections for boxes split by the plane at either end of the ray + for( int k = 0; k != 2; ++k ) + { + if( iSideMask[k] == 3 ) //box is split by the plane + { + for( int i = 0; i != 8; ++i ) + { + if( iSides[k][i] == 2 ) //point behind the plane + { + int iAxisCrossings[3]; + iAxisCrossings[0] = i ^ 4; //upper 4 vs lower 4 crosses X axis + iAxisCrossings[1] = ((i + 1) & 3) + (i & 4); //cycle to the next element while staying within the upper 4 or lower 4, this will cross either Y or Z axis, we don't care which + iAxisCrossings[2] = ((i - 1) & 3) + (i & 4); //cylce to the previous element while staying within the upper 4 or lower 4, this will cross the axis iAxisCrossings[1] didn't cross + + for( int j = 0; j != 3; ++j ) + { + if( iSides[k][iAxisCrossings[j]] == 1 ) //point in front of the plane + { + //line between ptEndPoints[i] and ptEndPoints[iAxisCrossings[j]] intersects the plane, generate a point at the intersection for further testing + float fInvTotalDist = 1.0f / (fDists[k][iAxisCrossings[j]] - fDists[k][i]); //remember that fDists[k][i] is a negative value + ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[k][iAxisCrossings[j]] * (-fDists[k][i] * fInvTotalDist)) + (ptEndPoints[k][i] * (fDists[k][iAxisCrossings[j]] * fInvTotalDist)); + + Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane + + ++iPlaneIntersectionsCount; + } + } + } + } + } + } + + if( ray.m_IsSwept ) + { + for( int i = 0; i != 8; ++i ) + { + if( iSides[0][i] != iSides[1][i] ) + { + int iPosSide, iNegSide; + if( iSides[0][i] == 1 ) + { + iPosSide = 0; + iNegSide = 1; + } + else + { + iPosSide = 1; + iNegSide = 0; + } + + Assert( (fDists[iPosSide][i] >= 0.0f) && (fDists[iNegSide][i] <= 0.0f) ); + + float fInvTotalDist = 1.0f / (fDists[iPosSide][i] - fDists[iNegSide][i]); //remember that fDists[iNegSide][i] is a negative value + ptPlaneIntersections[iPlaneIntersectionsCount] = (ptEndPoints[iPosSide][i] * (-fDists[iNegSide][i] * fInvTotalDist)) + (ptEndPoints[iNegSide][i] * (fDists[iPosSide][i] * fInvTotalDist)); + + Assert( fabs( ptPlaneIntersections[iPlaneIntersectionsCount].Dot( vQuadNormal ) - fQuadPlaneDist ) < 0.1f ); //intersection point is on plane + + ++iPlaneIntersectionsCount; + } + } + } + } + + //down here, we should simply have a collection of plane intersections, now we see if they reside within the quad + Assert( iPlaneIntersectionsCount != 0 ); + + for( int i = 0; i != iPlaneIntersectionsCount; ++i ) + { + //these points are guaranteed to be on the plane, now just check to see if they're within the quad's extents + Vector vToPointFromQuadCenter = ptPlaneIntersections[i] - ptQuadCenter; + + float fExt1Dist = vQuadExtent1_Normalized.Dot( vToPointFromQuadCenter ); + if( fabs( fExt1Dist ) > fQuadExtent1Length ) + return false; //point is outside boundaries + + //vToPointFromQuadCenter -= vQuadExtent1_Normalized * fExt1Dist; //to handle diamond shaped quads + + float fExt2Dist = vQuadExtent2_Normalized.Dot( vToPointFromQuadCenter ); + if( fabs( fExt2Dist ) > fQuadExtent2Length ) + return false; //point is outside boundaries + } + + return true; //there were lines crossing the quad plane, and every line crossing that plane had its intersection with the plane within the quad's boundaries +} + +//----------------------------------------------------------------------------- +// Purpose: override how single player rays hit the player +//----------------------------------------------------------------------------- + +bool LineCircleIntersection(const Vector2D ¢er, + const float radius, + const Vector2D &vLinePt, + const Vector2D &vLineDir, + float *fIntersection1, + float *fIntersection2) +{ + // Line = P + Vt + // Sphere = r (assume we've translated to origin) + // (P + Vt)^2 = r^2 + // VVt^2 + 2PVt + (PP - r^2) + // Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a + // If (b^2 - 4ac) is < 0 there is no solution. + // If (b^2 - 4ac) is = 0 there is one solution + // If (b^2 - 4ac) is > 0 there are two solutions. + + // Translate circle to origin. + const Vector2D P( vLinePt - center ); + + const float a = vLineDir.Dot(vLineDir); + const float b = 2.0f * P.Dot(vLineDir); + const float c = P.Dot(P) - (radius * radius); + + const float insideSqr = b*b - 4*a*c; + + // No solution - (b^2 - 4ac) is < 0 + if( insideSqr < -1.0e-6f ) + { + return false; + } + else + { + const float sqr = (float)FastSqrt(insideSqr); + const float denom = 1.0 / (2.0f * a); + const float t0 = (-b - sqr) * denom; + const float t1 = (-b + sqr) * denom; + + // One solution - (b^2 - 4ac) is = 0 + if( insideSqr < 1.0e-6f ) + { + // a = 0 if the line direction is the zero vector, in which case, + // the line starts inside the circle but will never exit. We fudge + // it for this case and say it intersects at the origin of the line. + // Otherwise, the result is the smallest positive result + *fIntersection1 = *fIntersection2 = ( a == 0.0f ) ? 0.0f : ( t0 < 0 ? t1 : t0 ); + Assert( !IS_NAN(*fIntersection1) ); + + // Started inside of the sphere (the only way we get one solution, unless + // the ray direction is the zero vector) + return c < 0; + } + // Two solutions - (b^2 - 4ac) is > 0 + else + { + *fIntersection1 = t0; + *fIntersection2 = t1; + } + + return true; + } +} + +bool IntersectRayWithAACylinder( const Ray_t &ray, + const Vector ¢er, float radius, float height, CBaseTrace *pTrace ) +{ + Assert( ray.m_IsRay ); + Collision_ClearTrace( ray.m_Start, ray.m_Delta, pTrace ); + + // First intersect the ray with the top + bottom planes + float halfHeight = height * 0.5; + + // Handle parallel case + Vector vStart = ray.m_Start - center; + Vector vEnd = vStart + ray.m_Delta; + + float flEnterFrac, flLeaveFrac; + if (FloatMakePositive(ray.m_Delta.z) < 1e-8) + { + if ( (vStart.z < -halfHeight) || (vStart.z > halfHeight) ) + { + return false; // no hit + } + flEnterFrac = 0.0f; flLeaveFrac = 1.0f; + } + else + { + // Clip the ray to the top and bottom of box + flEnterFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, halfHeight); + flLeaveFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, -halfHeight); + + if ( flLeaveFrac < flEnterFrac ) + { + float temp = flLeaveFrac; + flLeaveFrac = flEnterFrac; + flEnterFrac = temp; + } + + if ( flLeaveFrac < 0 || flEnterFrac > 1) + { + return false; + } + } + + // Intersect with circle + float flCircleEnterFrac, flCircleLeaveFrac; + if ( !LineCircleIntersection( vec3_origin.AsVector2D(), radius, + vStart.AsVector2D(), ray.m_Delta.AsVector2D(), &flCircleEnterFrac, &flCircleLeaveFrac ) ) + { + return false; // no hit + } + + Assert( flCircleEnterFrac <= flCircleLeaveFrac ); + if ( flCircleLeaveFrac < 0 || flCircleEnterFrac > 1) + { + return false; + } + + if ( flEnterFrac < flCircleEnterFrac ) + flEnterFrac = flCircleEnterFrac; + if ( flLeaveFrac > flCircleLeaveFrac ) + flLeaveFrac = flCircleLeaveFrac; + + if ( flLeaveFrac < flEnterFrac ) + return false; + + VectorMA( ray.m_Start, flEnterFrac , ray.m_Delta, pTrace->endpos ); + pTrace->fraction = flEnterFrac; + pTrace->contents = CONTENTS_SOLID; + + // Calculate the point on our center line where we're nearest the intersection point + Vector collisionCenter; + CalcClosestPointOnLineSegment( pTrace->endpos, center + Vector( 0, 0, halfHeight ), center - Vector( 0, 0, halfHeight ), collisionCenter ); + + // Our normal is the direction from that center point to the intersection point + pTrace->plane.normal = pTrace->endpos - collisionCenter; + VectorNormalize( pTrace->plane.normal ); + + return true; +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/collisionutils.h b/public/collisionutils.h new file mode 100644 index 0000000..ea4de76 --- /dev/null +++ b/public/collisionutils.h @@ -0,0 +1,491 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Common collision utility methods +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef COLLISIONUTILS_H +#define COLLISIONUTILS_H + +#include "tier0/platform.h" + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/ssemath.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct Ray_t; +class Vector; +class Vector2D; +class Vector4D; +struct cplane_t; +class QAngle; +class CBaseTrace; +struct matrix3x4_t; + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithTriangle +// +// Intersects a ray with a triangle, returns distance t along ray. +// t will be less than zero if no intersection occurred +// oneSided will cull collisions which approach the triangle from the back +// side, assuming the vertices are specified in counter-clockwise order +// The vertices need not be specified in that order if oneSided is not used +// +//----------------------------------------------------------------------------- +float IntersectRayWithTriangle( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, + bool oneSided ); + +//----------------------------------------------------------------------------- +// +// ComputeIntersectionBarycentricCoordinates +// +// Figures out the barycentric coordinates (u,v) where a ray hits a +// triangle. Note that this will ignore the ray extents, and it also ignores +// the ray length. Note that the edge from v1->v2 represents u (v2: u = 1), +// and the edge from v1->v3 represents v (v3: v = 1). It returns false +// if the ray is parallel to the triangle (or when t is specified if t is less +// than zero). +// +//----------------------------------------------------------------------------- +bool ComputeIntersectionBarycentricCoordinates( const Ray_t& ray, + const Vector& v1, const Vector& v2, const Vector& v3, float& u, float& v, + float *t = 0 ); + +//----------------------------------------------------------------------------- +// +// IntersectRayWithRay +// +// Returns whether or not there was an intersection. The "t" paramter is the +// distance along ray0 and the "s" parameter is the distance along ray1. If +// the two lines to not intersect the "t" and "s" represent the closest approach. +// "t" and "s" will not change if the rays are parallel. +// +//----------------------------------------------------------------------------- +bool IntersectRayWithRay( const Ray_t &ray0, const Ray_t &ray1, float &t, float &s ); + + +//----------------------------------------------------------------------------- +// +// IntersectRayWithSphere +// +// Returns whether or not there was an intersection. Returns the two intersection points. +// NOTE: The point of closest approach can be found at the average t value. +// +//----------------------------------------------------------------------------- +bool IntersectRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); + + +//----------------------------------------------------------------------------- +// +// IntersectInfiniteRayWithSphere +// +// Returns whether or not there was an intersection of a sphere against an infinitely +// extending ray. +// Returns the two intersection points +// +//----------------------------------------------------------------------------- +bool IntersectInfiniteRayWithSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float *pT1, float *pT2 ); + + +// returns true if the sphere and cone intersect +// NOTE: cone sine/cosine are the half angle of the cone +bool IsSphereIntersectingCone( const Vector &sphereCenter, float sphereRadius, const Vector &coneOrigin, const Vector &coneNormal, float coneSine, float coneCosine ); + +//----------------------------------------------------------------------------- +// +// IntersectRayWithPlane +// +// Intersects a ray with a plane, returns distance t along ray. +// t will be less than zero the intersection occurs in the opposite direction of the ray. +// +//----------------------------------------------------------------------------- +float IntersectRayWithPlane( const Ray_t& ray, const cplane_t& plane ); +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const cplane_t& plane ); +float IntersectRayWithPlane( const Vector& org, const Vector& dir, const Vector& normal, float dist ); + +// This version intersects a ray with an axis-aligned plane +float IntersectRayWithAAPlane( const Vector& vecStart, const Vector& vecEnd, int nAxis, float flSign, float flDist ); + + +//----------------------------------------------------------------------------- +// IntersectRayWithBox +// +// Purpose: Computes the intersection of a ray with a box (AABB) +// Output : Returns true if there is an intersection + trace information +//----------------------------------------------------------------------------- +bool IntersectRayWithBox( const Vector &rayStart, const Vector &rayDelta, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace, float *pFractionLeftSolid = NULL ); +bool IntersectRayWithBox( const Ray_t &ray, const Vector &boxMins, const Vector &boxMaxs, float epsilon, CBaseTrace *pTrace, float *pFractionLeftSolid = NULL ); + +//----------------------------------------------------------------------------- +// Intersects a ray against a box +//----------------------------------------------------------------------------- +struct BoxTraceInfo_t +{ + float t1; + float t2; + int hitside; + bool startsolid; +}; + +bool IntersectRayWithBox( const Vector &vecRayStart, const Vector &vecRayDelta, + const Vector &boxMins, const Vector &boxMaxs, float flTolerance, BoxTraceInfo_t *pTrace ); + + +//----------------------------------------------------------------------------- +// IntersectRayWithOBB +// +// Purpose: Computes the intersection of a ray with a oriented box (OBB) +// Output : Returns true if there is an intersection + trace information +//----------------------------------------------------------------------------- +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Ray_t &ray, const Vector &vecBoxOrigin, const QAngle &angBoxRotation, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Ray_t &ray, const matrix3x4_t &matOBBToWorld, + const Vector &vecOBBMins, const Vector &vecOBBMaxs, float flTolerance, CBaseTrace *pTrace ); + +bool IntersectRayWithOBB( const Vector &vecRayStart, const Vector &vecRayDelta, + const matrix3x4_t &matOBBToWorld, const Vector &vecOBBMins, const Vector &vecOBBMaxs, + float flTolerance, BoxTraceInfo_t *pTrace ); + +//----------------------------------------------------------------------------- +// +// IsSphereIntersectingSphere +// +// returns true if there's an intersection between sphere and sphere +// +//----------------------------------------------------------------------------- +bool IsSphereIntersectingSphere( const Vector& center1, float radius1, + const Vector& center2, float radius2 ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingSphere +// +// returns true if there's an intersection between box and sphere +// +//----------------------------------------------------------------------------- +bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, + const Vector& center, float radius ); + +bool IsBoxIntersectingSphereExtents( const Vector& boxCenter, const Vector& boxHalfDiag, + const Vector& center, float radius ); + + +//----------------------------------------------------------------------------- +// Returns true if a box intersects with a sphere +// NOTE: f4RadiusSq must have the radius sq in all components +//----------------------------------------------------------------------------- +FORCEINLINE bool IsBoxIntersectingSphere( const Vector& boxMin, const Vector& boxMax, + const fltx4& f4Center, const fltx4& f4RadiusSq ) +{ + // See Graphics Gems, box-sphere intersection + fltx4 f4Mins = LoadUnalignedSIMD( &boxMin.x ); + fltx4 f4Maxs = LoadUnalignedSIMD( &boxMax.x ); + fltx4 f4MinDelta = SubSIMD( f4Mins, f4Center ); + fltx4 f4MaxDelta = SubSIMD( f4Center, f4Maxs ); + f4MinDelta = MaxSIMD( f4MinDelta, Four_Zeros ); + f4MaxDelta = MaxSIMD( f4MaxDelta, Four_Zeros ); + fltx4 f4Delta = AddSIMD( f4MinDelta, f4MaxDelta ); + fltx4 f4DistSq = Dot3SIMD( f4Delta, f4Delta ); + return IsAllGreaterThan( f4RadiusSq, f4DistSq ); +} + + +//----------------------------------------------------------------------------- +// returns true if there's an intersection between ray and sphere +//----------------------------------------------------------------------------- +bool IsRayIntersectingSphere( const Vector &vecRayOrigin, const Vector &vecRayDelta, + const Vector &vecSphereCenter, float flRadius, float flTolerance = 0.0f ); + + +//----------------------------------------------------------------------------- +// +// IsCircleIntersectingRectangle +// +// returns true if there's an intersection between rectangle and circle +// +//----------------------------------------------------------------------------- +bool IsCircleIntersectingRectangle( const Vector2D& boxMin, const Vector2D& boxMax, + const Vector2D& center, float radius ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingBox +// +// returns true if there's an intersection between two boxes +// +//----------------------------------------------------------------------------- +bool IsBoxIntersectingBox( const Vector& boxMin1, const Vector& boxMax1, + const Vector& boxMin2, const Vector& boxMax2 ); + +bool IsBoxIntersectingBoxExtents( const Vector& boxCenter1, const Vector& boxHalfDiagonal1, + const Vector& boxCenter2, const Vector& boxHalfDiagonal2 ); + + +#ifdef _X360 +// inline version: +#include "mathlib/ssemath.h" +inline bool IsBoxIntersectingBoxExtents( const fltx4 boxCenter1, const fltx4 boxHalfDiagonal1, + const fltx4 boxCenter2, const fltx4 boxHalfDiagonal2 ); +#endif + +//----------------------------------------------------------------------------- +// +// IsOBBIntersectingOBB +// +// returns true if there's an intersection between two OBBs +// +//----------------------------------------------------------------------------- +bool IsOBBIntersectingOBB( const Vector &vecOrigin1, const QAngle &vecAngles1, const Vector& boxMin1, const Vector& boxMax1, + const Vector &vecOrigin2, const QAngle &vecAngles2, const Vector& boxMin2, const Vector& boxMax2, float flTolerance = 0.0f ); + + +//----------------------------------------------------------------------------- +// +// IsBoxIntersectingRay +// +// returns true if there's an intersection between box and ray +// +//----------------------------------------------------------------------------- + +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, float flTolerance = 0.0f ); + +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Ray_t& ray, float flTolerance = 0.0f ); + +bool FASTCALL IsBoxIntersectingRay( const Vector& boxMin, const Vector& boxMax, + const Vector& origin, const Vector& delta, + const Vector& invDelta, float flTolerance = 0.0f ); + + +// On the PC, we can't pass fltx4's in registers like this. On the x360, it is +// much better if we do. +#ifdef _X360 +bool FASTCALL IsBoxIntersectingRay( fltx4 boxMin, fltx4 boxMax, + fltx4 origin, fltx4 delta, fltx4 invDelta, // ray parameters + fltx4 vTolerance = LoadZeroSIMD() ///< eg from ReplicateX4(flTolerance) + ); +#else +bool FASTCALL IsBoxIntersectingRay( const fltx4 &boxMin, const fltx4 &boxMax, + const fltx4 & origin, const fltx4 & delta, const fltx4 & invDelta, // ray parameters + const fltx4 & vTolerance = Four_Zeros ///< eg from ReplicateX4(flTolerance) + ); +#endif + +bool inline FASTCALL IsBoxIntersectingRay( const fltx4& boxMin, const fltx4& boxMax, + const fltx4& origin, const fltx4& delta, float flTolerance = 0.0f ) +{ + return IsBoxIntersectingRay( boxMin, boxMax, origin, delta, ReciprocalSIMD(delta), ReplicateX4(flTolerance) ); +} + + +bool FASTCALL IsBoxIntersectingRay( const fltx4& boxMin, const fltx4& boxMax, + const Ray_t& ray, float flTolerance = 0.0f ); + + + +//----------------------------------------------------------------------------- +// +// IsPointInBox +// +// returns true if the point is in the box +// +//----------------------------------------------------------------------------- +bool IsPointInBox( const Vector& pt, const Vector& boxMin, const Vector& boxMax ); + + +// SIMD version +FORCEINLINE bool IsPointInBox( const fltx4& pt, const fltx4& boxMin, const fltx4& boxMax ) +{ + fltx4 greater = CmpGtSIMD( pt,boxMax ); + fltx4 less = CmpLtSIMD( pt, boxMin ); + return (IsAllZeros(SetWToZeroSIMD(OrSIMD(greater,less)))); +} + + + +//----------------------------------------------------------------------------- +// Purpose: returns true if pt intersects the truncated cone +// origin - cone tip, axis unit cone axis, cosAngle - cosine of cone axis to surface angle +//----------------------------------------------------------------------------- +bool IsPointInCone( const Vector &pt, const Vector &origin, const Vector &axis, float cosAngle, float length ); + +//----------------------------------------------------------------------------- +// Intersects a plane with a triangle (using barycentric definition) +// The return value, in pIntersection, is an array of barycentric coordinates +// describing at most 2 intersection points. +// The return value is the number of intersection points +//----------------------------------------------------------------------------- +int IntersectTriangleWithPlaneBarycentric( const Vector& org, const Vector& edgeU, const Vector& edgeV, + const Vector4D& plane, Vector2D* pIntersection ); + +//----------------------------------------------------------------------------- +// +// PointInQuadBarycentric +// +// Given a point and a quad in a plane return the u and v (barycentric) positions +// of the point relative to the quad. The points (v1,v2,v3,v4) should be given +// in a counter-clockwise order with v1 acting as the primary corner (u=0, v=0). +// Thus, u0 = v2 - v1, and v0 = v4 - v1. +// +//----------------------------------------------------------------------------- + +enum QuadBarycentricRetval_t +{ + BARY_QUADRATIC_FALSE = 0, + BARY_QUADRATIC_TRUE = 1, + BARY_QUADRATIC_NEGATIVE_DISCRIMINANT = 2 +}; + +QuadBarycentricRetval_t PointInQuadToBarycentric( const Vector &v1, const Vector &v2, + const Vector &v3, const Vector &v4, const Vector &point, Vector2D &uv ); + + +void PointInQuadFromBarycentric( const Vector &v1, const Vector &v2, const Vector &v3, const Vector &v4, + const Vector2D &uv, Vector &point ); +void TexCoordInQuadFromBarycentric( const Vector2D &v1, const Vector2D &v2, const Vector2D &v3, const Vector2D &v4, + const Vector2D &uv, Vector2D &texCoord ); + + +//----------------------------------------------------------------------------- +// Compute point from barycentric specification +// Edge u goes from v0 to v1, edge v goes from v0 to v2 +//----------------------------------------------------------------------------- +void ComputePointFromBarycentric( const Vector& v0, const Vector& v1, const Vector& v2, + float u, float v, Vector& pt ); +void ComputePointFromBarycentric( const Vector2D& v0, const Vector2D& v1, const Vector2D& v2, + float u, float v, Vector2D& pt ); + + +//----------------------------------------------------------------------------- +// Swept OBB test +//----------------------------------------------------------------------------- +bool IsRayIntersectingOBB( const Ray_t &ray, const Vector& org, const QAngle& angles, + const Vector& mins, const Vector& maxs ); + + +//----------------------------------------------------------------------------- +// Compute a separating plane between two boxes (expensive!) +// Returns false if no separating plane exists +//----------------------------------------------------------------------------- +bool ComputeSeparatingPlane( const Vector& org1, const QAngle& angles1, const Vector& min1, const Vector& max1, + const Vector& org2, const QAngle& angles2, const Vector& min2, const Vector& max2, + float tolerance, cplane_t* pPlane ); + +//----------------------------------------------------------------------------- +// IsBoxIntersectingTriangle +// +// Test for an intersection (overlap) between an axial-aligned bounding +// box (AABB) and a triangle. +// +// Triangle points are in counter-clockwise order with the normal facing "out." +// +// Using the "Separating-Axis Theorem" to test for intersections between +// a triangle and an axial-aligned bounding box (AABB). +// 1. 3 Axis Plane Tests - x, y, z +// 2. 9 Edge Planes Tests - the 3 edges of the triangle crossed with all 3 axial +// planes (x, y, z) +// 3. 1 Face Plane Test - the plane the triangle resides in (cplane_t plane) +//----------------------------------------------------------------------------- +bool IsBoxIntersectingTriangle( const Vector &vecBoxCenter, const Vector &vecBoxExtents, + const Vector &v1, const Vector &v2, const Vector &v3, + const cplane_t &plane, float flTolerance ); + + +Vector CalcClosestPointOnTriangle( const Vector &P, const Vector &v0, const Vector &v1, const Vector &v2 ); + + +//----------------------------------------------------------------------------- +// Compute if the OBB intersects the quad plane, and whether the entire +// OBB/Quad intersection is contained within the quad itself +// +// False if no intersection exists, or if part of the intersection is +// outside the quad's extents +//----------------------------------------------------------------------------- +bool OBBHasFullyContainedIntersectionWithQuad( const Vector &vOBBExtent1_Scaled, const Vector &vOBBExtent2_Scaled, const Vector &vOBBExtent3_Scaled, const Vector &ptOBBCenter, + const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, + const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, + const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length ); + + +//----------------------------------------------------------------------------- +// Compute if the Ray intersects the quad plane, and whether the entire +// Ray/Quad intersection is contained within the quad itself +// +// False if no intersection exists, or if part of the intersection is +// outside the quad's extents +//----------------------------------------------------------------------------- +bool RayHasFullyContainedIntersectionWithQuad( const Ray_t &ray, + const Vector &vQuadNormal, float fQuadPlaneDist, const Vector &ptQuadCenter, + const Vector &vQuadExtent1_Normalized, float fQuadExtent1Length, + const Vector &vQuadExtent2_Normalized, float fQuadExtent2Length ); + + + +//----------------------------------------------------------------------------- +// Compute the intersection of a line and a circle +//----------------------------------------------------------------------------- +bool LineCircleIntersection(const Vector2D ¢er, + const float radius, + const Vector2D &vLinePt, + const Vector2D &vLineDir, + float *fIntersection1, + float *fIntersection2); + + +//----------------------------------------------------------------------------- +// Find the intersection of a ray with an axis-aligned cylinder +//----------------------------------------------------------------------------- +bool IntersectRayWithAACylinder( const Ray_t &ray, + const Vector ¢er, + float radius, + float height, + CBaseTrace *pTrace ); + +//----------------------------------------------------------------------------- +// INLINES +//----------------------------------------------------------------------------- + + +#ifdef _X360 +inline bool IsBoxIntersectingBoxExtents( const fltx4 boxCenter1, const fltx4 boxHalfDiagonal1, + const fltx4 boxCenter2, const fltx4 boxHalfDiagonal2 ) +{ + fltx4 vecDelta, vecSize; + + vecDelta = SubSIMD(boxCenter1, boxCenter2); + vecSize = AddSIMD(boxHalfDiagonal1, boxHalfDiagonal2); + + uint condition; + XMVectorInBoundsR(&condition, vecDelta, vecSize); + // we want the top three words to be all 1's ; that means in bounds + + + return XMComparisonAllInBounds( condition ); +} +#endif + + +#endif // COLLISIONUTILS_H diff --git a/public/con_nprint.h b/public/con_nprint.h new file mode 100644 index 0000000..02e2c63 --- /dev/null +++ b/public/con_nprint.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Debug overlay / notfication printing +// +//=============================================================================// + +#ifndef CON_NPRINT_H +#define CON_NPRINT_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Debug overlay / notfication printing +// NOTE: Structure cannot be changed by mods +//----------------------------------------------------------------------------- +typedef struct con_nprint_s +{ + int index; // Row # + float time_to_live; // # of seconds before it disappears. -1 means to display for 1 frame then go away. + float color[ 3 ]; // RGB colors ( 0.0 -> 1.0 scale ) + bool fixed_width_font; +} con_nprint_t; + +// Print string on line idx +void Con_NPrintf( int idx, const char *fmt, ... ); +// Customized printout +void Con_NXPrintf( const con_nprint_t *info, const char *fmt, ... ); + +#endif // CON_NPRINT_H diff --git a/public/const.h b/public/const.h new file mode 100644 index 0000000..7798689 --- /dev/null +++ b/public/const.h @@ -0,0 +1,429 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef CONST_H +#define CONST_H + +#ifdef _WIN32 +#pragma once +#endif + +// the command line param that tells the engine to use steam +#define STEAM_PARM "-steam" +// the command line param to tell dedicated server to restart +// if they are out of date +#define AUTO_RESTART "-autoupdate" + +// the message a server sends when a clients steam login is expired +#define INVALID_STEAM_TICKET "Invalid STEAM UserID Ticket\n" +#define INVALID_STEAM_LOGON "No Steam logon\n" +#define INVALID_STEAM_VACBANSTATE "VAC banned from secure server\n" +#define INVALID_STEAM_LOGGED_IN_ELSEWHERE "This Steam account is being used in another location\n" + +// This is the default, see shareddefs.h for mod-specific value, which can override this +#define DEFAULT_TICK_INTERVAL (1.0f / 60.0f) // 16.666667 msec is the default +#define MINIMUM_TICK_INTERVAL (0.001) +#define MAXIMUM_TICK_INTERVAL (0.1) + +// This is the max # of players the engine can handle +// Note, must be power of 2 +#define ABSOLUTE_PLAYER_LIMIT 64 +#define ABSOLUTE_PLAYER_LIMIT_DW ( (ABSOLUTE_PLAYER_LIMIT/32) + 1 ) + +#if ABSOLUTE_PLAYER_LIMIT > 32 +#define CPlayerBitVec CBitVec +#else +#define CPlayerBitVec CDWordBitVec +#endif + +// a player name may have 31 chars + 0 on the PC. +// the 360 only allows 15 char + 0, but stick with the larger PC size for cross-platform communication +#define MAX_PLAYER_NAME_LENGTH 32 + +#ifdef _X360 +#define MAX_PLAYERS_PER_CLIENT XUSER_MAX_COUNT // Xbox 360 supports 4 players per console +#else +#define MAX_PLAYERS_PER_CLIENT 1 // One player per PC +#endif + +#define MAX_MAP_NAME 32 +#define MAX_NETWORKID_LENGTH 64 // num chars for a network (i.e steam) ID + +// BUGBUG: Reconcile with or derive this from the engine's internal definition! +// FIXME: I added an extra bit because I needed to make it signed +#define SP_MODEL_INDEX_BITS 11 + +// How many bits to use to encode an edict. +#define MAX_EDICT_BITS 11 // # of bits needed to represent max edicts +// Max # of edicts in a level +#define MAX_EDICTS (1<movetype values +enum MoveType_t +{ + MOVETYPE_NONE = 0, // never moves + MOVETYPE_ISOMETRIC, // For players -- in TF2 commander view, etc. + MOVETYPE_WALK, // Player only - moving on the ground + MOVETYPE_STEP, // gravity, special edge handling -- monsters use this + MOVETYPE_FLY, // No gravity, but still collides with stuff + MOVETYPE_FLYGRAVITY, // flies through the air + is affected by gravity + MOVETYPE_VPHYSICS, // uses VPHYSICS for simulation + MOVETYPE_PUSH, // no clip to world, push and crush + MOVETYPE_NOCLIP, // No gravity, no collisions, still do velocity/avelocity + MOVETYPE_LADDER, // Used by players only when going onto a ladder + MOVETYPE_OBSERVER, // Observer movement, depends on player's observer mode + MOVETYPE_CUSTOM, // Allows the entity to describe its own physics + + // should always be defined as the last item in the list + MOVETYPE_LAST = MOVETYPE_CUSTOM, + + MOVETYPE_MAX_BITS = 4 +}; + +// edict->movecollide values +enum MoveCollide_t +{ + MOVECOLLIDE_DEFAULT = 0, + + // These ones only work for MOVETYPE_FLY + MOVETYPE_FLYGRAVITY + MOVECOLLIDE_FLY_BOUNCE, // bounces, reflects, based on elasticity of surface and object - applies friction (adjust velocity) + MOVECOLLIDE_FLY_CUSTOM, // Touch() will modify the velocity however it likes + MOVECOLLIDE_FLY_SLIDE, // slides along surfaces (no bounce) - applies friciton (adjusts velocity) + + MOVECOLLIDE_COUNT, // Number of different movecollides + + // When adding new movecollide types, make sure this is correct + MOVECOLLIDE_MAX_BITS = 3 +}; + +// edict->solid values +// NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves +// SOLID only effects OTHER entities colliding with this one when they move - UGH! + +// Solid type basically describes how the bounding volume of the object is represented +// NOTE: SOLID_BBOX MUST BE 2, and SOLID_VPHYSICS MUST BE 6 +// NOTE: These numerical values are used in the FGD by the prop code (see prop_dynamic) +enum SolidType_t +{ + SOLID_NONE = 0, // no solid model + SOLID_BSP = 1, // a BSP tree + SOLID_BBOX = 2, // an AABB + SOLID_OBB = 3, // an OBB (not implemented yet) + SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw + SOLID_CUSTOM = 5, // Always call into the entity for tests + SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that + SOLID_LAST, +}; + +enum SolidFlags_t +{ + FSOLID_CUSTOMRAYTEST = 0x0001, // Ignore solid type + always call into the entity for ray tests + FSOLID_CUSTOMBOXTEST = 0x0002, // Ignore solid type + always call into the entity for swept box tests + FSOLID_NOT_SOLID = 0x0004, // Are we currently not solid? + FSOLID_TRIGGER = 0x0008, // This is something may be collideable but fires touch functions + // even when it's not collideable (when the FSOLID_NOT_SOLID flag is set) + FSOLID_NOT_STANDABLE = 0x0010, // You can't stand on this + FSOLID_VOLUME_CONTENTS = 0x0020, // Contains volumetric contents (like water) + FSOLID_FORCE_WORLD_ALIGNED = 0x0040, // Forces the collision rep to be world-aligned even if it's SOLID_BSP or SOLID_VPHYSICS + FSOLID_USE_TRIGGER_BOUNDS = 0x0080, // Uses a special trigger bounds separate from the normal OBB + FSOLID_ROOT_PARENT_ALIGNED = 0x0100, // Collisions are defined in root parent's local coordinate space + FSOLID_TRIGGER_TOUCH_DEBRIS = 0x0200, // This trigger will touch debris objects + FSOLID_TRIGGER_TOUCH_PLAYER = 0x0400, // This trigger will touch only players + FSOLID_NOT_MOVEABLE = 0x0800, // Assume this object will not move + + FSOLID_MAX_BITS = 12 +}; + +//----------------------------------------------------------------------------- +// A couple of inline helper methods +//----------------------------------------------------------------------------- +inline bool IsSolid( SolidType_t solidType, int nSolidFlags ) +{ + return (solidType != SOLID_NONE) && ((nSolidFlags & FSOLID_NOT_SOLID) == 0); +} + + +// m_lifeState values +#define LIFE_ALIVE 0 // alive +#define LIFE_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground +#define LIFE_DEAD 2 // dead. lying still. +#define LIFE_RESPAWNABLE 3 +#define LIFE_DISCARDBODY 4 + +// entity effects +enum +{ + EF_BONEMERGE = 0x001, // Performs bone merge on client side + EF_BRIGHTLIGHT = 0x002, // DLIGHT centered at entity origin + EF_DIMLIGHT = 0x004, // player flashlight + EF_NOINTERP = 0x008, // don't interpolate the next frame + EF_NOSHADOW = 0x010, // Don't cast no shadow + EF_NODRAW = 0x020, // don't draw entity + EF_NORECEIVESHADOW = 0x040, // Don't receive no shadow + EF_BONEMERGE_FASTCULL = 0x080, // For use with EF_BONEMERGE. If this is set, then it places this ent's origin at its + // parent and uses the parent's bbox + the max extents of the aiment. + // Otherwise, it sets up the parent's bones every frame to figure out where to place + // the aiment, which is inefficient because it'll setup the parent's bones even if + // the parent is not in the PVS. + EF_ITEM_BLINK = 0x100, // blink an item so that the user notices it. + EF_PARENT_ANIMATES = 0x200, // always assume that the parent entity is animating + EF_MAX_BITS = 10 +}; + +#define EF_PARITY_BITS 3 +#define EF_PARITY_MASK ((1< +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void CheckHeap( void ) +{ +#ifdef USECRTMEMDEBUG + _CrtCheckMemory(); +#endif +} + +// undone: this needs to be somehow made to construct before everything else. +// see http://msdn.microsoft.com/library/periodic/period97/dembugs.htm for info +void InitCRTMemDebug( void ) +{ +#ifdef USECRTMEMDEBUG + _CrtSetDbgFlag( +// _CRTDBG_ALLOC_MEM_DF | + _CRTDBG_CHECK_ALWAYS_DF | + _CRTDBG_CHECK_CRT_DF | + _CRTDBG_DELAY_FREE_MEM_DF ); +#endif +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/crtmemdebug.h b/public/crtmemdebug.h new file mode 100644 index 0000000..04c5f7a --- /dev/null +++ b/public/crtmemdebug.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CRTMEMDEBUG_H +#define CRTMEMDEBUG_H +#pragma once + +#ifdef USECRTMEMDEBUG + +#include +#define MEMCHECK CheckHeap() +void CheckHeap( void ); + +#else + +#define MEMCHECK + +#endif + +void InitCRTMemDebug( void ); + + +#endif // CRTMEMDEBUG_H diff --git a/public/datacache/idatacache.h b/public/datacache/idatacache.h new file mode 100644 index 0000000..5653477 --- /dev/null +++ b/public/datacache/idatacache.h @@ -0,0 +1,567 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IDATACACHE_H +#define IDATACACHE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/dbg.h" +#include "appframework/iappsystem.h" +#include "tier3/tier3.h" + +class IDataCache; + +//----------------------------------------------------------------------------- +// +// Shared Data Cache API +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Support types and enums +//----------------------------------------------------------------------------- + +//--------------------------------------------------------- +// Unique (per section) identifier for a cache item defined by client +//--------------------------------------------------------- +typedef uint32 DataCacheClientID_t; + + +//--------------------------------------------------------- +// Cache-defined handle for a cache item +//--------------------------------------------------------- +FORWARD_DECLARE_HANDLE( memhandle_t ); +typedef memhandle_t DataCacheHandle_t; +#define DC_INVALID_HANDLE ((DataCacheHandle_t)0) + +//--------------------------------------------------------- +// Cache Limits +//--------------------------------------------------------- +struct DataCacheLimits_t +{ + DataCacheLimits_t( unsigned nMaxBytes = (unsigned)-1, unsigned nMaxItems = (unsigned)-1, unsigned nMinBytes = 0, unsigned nMinItems = 0 ) + : nMaxBytes(nMaxBytes), + nMaxItems(nMaxItems), + nMinBytes(nMinBytes), + nMinItems(nMinItems) + { + } + + // Maximum levels permitted + unsigned nMaxBytes; + unsigned nMaxItems; + + // Minimum levels permitted + unsigned nMinBytes; + unsigned nMinItems; +}; + +//--------------------------------------------------------- +// Cache status +//--------------------------------------------------------- +struct DataCacheStatus_t +{ + // Current state of the cache + unsigned nBytes; + unsigned nItems; + + unsigned nBytesLocked; + unsigned nItemsLocked; + + // Diagnostics + unsigned nFindRequests; + unsigned nFindHits; +}; + +//--------------------------------------------------------- +// Cache options +//--------------------------------------------------------- +enum DataCacheOptions_t +{ + DC_TRACE_ACTIVITY = (1 << 0), + DC_FORCE_RELOCATE = (1 << 1), + DC_ALWAYS_MISS = (1 << 2), + DC_VALIDATE = (1 << 3), + DC_NO_USER_FORCE_FLUSH = (1 << 4) +}; + + +//--------------------------------------------------------- +// Cache report types +//--------------------------------------------------------- +enum DataCacheReportType_t +{ + DC_SUMMARY_REPORT, + DC_DETAIL_REPORT, + DC_DETAIL_REPORT_LRU, + DC_DETAIL_REPORT_VXCONSOLE, +}; + + +//--------------------------------------------------------- +// Notifications to section clients on cache events +//--------------------------------------------------------- +enum DataCacheNotificationType_t +{ + // Used internally to prohibit notifications + DC_NONE, + + // Item is falling off the LRU and should be deleted, return false to block + DC_AGE_DISCARD, + + // Item is being explicitly flushed and should be deleted, return false to block + DC_FLUSH_DISCARD, + + // Item is being explicitly removed and should be deleted. Failure is not an option + DC_REMOVED, + + // Cache is requesting item be relocated for debugging purposes + DC_RELOCATE, + + // Item info should be output to console, return false to accept default handling + DC_PRINT_INF0, +}; + +//------------------------------------- + +struct DataCacheNotification_t +{ + DataCacheNotificationType_t type; + const char * pszSectionName; + DataCacheClientID_t clientId; + const void * pItemData; + unsigned nItemSize; +}; + +//--------------------------------------------------------- + +const int DC_MAX_CLIENT_NAME = 15; +const int DC_MAX_ITEM_NAME = 511; + +//--------------------------------------------------------- +// Result codes +//--------------------------------------------------------- +enum DataCacheRemoveResult_t +{ + DC_OK, + DC_NOT_FOUND, + DC_LOCKED, +}; + +//--------------------------------------------------------- +// Add flags +//--------------------------------------------------------- +enum DataCacheAddFlags_t +{ + DCAF_LOCK = ( 1 << 0 ), + DCAF_DEFAULT = 0, +}; + + + +//----------------------------------------------------------------------------- +// IDataCacheSection +// +// Purpose: Implements a sub-section of the global cache. Subsections are +// areas of the cache with thier own memory constraints and common +// management. +//----------------------------------------------------------------------------- +abstract_class IDataCacheSection +{ +public: + //-------------------------------------------------------- + + virtual IDataCache *GetSharedCache() = 0; + virtual const char *GetName() = 0; + + //-------------------------------------------------------- + // Purpose: Controls cache size & options + //-------------------------------------------------------- + virtual void SetLimits( const DataCacheLimits_t &limits ) = 0; + virtual void SetOptions( unsigned options ) = 0; + + + //-------------------------------------------------------- + // Purpose: Get the current state of the section + //-------------------------------------------------------- + virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ) = 0; + + + //-------------------------------------------------------- + // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. + //-------------------------------------------------------- + virtual void EnsureCapacity( unsigned nBytes, unsigned nItems = 1 ) = 0; + + + //-------------------------------------------------------- + // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. + //-------------------------------------------------------- + virtual bool Add( DataCacheClientID_t clientId, const void *pItemData, unsigned size, DataCacheHandle_t *pHandle ) = 0; + + //-------------------------------------------------------- + // Purpose: Finds an item in the cache, returns NULL if item is not in cache. Not a cheap operation if section not configured for fast find. + //-------------------------------------------------------- + virtual DataCacheHandle_t Find( DataCacheClientID_t clientId ) = 0; + + + //-------------------------------------------------------- + // Purpose: Get an item out of the cache and remove it. No callbacks are executed unless explicity specified. + //-------------------------------------------------------- + virtual DataCacheRemoveResult_t Remove( DataCacheHandle_t handle, const void **ppItemData, unsigned *pItemSize = NULL, bool bNotify = false ) = 0; + DataCacheRemoveResult_t Remove( DataCacheHandle_t handle, bool bNotify = false ) { return Remove( handle, NULL, NULL, bNotify ); } + + + //-------------------------------------------------------- + // Purpose: Returns if the data is currently in memory, but does *not* change its location in the LRU + //-------------------------------------------------------- + virtual bool IsPresent( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Lock an item in the cache, returns NULL if item is not in the cache. + //-------------------------------------------------------- + virtual void *Lock( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Unlock a previous lock. + //-------------------------------------------------------- + virtual int Unlock( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Get an item without locking it, returns NULL if item is not in the cache. Use with care! + //-------------------------------------------------------- + virtual void *Get( DataCacheHandle_t handle, bool bFrameLock = false ) = 0; + virtual void *GetNoTouch( DataCacheHandle_t handle, bool bFrameLock = false ) = 0; + + //-------------------------------------------------------- + // Purpose: "Frame locking" (not game frame). A crude way to manage locks over relatively + // short periods. Does not affect normal locks/unlocks + //-------------------------------------------------------- + virtual int BeginFrameLocking() = 0; + virtual bool IsFrameLocking() = 0; + virtual void *FrameLock( DataCacheHandle_t handle ) = 0; + virtual int EndFrameLocking() = 0; + virtual int *GetFrameUnlockCounterPtr() = 0; + + + //-------------------------------------------------------- + // Purpose: Lock management, not for the feint of heart + //-------------------------------------------------------- + virtual int GetLockCount( DataCacheHandle_t handle ) = 0; + virtual int BreakLock( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Explicitly mark an item as "recently used" + //-------------------------------------------------------- + virtual bool Touch( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Explicitly mark an item as "least recently used". + //-------------------------------------------------------- + virtual bool Age( DataCacheHandle_t handle ) = 0; + + + //-------------------------------------------------------- + // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified + //-------------------------------------------------------- + virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ) = 0; + + + //-------------------------------------------------------- + // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed + //-------------------------------------------------------- + virtual unsigned Purge( unsigned nBytes ) = 0; + + + //-------------------------------------------------------- + // Purpose: Output the state of the section + //-------------------------------------------------------- + virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT ) = 0; + + //-------------------------------------------------------- + // Purpose: Updates the size used by a specific item (locks the item, kicks + // other items out to make room as necessary, unlocks the item). + //-------------------------------------------------------- + virtual void UpdateSize( DataCacheHandle_t handle, unsigned int nNewSize ) = 0; + + + //-------------------------------------------------------- + // Purpose: Access to the mutex. More explicit control during get-then-lock sequences + // to ensure object stays valid during "then" + //-------------------------------------------------------- + virtual void LockMutex() = 0; + virtual void UnlockMutex() = 0; + + //-------------------------------------------------------- + // Purpose: Add an item to the cache. Purges old items if over budget, returns false if item was already in cache. + //-------------------------------------------------------- + virtual bool AddEx( DataCacheClientID_t clientId, const void *pItemData, unsigned size, unsigned flags, DataCacheHandle_t *pHandle ) = 0; + + virtual unsigned int GetOptions() = 0; + + // Batch oriented get/lock + virtual void GetAndLockMultiple( void **ppData, int nCount, DataCacheHandle_t *pHandles ) = 0; +}; + + +//----------------------------------------------------------------------------- +// IDataCacheClient +// +// Purpose: Connection between the cache and the owner of a cache section +// +//----------------------------------------------------------------------------- +abstract_class IDataCacheClient +{ +public: + //-------------------------------------------------------- + // + //-------------------------------------------------------- + virtual bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) = 0; + + + //-------------------------------------------------------- + // + //-------------------------------------------------------- + virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen ) = 0; +}; + +//------------------------------------- + +class CDefaultDataCacheClient : public IDataCacheClient +{ +public: + virtual bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) + { + switch ( notification.type ) + { + case DC_AGE_DISCARD: + case DC_FLUSH_DISCARD: + case DC_REMOVED: + Assert ( 0 ); + return false; + } + return false; + } + + virtual bool GetItemName( DataCacheClientID_t clientId, const void *pItem, char *pDest, unsigned nMaxLen ) + { + return false; + } +}; + + +//----------------------------------------------------------------------------- +// IDataCache +// +// Purpose: The global shared cache. Manages sections and overall budgets. +// +//----------------------------------------------------------------------------- +abstract_class IDataCache : public IAppSystem +{ +public: + //-------------------------------------------------------- + // Purpose: Controls cache size. + //-------------------------------------------------------- + virtual void SetSize( int nMaxBytes ) = 0; + virtual void SetOptions( unsigned options ) = 0; + virtual void SetSectionLimits( const char *pszSectionName, const DataCacheLimits_t &limits ) = 0; + + + //-------------------------------------------------------- + // Purpose: Get the current state of the cache + //-------------------------------------------------------- + virtual void GetStatus( DataCacheStatus_t *pStatus, DataCacheLimits_t *pLimits = NULL ) = 0; + + + //-------------------------------------------------------- + // Purpose: Add a section to the cache + //-------------------------------------------------------- + virtual IDataCacheSection *AddSection( IDataCacheClient *pClient, const char *pszSectionName, const DataCacheLimits_t &limits = DataCacheLimits_t(), bool bSupportFastFind = false ) = 0; + + + //-------------------------------------------------------- + // Purpose: Remove a section from the cache + //-------------------------------------------------------- + virtual void RemoveSection( const char *pszClientName, bool bCallFlush = true ) = 0; + void RemoveSection( IDataCacheSection *pSection, bool bCallFlush = true ) { if ( pSection) RemoveSection( pSection->GetName() ); } + + + //-------------------------------------------------------- + // Purpose: Find a section of the cache + //-------------------------------------------------------- + virtual IDataCacheSection *FindSection( const char *pszClientName ) = 0; + + //-------------------------------------------------------- + // Purpose: Dump the oldest items to free the specified amount of memory. Returns amount actually freed + //-------------------------------------------------------- + virtual unsigned Purge( unsigned nBytes ) = 0; + + + //-------------------------------------------------------- + // Purpose: Empty the cache. Returns bytes released, will remove locked items if force specified + //-------------------------------------------------------- + virtual unsigned Flush( bool bUnlockedOnly = true, bool bNotify = true ) = 0; + + + //-------------------------------------------------------- + // Purpose: Output the state of the cache + //-------------------------------------------------------- + virtual void OutputReport( DataCacheReportType_t reportType = DC_SUMMARY_REPORT, const char *pszSection = NULL ) = 0; + + virtual int GetSectionCount() = 0; + virtual const char *GetSectionName( int iIndex ) = 0; +}; + +//----------------------------------------------------------------------------- +// Helper class to support usage pattern similar to CDataManager +//----------------------------------------------------------------------------- + +template< class STORAGE_TYPE, class CREATE_PARAMS, class LOCK_TYPE = STORAGE_TYPE * > +class CManagedDataCacheClient : public CDefaultDataCacheClient +{ +public: + typedef CManagedDataCacheClient CCacheClientBaseClass; + + CManagedDataCacheClient() + : m_pCache( NULL ) + { + } + + void Init( IDataCache *pSharedCache, const char *pszSectionName, const DataCacheLimits_t &limits = DataCacheLimits_t(), bool bSupportFastFind = false ) + { + if ( !m_pCache ) + { + m_pCache = pSharedCache->AddSection( this, pszSectionName, limits, bSupportFastFind ); + } + } + + void Shutdown() + { + if ( m_pCache ) + { + m_pCache->GetSharedCache()->RemoveSection( m_pCache ); + m_pCache = NULL; + } + } + + LOCK_TYPE CacheGet( DataCacheHandle_t handle, bool bFrameLock = true ) + { + return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->Get( handle, bFrameLock ))->GetData()); + } + + void CacheGetAndLockMultiple( LOCK_TYPE *pData, int nCount, DataCacheHandle_t *pHandles ) + { + m_pCache->GetAndLockMultiple( (void**)pData, nCount, pHandles ); + for ( int i = 0; i < nCount; ++i ) + { + STORAGE_TYPE *pTypedData = pData[i]; + if ( pTypedData ) + { + pData[i] = (LOCK_TYPE)( pTypedData->GetData() ); + } + } + } + + LOCK_TYPE CacheGetNoTouch( DataCacheHandle_t handle ) + { + return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->GetNoTouch( handle ))->GetData()); + } + + LOCK_TYPE CacheLock( DataCacheHandle_t handle ) + { + return (LOCK_TYPE)(((STORAGE_TYPE *)m_pCache->Lock( handle ))->GetData()); + } + + int CacheUnlock( DataCacheHandle_t handle ) + { + return m_pCache->Unlock( handle ); + } + + void CacheTouch( DataCacheHandle_t handle ) + { + m_pCache->Touch( handle ); + } + + void CacheRemove( DataCacheHandle_t handle, bool bNotify = true ) + { + m_pCache->Remove( handle, bNotify ); + } + + void CacheFlush() + { + m_pCache->Flush(); + } + + DataCacheHandle_t CacheCreate( const CREATE_PARAMS &createParams, unsigned flags = DCAF_DEFAULT ) + { + m_pCache->EnsureCapacity(STORAGE_TYPE::EstimatedSize(createParams)); + STORAGE_TYPE *pStore = STORAGE_TYPE::CreateResource( createParams ); + if ( !pStore ) + { + return DC_INVALID_HANDLE; + } + DataCacheHandle_t handle; + m_pCache->AddEx( (DataCacheClientID_t)pStore, pStore, pStore->Size(), flags, &handle); + return handle; + } + + void CacheLockMutex() + { + m_pCache->LockMutex(); + } + + void CacheUnlockMutex() + { + m_pCache->UnlockMutex(); + } + + bool HandleCacheNotification( const DataCacheNotification_t ¬ification ) + { + switch ( notification.type ) + { + case DC_AGE_DISCARD: + case DC_FLUSH_DISCARD: + case DC_REMOVED: + STORAGE_TYPE *p = (STORAGE_TYPE *)notification.clientId; + p->DestroyResource(); + return true; + } + + return CDefaultDataCacheClient::HandleCacheNotification( notification ); + } + + +protected: + + ~CManagedDataCacheClient() + { + Shutdown(); + } + + IDataCacheSection *GetCacheSection() + { + return m_pCache; + } + +private: + IDataCacheSection *m_pCache; + +}; + +//----------------------------------------------------------------------------- + +#endif // IDataCache diff --git a/public/datacache/imdlcache.h b/public/datacache/imdlcache.h new file mode 100644 index 0000000..f58f231 --- /dev/null +++ b/public/datacache/imdlcache.h @@ -0,0 +1,258 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// model loading and caching +// +//============================================================================= + +#ifndef IMDLCACHE_H +#define IMDLCACHE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/iappsystem.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct studiohdr_t; +struct studiohwdata_t; +struct vcollide_t; +struct virtualmodel_t; +struct vertexFileHeader_t; + +namespace OptimizedModel +{ + struct FileHeader_t; +} + + +//----------------------------------------------------------------------------- +// Reference to a loaded studiomdl +//----------------------------------------------------------------------------- +typedef unsigned short MDLHandle_t; + +inline MDLHandle_t VoidPtrToMDLHandle( void *ptr ) +{ + return (MDLHandle_t)(int)ptr; +} + +enum +{ + MDLHANDLE_INVALID = (MDLHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Cache data types +//----------------------------------------------------------------------------- +enum MDLCacheDataType_t +{ + // Callbacks to get called when data is loaded or unloaded for these: + MDLCACHE_STUDIOHDR = 0, + MDLCACHE_STUDIOHWDATA, + MDLCACHE_VCOLLIDE, + + // Callbacks NOT called when data is loaded or unloaded for these: + MDLCACHE_ANIMBLOCK, + MDLCACHE_VIRTUALMODEL, + MDLCACHE_VERTEXES, + MDLCACHE_DECODEDANIMBLOCK +}; + +abstract_class IMDLCacheNotify +{ +public: + // Called right after the data is loaded + virtual void OnDataLoaded( MDLCacheDataType_t type, MDLHandle_t handle ) = 0; + + // Called right before the data is unloaded + virtual void OnDataUnloaded( MDLCacheDataType_t type, MDLHandle_t handle ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Flags for flushing +//----------------------------------------------------------------------------- +enum MDLCacheFlush_t +{ + MDLCACHE_FLUSH_STUDIOHDR = 0x01, + MDLCACHE_FLUSH_STUDIOHWDATA = 0x02, + MDLCACHE_FLUSH_VCOLLIDE = 0x04, + MDLCACHE_FLUSH_ANIMBLOCK = 0x08, + MDLCACHE_FLUSH_VIRTUALMODEL = 0x10, + MDLCACHE_FLUSH_AUTOPLAY = 0x20, + MDLCACHE_FLUSH_VERTEXES = 0x40, + + MDLCACHE_FLUSH_IGNORELOCK = 0x80000000, + MDLCACHE_FLUSH_ALL = 0xFFFFFFFF +}; + + +//----------------------------------------------------------------------------- +// The main MDL cacher +//----------------------------------------------------------------------------- +abstract_class IMDLCache : public IAppSystem +{ +public: + // Used to install callbacks for when data is loaded + unloaded + // Returns the prior notify + virtual void SetCacheNotify( IMDLCacheNotify *pNotify ) = 0; + + // NOTE: This assumes the "GAME" path if you don't use + // the UNC method of specifying files. This will also increment + // the reference count of the MDL + virtual MDLHandle_t FindMDL( const char *pMDLRelativePath ) = 0; + + // Reference counting + virtual int AddRef( MDLHandle_t handle ) = 0; + virtual int Release( MDLHandle_t handle ) = 0; + virtual int GetRef( MDLHandle_t handle ) = 0; + + // Gets at the various data associated with a MDL + virtual studiohdr_t *GetStudioHdr( MDLHandle_t handle ) = 0; + virtual studiohwdata_t *GetHardwareData( MDLHandle_t handle ) = 0; + virtual vcollide_t *GetVCollide( MDLHandle_t handle ) = 0; + virtual unsigned char *GetAnimBlock( MDLHandle_t handle, int nBlock ) = 0; + virtual virtualmodel_t *GetVirtualModel( MDLHandle_t handle ) = 0; + virtual int GetAutoplayList( MDLHandle_t handle, unsigned short **pOut ) = 0; + virtual vertexFileHeader_t *GetVertexData( MDLHandle_t handle ) = 0; + + // Brings all data associated with an MDL into memory + virtual void TouchAllData( MDLHandle_t handle ) = 0; + + // Gets/sets user data associated with the MDL + virtual void SetUserData( MDLHandle_t handle, void* pData ) = 0; + virtual void *GetUserData( MDLHandle_t handle ) = 0; + + // Is this MDL using the error model? + virtual bool IsErrorModel( MDLHandle_t handle ) = 0; + + // Flushes the cache, force a full discard + virtual void Flush( MDLCacheFlush_t nFlushFlags = MDLCACHE_FLUSH_ALL ) = 0; + + // Flushes a particular model out of memory + virtual void Flush( MDLHandle_t handle, int nFlushFlags = MDLCACHE_FLUSH_ALL ) = 0; + + // Returns the name of the model (its relative path) + virtual const char *GetModelName( MDLHandle_t handle ) = 0; + + // faster access when you already have the studiohdr + virtual virtualmodel_t *GetVirtualModelFast( const studiohdr_t *pStudioHdr, MDLHandle_t handle ) = 0; + + // all cache entries that subsequently allocated or successfully checked + // are considered "locked" and will not be freed when additional memory is needed + virtual void BeginLock() = 0; + + // reset all protected blocks to normal + virtual void EndLock() = 0; + + // returns a pointer to a counter that is incremented every time the cache has been out of the locked state (EVIL) + virtual int *GetFrameUnlockCounterPtrOLD() = 0; + + // Finish all pending async operations + virtual void FinishPendingLoads() = 0; + + virtual vcollide_t *GetVCollideEx( MDLHandle_t handle, bool synchronousLoad = true ) = 0; + virtual bool GetVCollideSize( MDLHandle_t handle, int *pVCollideSize ) = 0; + + virtual bool GetAsyncLoad( MDLCacheDataType_t type ) = 0; + virtual bool SetAsyncLoad( MDLCacheDataType_t type, bool bAsync ) = 0; + + virtual void BeginMapLoad() = 0; + virtual void EndMapLoad() = 0; + virtual void MarkAsLoaded( MDLHandle_t handle ) = 0; + + virtual void InitPreloadData( bool rebuild ) = 0; + virtual void ShutdownPreloadData() = 0; + + virtual bool IsDataLoaded( MDLHandle_t handle, MDLCacheDataType_t type ) = 0; + + virtual int *GetFrameUnlockCounterPtr( MDLCacheDataType_t type ) = 0; + + virtual studiohdr_t *LockStudioHdr( MDLHandle_t handle ) = 0; + virtual void UnlockStudioHdr( MDLHandle_t handle ) = 0; + + virtual bool PreloadModel( MDLHandle_t handle ) = 0; + + // Hammer uses this. If a model has an error loading in GetStudioHdr, then it is flagged + // as an error model and any further attempts to load it will just get the error model. + // That is, until you call this function. Then it will load the correct model. + virtual void ResetErrorModelStatus( MDLHandle_t handle ) = 0; + + virtual void MarkFrame() = 0; + + // Locking for things that we can lock over longer intervals than + // resources locked by BeginLock/EndLock + virtual void BeginCoarseLock() = 0; + virtual void EndCoarseLock() = 0; + + virtual void ReloadVCollide( MDLHandle_t handle ) = 0; +}; + +DECLARE_TIER3_INTERFACE( IMDLCache, g_pMDLCache ); + + +//----------------------------------------------------------------------------- +// Critical section helper code +//----------------------------------------------------------------------------- +class CMDLCacheCriticalSection +{ +public: + CMDLCacheCriticalSection( IMDLCache *pCache ) : m_pCache( pCache ) + { + m_pCache->BeginLock(); + } + + ~CMDLCacheCriticalSection() + { + m_pCache->EndLock(); + } + +private: + IMDLCache *m_pCache; +}; + +class CMDLCacheCoarseCriticalSection +{ +public: + CMDLCacheCoarseCriticalSection( IMDLCache *pCache ) : m_pCache( pCache ) + { + m_pCache->BeginCoarseLock(); + } + + ~CMDLCacheCoarseCriticalSection() + { + m_pCache->EndCoarseLock(); + } + +private: + IMDLCache *m_pCache; +}; + +#define MDCACHE_FINE_GRAINED 1 + +#if defined(MDCACHE_FINE_GRAINED) +#define MDLCACHE_CRITICAL_SECTION_( pCache ) CMDLCacheCriticalSection cacheCriticalSection(pCache) +#define MDLCACHE_COARSE_LOCK_( pCache ) CMDLCacheCoarseCriticalSection cacheCoarseCriticalSection(pCache) +#elif defined(MDLCACHE_LEVEL_LOCKED) +#define MDLCACHE_CRITICAL_SECTION_( pCache ) ((void)(0)) +#define MDLCACHE_COARSE_LOCK_( pCache ) ((void)(0)) +#else +#define MDLCACHE_CRITICAL_SECTION_( pCache ) ((void)(0)) +#define MDLCACHE_COARSE_LOCK_( pCache ) CMDLCacheCriticalSection cacheCriticalSection(pCache); CMDLCacheCoarseCriticalSection cacheCoarseCriticalSection(pCache) +#endif +#define MDLCACHE_CRITICAL_SECTION() MDLCACHE_CRITICAL_SECTION_(mdlcache) +#define MDLCACHE_COARSE_LOCK() MDLCACHE_COARSE_LOCK_(mdlcache) + +#endif // IMDLCACHE_H + diff --git a/public/datacache/iprecachesystem.h b/public/datacache/iprecachesystem.h new file mode 100644 index 0000000..fa21075 --- /dev/null +++ b/public/datacache/iprecachesystem.h @@ -0,0 +1,51 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IPRECACHESYSTEM_H +#define IPRECACHESYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier2/tier2.h" +#include "tier2/resourceprecacher.h" +#include "appframework/iappsystem.h" + +//----------------------------------------------------------------------------- +// Resource access control API +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// IResourceAccessControl +// Purpose: Maintains lists of resources to use them as filters to prevent access +// to ensure proper precache behavior in game code +//----------------------------------------------------------------------------- +abstract_class IPrecacheSystem : public IAppSystem +{ +public: + // Precaches/uncaches all resources used by a particular system + virtual void Cache( IPrecacheHandler *pPrecacheHandler, PrecacheSystem_t nSystem, + const char *pName, bool bPrecache, ResourceList_t hResourceList, bool bBuildResourceList ) = 0; + + virtual void UncacheAll( IPrecacheHandler *pPrecacheHandler ) = 0 ; + + virtual void Register( IResourcePrecacher *pResourcePrecacherFirst, PrecacheSystem_t nSystem ) = 0; + + // Limits resource access to only resources used by this particular system + // Use GLOBAL system, and NULL name to disable limited resource access + virtual void LimitResourceAccess( PrecacheSystem_t nSystem, const char *pName ) = 0; + virtual void EndLimitedResourceAccess() = 0; +}; + +DECLARE_TIER2_INTERFACE( IPrecacheSystem, g_pPrecacheSystem ); + + +#endif // IPRECACHESYSTEM_H diff --git a/public/datacache/iresourceaccesscontrol.h b/public/datacache/iresourceaccesscontrol.h new file mode 100644 index 0000000..497731e --- /dev/null +++ b/public/datacache/iresourceaccesscontrol.h @@ -0,0 +1,50 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IRESOURCEACCESSCONTROL_H +#define IRESOURCEACCESSCONTROL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/dbg.h" +#include "tier2/resourceprecacher.h" +#include "appframework/iappsystem.h" + +//----------------------------------------------------------------------------- +// Resource access control API +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// IResourceAccessControl +// Purpose: Maintains lists of resources to use them as filters to prevent access +// to ensure proper precache behavior in game code +//----------------------------------------------------------------------------- +abstract_class IResourceAccessControl : public IAppSystem +{ +public: + // Creates, destroys a resource list + virtual ResourceList_t CreateResourceList( const char *pDebugName ) = 0; + virtual void DestroyAllResourceLists( ) = 0; + + // Adds a resource to a resource list + virtual void AddResource( ResourceList_t hResourceList, ResourceTypeOld_t nType, const char *pResourceName ) = 0; + + // Prevents access to anything except the specified resource list + // Pass RESOURCE_LIST_INVALID to allow access to all resources + virtual void LimitAccess( ResourceList_t hResourceList ) = 0; + + // Is access to this resource allowed? + virtual bool IsAccessAllowed( ResourceTypeOld_t nType, const char *pResource ) = 0; +}; + + +#endif // IRESOURCEACCESSCONTROL_H diff --git a/public/datamap.h b/public/datamap.h new file mode 100644 index 0000000..0049861 --- /dev/null +++ b/public/datamap.h @@ -0,0 +1,500 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DATAMAP_H +#define DATAMAP_H +#ifdef _WIN32 +#pragma once +#endif + +#ifndef VECTOR_H +#include "mathlib/vector.h" +#endif + +#include "tier1/utlvector.h" + +#include "tier0/memdbgon.h" + +// SINGLE_INHERITANCE restricts the size of CBaseEntity pointers-to-member-functions to 4 bytes +class SINGLE_INHERITANCE CBaseEntity; +struct inputdata_t; + +#define INVALID_TIME (FLT_MAX * -1.0) // Special value not rebased on save/load + +typedef enum _fieldtypes +{ + FIELD_VOID = 0, // No type or value + FIELD_FLOAT, // Any floating point value + FIELD_STRING, // A string ID (return from ALLOC_STRING) + FIELD_VECTOR, // Any vector, QAngle, or AngularImpulse + FIELD_QUATERNION, // A quaternion + FIELD_INTEGER, // Any integer or enum + FIELD_BOOLEAN, // boolean, implemented as an int, I may use this as a hint for compression + FIELD_SHORT, // 2 byte integer + FIELD_CHARACTER, // a byte + FIELD_COLOR32, // 8-bit per channel r,g,b,a (32bit color) + FIELD_EMBEDDED, // an embedded object with a datadesc, recursively traverse and embedded class/structure based on an additional typedescription + FIELD_CUSTOM, // special type that contains function pointers to it's read/write/parse functions + + FIELD_CLASSPTR, // CBaseEntity * + FIELD_EHANDLE, // Entity handle + FIELD_EDICT, // edict_t * + + FIELD_POSITION_VECTOR, // A world coordinate (these are fixed up across level transitions automagically) + FIELD_TIME, // a floating point time (these are fixed up automatically too!) + FIELD_TICK, // an integer tick count( fixed up similarly to time) + FIELD_MODELNAME, // Engine string that is a model name (needs precache) + FIELD_SOUNDNAME, // Engine string that is a sound name (needs precache) + + FIELD_INPUT, // a list of inputed data fields (all derived from CMultiInputVar) + FIELD_FUNCTION, // A class function pointer (Think, Use, etc) + + FIELD_VMATRIX, // a vmatrix (output coords are NOT worldspace) + + // NOTE: Use float arrays for local transformations that don't need to be fixed up. + FIELD_VMATRIX_WORLDSPACE,// A VMatrix that maps some local space to world space (translation is fixed up on level transitions) + FIELD_MATRIX3X4_WORLDSPACE, // matrix3x4_t that maps some local space to world space (translation is fixed up on level transitions) + + FIELD_INTERVAL, // a start and range floating point interval ( e.g., 3.2->3.6 == 3.2 and 0.4 ) + FIELD_MODELINDEX, // a model index + FIELD_MATERIALINDEX, // a material index (using the material precache string table) + + FIELD_VECTOR2D, // 2 floats + FIELD_INTEGER64, // 64bit integer + + FIELD_VECTOR4D, // 4 floats + + FIELD_TYPECOUNT, // MUST BE LAST +} fieldtype_t; + + +//----------------------------------------------------------------------------- +// Field sizes... +//----------------------------------------------------------------------------- +template +class CDatamapFieldSizeDeducer +{ +public: + enum + { + SIZE = 0 + }; + + static int FieldSize( ) + { + return 0; + } +}; + +#define DECLARE_FIELD_SIZE( _fieldType, _fieldSize ) \ + template< > class CDatamapFieldSizeDeducer<_fieldType> { public: enum { SIZE = _fieldSize }; static int FieldSize() { return _fieldSize; } }; +#define FIELD_SIZE( _fieldType ) CDatamapFieldSizeDeducer<_fieldType>::SIZE +#define FIELD_BITS( _fieldType ) (FIELD_SIZE( _fieldType ) * 8) + +DECLARE_FIELD_SIZE( FIELD_FLOAT, sizeof(float) ) +DECLARE_FIELD_SIZE( FIELD_STRING, sizeof(int) ) +DECLARE_FIELD_SIZE( FIELD_VECTOR, 3 * sizeof(float) ) +DECLARE_FIELD_SIZE( FIELD_VECTOR2D, 2 * sizeof(float) ) +DECLARE_FIELD_SIZE( FIELD_VECTOR4D, 4 * sizeof( float ) ) +DECLARE_FIELD_SIZE( FIELD_QUATERNION, 4 * sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_INTEGER, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_INTEGER64, sizeof(int64)) +DECLARE_FIELD_SIZE( FIELD_BOOLEAN, sizeof(char)) +DECLARE_FIELD_SIZE( FIELD_SHORT, sizeof(short)) +DECLARE_FIELD_SIZE( FIELD_CHARACTER, sizeof(char)) +DECLARE_FIELD_SIZE( FIELD_COLOR32, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_CLASSPTR, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_EHANDLE, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_EDICT, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_POSITION_VECTOR, 3 * sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_TIME, sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_TICK, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_MODELNAME, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_SOUNDNAME, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_INPUT, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_FUNCTION, sizeof(int *)) +DECLARE_FIELD_SIZE( FIELD_VMATRIX, 16 * sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_VMATRIX_WORLDSPACE, 16 * sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_MATRIX3X4_WORLDSPACE, 12 * sizeof(float)) +DECLARE_FIELD_SIZE( FIELD_INTERVAL, 2 * sizeof( float) ) // NOTE: Must match interval.h definition +DECLARE_FIELD_SIZE( FIELD_MODELINDEX, sizeof(int) ) +DECLARE_FIELD_SIZE( FIELD_MATERIALINDEX, sizeof(int) ) + + +#define ARRAYSIZE2D(p) (sizeof(p)/sizeof(p[0][0])) +#define SIZE_OF_ARRAY(p) _ARRAYSIZE(p) + +#define _FIELD(name,fieldtype,count,flags,mapname,tolerance) { fieldtype, #name, offsetof(classNameTypedef, name), count, flags, mapname, NULL, NULL, NULL, sizeof( ((classNameTypedef *)0)->name ), NULL, 0, tolerance } +#define DEFINE_FIELD_NULL { FIELD_VOID,0,0,0,0,0,0,0,0} +#define DEFINE_FIELD(name,fieldtype) _FIELD(name, fieldtype, 1, FTYPEDESC_SAVE, NULL, 0 ) +#define DEFINE_FIELD_NOT_SAVED(name,fieldtype) _FIELD(name, fieldtype, 1, 0, NULL, 0 ) + +#define DEFINE_KEYFIELD(name,fieldtype, mapname) _FIELD(name, fieldtype, 1, FTYPEDESC_KEY | FTYPEDESC_SAVE, mapname, 0 ) +#define DEFINE_KEYFIELD_NOT_SAVED(name,fieldtype, mapname)_FIELD(name, fieldtype, 1, FTYPEDESC_KEY, mapname, 0 ) +#define DEFINE_AUTO_ARRAY(name,fieldtype) _FIELD(name, fieldtype, SIZE_OF_ARRAY(((classNameTypedef *)0)->name), FTYPEDESC_SAVE, NULL, 0 ) +#define DEFINE_AUTO_ARRAY_KEYFIELD(name,fieldtype,mapname) _FIELD(name, fieldtype, SIZE_OF_ARRAY(((classNameTypedef *)0)->name), FTYPEDESC_SAVE, mapname, 0 ) +#define DEFINE_ARRAY(name,fieldtype, count) _FIELD(name, fieldtype, count, FTYPEDESC_SAVE, NULL, 0 ) +#define DEFINE_ARRAY_NOT_SAVED(name,fieldtype, count) _FIELD(name, fieldtype, count, 0, NULL, 0 ) +#define DEFINE_ENTITY_FIELD(name,fieldtype) _FIELD(edict_t, name, fieldtype, 1, FTYPEDESC_KEY | FTYPEDESC_SAVE, #name, 0 ) +#define DEFINE_ENTITY_GLOBAL_FIELD(name,fieldtype) _FIELD(edict_t, name, fieldtype, 1, FTYPEDESC_KEY | FTYPEDESC_SAVE | FTYPEDESC_GLOBAL, #name, 0 ) +#define DEFINE_GLOBAL_FIELD(name,fieldtype) _FIELD(name, fieldtype, 1, FTYPEDESC_GLOBAL | FTYPEDESC_SAVE, NULL, 0 ) +#define DEFINE_GLOBAL_KEYFIELD(name,fieldtype, mapname) _FIELD(name, fieldtype, 1, FTYPEDESC_GLOBAL | FTYPEDESC_KEY | FTYPEDESC_SAVE, mapname, 0 ) +#define DEFINE_CUSTOM_FIELD(name,datafuncs) { FIELD_CUSTOM, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE, NULL, datafuncs, NULL } +#define DEFINE_CUSTOM_KEYFIELD(name,datafuncs,mapname) { FIELD_CUSTOM, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, mapname, datafuncs, NULL } +#define DEFINE_AUTO_ARRAY2D(name,fieldtype) _FIELD(name, fieldtype, ARRAYSIZE2D(((classNameTypedef *)0)->name), FTYPEDESC_SAVE, NULL, 0 ) +// Used by byteswap datadescs +#define DEFINE_BITFIELD(name,fieldtype,bitcount) DEFINE_ARRAY(name,fieldtype,((bitcount+FIELD_BITS(fieldtype)-1)&~(FIELD_BITS(fieldtype)-1)) / FIELD_BITS(fieldtype) ) +#define DEFINE_INDEX(name,fieldtype) _FIELD(name, fieldtype, 1, FTYPEDESC_INDEX, NULL, 0 ) + +#define DEFINE_EMBEDDED( name ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE, NULL, NULL, NULL, &(((classNameTypedef *)0)->name.m_DataMap), sizeof( ((classNameTypedef *)0)->name ), NULL, 0, 0.0f } + +#define DEFINE_EMBEDDED_OVERRIDE( name, overridetype ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE, NULL, NULL, NULL, &((overridetype *)0)->m_DataMap, sizeof( ((classNameTypedef *)0)->name ), NULL, 0, 0.0f } + +#define DEFINE_EMBEDDEDBYREF( name ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE | FTYPEDESC_PTR, NULL, NULL, NULL, &(((classNameTypedef *)0)->name->m_DataMap), sizeof( *(((classNameTypedef *)0)->name) ), NULL, 0, 0.0f } + +#define DEFINE_EMBEDDED_ARRAY( name, count ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), count, FTYPEDESC_SAVE, NULL, NULL, NULL, &(((classNameTypedef *)0)->name->m_DataMap), sizeof( ((classNameTypedef *)0)->name[0] ), NULL, 0, 0.0f } + +#define DEFINE_EMBEDDED_AUTO_ARRAY( name ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), SIZE_OF_ARRAY( ((classNameTypedef *)0)->name ), FTYPEDESC_SAVE, NULL, NULL, NULL, &(((classNameTypedef *)0)->name->m_DataMap), sizeof( ((classNameTypedef *)0)->name[0] ), NULL, 0, 0.0f } + +#ifndef NO_ENTITY_PREDICTION + +// FTYPEDESC_KEY tells the prediction copy system to report the full nameof the field when reporting errors +#define DEFINE_PRED_TYPEDESCRIPTION( name, fieldtype ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE | FTYPEDESC_KEY, NULL, NULL, NULL, &fieldtype::m_PredMap } + +#define DEFINE_PRED_TYPEDESCRIPTION_PTR( name, fieldtype ) \ + { FIELD_EMBEDDED, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_SAVE | FTYPEDESC_PTR | FTYPEDESC_KEY, NULL, NULL, NULL, &fieldtype::m_PredMap } + +#else + +#define DEFINE_PRED_TYPEDESCRIPTION( name, fieldtype ) DEFINE_FIELD_NULL +#define DEFINE_PRED_TYPEDESCRIPTION_PTR( name, fieldtype ) DEFINE_FIELD_NULL + +#endif + +// Extensions to datamap.h macros for predicted entities only +#define DEFINE_PRED_FIELD(name,fieldtype, flags) _FIELD(name, fieldtype, 1, flags, NULL, 0.0f ) +#define DEFINE_PRED_ARRAY(name,fieldtype, count,flags) _FIELD(name, fieldtype, count, flags, NULL, 0.0f ) +#define DEFINE_FIELD_NAME(localname,netname,fieldtype) _FIELD(localname, fieldtype, 1, 0, #netname, 0.0f ) +// Predictable macros, which include a tolerance for floating point values... +#define DEFINE_PRED_FIELD_TOL(name,fieldtype, flags,tolerance) _FIELD(name, fieldtype, 1, flags, NULL, tolerance ) +#define DEFINE_PRED_ARRAY_TOL(name,fieldtype, count,flags,tolerance) _FIELD(name, fieldtype, count, flags, NULL, tolerance) +#define DEFINE_FIELD_NAME_TOL(localname,netname,fieldtolerance) _FIELD(localname, fieldtype, 1, 0, #netname, tolerance ) + +//#define DEFINE_DATA( name, fieldextname, flags ) _FIELD(name, fieldtype, 1, flags, extname ) + +// INPUTS +#define DEFINE_INPUT( name, fieldtype, inputname ) { fieldtype, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_INPUT | FTYPEDESC_SAVE | FTYPEDESC_KEY, inputname, NULL, NULL, NULL, sizeof( ((classNameTypedef *)0)->name ) } +#define DEFINE_INPUTFUNC( fieldtype, inputname, inputfunc ) { fieldtype, #inputfunc, NULL, 1, FTYPEDESC_INPUT, inputname, NULL, static_cast (&classNameTypedef::inputfunc) } + +// OUTPUTS +// the variable 'name' MUST BE derived from CBaseOutput +// we know the output type from the variable itself, so it doesn't need to be specified here +class ISaveRestoreOps; +extern ISaveRestoreOps *eventFuncs; +#define DEFINE_OUTPUT( name, outputname ) { FIELD_CUSTOM, #name, offsetof(classNameTypedef, name), 1, FTYPEDESC_OUTPUT | FTYPEDESC_SAVE | FTYPEDESC_KEY, outputname, eventFuncs } + +// replaces EXPORT table for portability and non-DLL based systems (xbox) +#define DEFINE_FUNCTION_RAW( function, func_type ) { FIELD_VOID, nameHolder.GenerateName(#function), NULL, 1, FTYPEDESC_FUNCTIONTABLE, NULL, NULL, (inputfunc_t)((func_type)(&classNameTypedef::function)) } +#define DEFINE_FUNCTION( function ) DEFINE_FUNCTION_RAW( function, inputfunc_t ) + + +#define FTYPEDESC_GLOBAL 0x0001 // This field is masked for global entity save/restore +#define FTYPEDESC_SAVE 0x0002 // This field is saved to disk +#define FTYPEDESC_KEY 0x0004 // This field can be requested and written to by string name at load time +#define FTYPEDESC_INPUT 0x0008 // This field can be written to by string name at run time, and a function called +#define FTYPEDESC_OUTPUT 0x0010 // This field propogates it's value to all targets whenever it changes +#define FTYPEDESC_FUNCTIONTABLE 0x0020 // This is a table entry for a member function pointer +#define FTYPEDESC_PTR 0x0040 // This field is a pointer, not an embedded object +#define FTYPEDESC_OVERRIDE 0x0080 // The field is an override for one in a base class (only used by prediction system for now) + +// Flags used by other systems (e.g., prediction system) +#define FTYPEDESC_INSENDTABLE 0x0100 // This field is present in a network SendTable +#define FTYPEDESC_PRIVATE 0x0200 // The field is local to the client or server only (not referenced by prediction code and not replicated by networking) +#define FTYPEDESC_NOERRORCHECK 0x0400 // The field is part of the prediction typedescription, but doesn't get compared when checking for errors + +#define FTYPEDESC_MODELINDEX 0x0800 // The field is a model index (used for debugging output) + +#define FTYPEDESC_INDEX 0x1000 // The field is an index into file data, used for byteswapping. + +// These flags apply to C_BasePlayer derived objects only +#define FTYPEDESC_VIEW_OTHER_PLAYER 0x2000 // By default you can only view fields on the local player (yourself), + // but if this is set, then we allow you to see fields on other players +#define FTYPEDESC_VIEW_OWN_TEAM 0x4000 // Only show this data if the player is on the same team as the local player +#define FTYPEDESC_VIEW_NEVER 0x8000 // Never show this field to anyone, even the local player (unusual) + +#define TD_MSECTOLERANCE 0.001f // This is a FIELD_FLOAT and should only be checked to be within 0.001 of the networked info + +struct typedescription_t; + + +class ISaveRestoreOps; + +// +// Function prototype for all input handlers. +// +typedef void (CBaseEntity::*inputfunc_t)(inputdata_t &data); + +struct datamap_t; +struct typedescription_t; + +enum +{ + PC_NON_NETWORKED_ONLY = 0, + PC_NETWORKED_ONLY, + + PC_COPYTYPE_COUNT, + PC_EVERYTHING = PC_COPYTYPE_COUNT, +}; + +enum +{ + TD_OFFSET_NORMAL = 0, + TD_OFFSET_PACKED = 1, + + // Must be last + TD_OFFSET_COUNT, +}; + +struct typedescription_t +{ + fieldtype_t fieldType; + const char *fieldName; + int fieldOffset; // Local offset value + unsigned short fieldSize; + short flags; + // the name of the variable in the map/fgd data, or the name of the action + const char *externalName; + // pointer to the function set for save/restoring of custom data types + ISaveRestoreOps *pSaveRestoreOps; + // for associating function with string names + inputfunc_t inputFunc; + // For embedding additional datatables inside this one + datamap_t *td; + + // Stores the actual member variable size in bytes + int fieldSizeInBytes; + + // FTYPEDESC_OVERRIDE point to first baseclass instance if chains_validated has occurred + struct typedescription_t *override_field; + + // Used to track exclusion of baseclass fields + int override_count; + + // Tolerance for field errors for float fields + float fieldTolerance; + + // For raw fields (including children of embedded stuff) this is the flattened offset + int flatOffset[ TD_OFFSET_COUNT ]; + unsigned short flatGroup; +}; + +// See predictioncopy.h for implementation and notes +struct optimized_datamap_t; + +//----------------------------------------------------------------------------- +// Purpose: stores the list of objects in the hierarchy +// used to iterate through an object's data descriptions +//----------------------------------------------------------------------------- +struct datamap_t +{ + typedescription_t *dataDesc; + int dataNumFields; + char const *dataClassName; + datamap_t *baseMap; + + int m_nPackedSize; + optimized_datamap_t *m_pOptimizedDataMap; + +#if defined( _DEBUG ) + bool bValidityChecked; +#endif // _DEBUG +}; + + +//----------------------------------------------------------------------------- +// +// Macros used to implement datadescs +// +#define DECLARE_FRIEND_DATADESC_ACCESS() \ + template friend void DataMapAccess(T *, datamap_t **p); \ + template friend datamap_t *DataMapInit(T *); + +#define DECLARE_SIMPLE_DATADESC() \ + static datamap_t m_DataMap; \ + static datamap_t *GetBaseMap(); \ + template friend void DataMapAccess(T *, datamap_t **p); \ + template friend datamap_t *DataMapInit(T *); + +#define DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE() \ + static datamap_t m_DataMap; \ + static datamap_t *GetBaseMap(); \ + template friend void ::DataMapAccess(T *, datamap_t **p); \ + template friend datamap_t *::DataMapInit(T *); + +#define DECLARE_DATADESC() \ + DECLARE_SIMPLE_DATADESC() \ + virtual datamap_t *GetDataDescMap( void ); + +#define BEGIN_DATADESC( className ) \ + datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \ + datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \ + datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((BaseClass *)NULL, &pResult); return pResult; } \ + BEGIN_DATADESC_GUTS( className ) + +#define BEGIN_DATADESC_NO_BASE( className ) \ + datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \ + datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \ + datamap_t *className::GetBaseMap() { return NULL; } \ + BEGIN_DATADESC_GUTS( className ) + +#define BEGIN_SIMPLE_DATADESC( className ) \ + datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \ + datamap_t *className::GetBaseMap() { return NULL; } \ + BEGIN_DATADESC_GUTS( className ) + +#define BEGIN_SIMPLE_DATADESC_( className, BaseClass ) \ + datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \ + datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((BaseClass *)NULL, &pResult); return pResult; } \ + BEGIN_DATADESC_GUTS( className ) + +#define BEGIN_DATADESC_GUTS( className ) \ + template datamap_t *DataMapInit(T *); \ + template <> datamap_t *DataMapInit( className * ); \ + namespace className##_DataDescInit \ + { \ + datamap_t *g_DataMapHolder = DataMapInit( (className *)NULL ); /* This can/will be used for some clean up duties later */ \ + } \ + \ + template <> datamap_t *DataMapInit( className * ) \ + { \ + typedef className classNameTypedef; \ + static CDatadescGeneratedNameHolder nameHolder(#className); \ + className::m_DataMap.baseMap = className::GetBaseMap(); \ + static typedescription_t dataDesc[] = \ + { \ + { FIELD_VOID,0,0,0,0,0,0,0,0}, /* so you can define "empty" tables */ + + +#define BEGIN_DATADESC_GUTS_NAMESPACE( className, nameSpace ) \ + template datamap_t *nameSpace::DataMapInit(T *); \ + template <> datamap_t *nameSpace::DataMapInit( className * ); \ + namespace className##_DataDescInit \ + { \ + datamap_t *g_DataMapHolder = nameSpace::DataMapInit( (className *)NULL ); /* This can/will be used for some clean up duties later */ \ + } \ + \ + template <> datamap_t *nameSpace::DataMapInit( className * ) \ + { \ + typedef className classNameTypedef; \ + static CDatadescGeneratedNameHolder nameHolder(#className); \ + className::m_DataMap.baseMap = className::GetBaseMap(); \ + static typedescription_t dataDesc[] = \ + { \ + { FIELD_VOID,0,0,0,0,0,0,0,0}, /* so you can define "empty" tables */ + +#define END_DATADESC() \ + }; \ + \ + if ( sizeof( dataDesc ) > sizeof( dataDesc[0] ) ) \ + { \ + classNameTypedef::m_DataMap.dataNumFields = SIZE_OF_ARRAY( dataDesc ) - 1; \ + classNameTypedef::m_DataMap.dataDesc = &dataDesc[1]; \ + } \ + else \ + { \ + classNameTypedef::m_DataMap.dataNumFields = 1; \ + classNameTypedef::m_DataMap.dataDesc = dataDesc; \ + } \ + return &classNameTypedef::m_DataMap; \ + } + +// used for when there is no data description +#define IMPLEMENT_NULL_SIMPLE_DATADESC( derivedClass ) \ + BEGIN_SIMPLE_DATADESC( derivedClass ) \ + END_DATADESC() + +#define IMPLEMENT_NULL_SIMPLE_DATADESC_( derivedClass, baseClass ) \ + BEGIN_SIMPLE_DATADESC_( derivedClass, baseClass ) \ + END_DATADESC() + +#define IMPLEMENT_NULL_DATADESC( derivedClass ) \ + BEGIN_DATADESC( derivedClass ) \ + END_DATADESC() + +// helps get the offset of a bitfield +#define BEGIN_BITFIELD( name ) \ + union \ + { \ + char name; \ + struct \ + { + +#define END_BITFIELD() \ + }; \ + }; + +//----------------------------------------------------------------------------- +// Forward compatability with potential seperate byteswap datadescs + +#define DECLARE_BYTESWAP_DATADESC() DECLARE_SIMPLE_DATADESC() +#define BEGIN_BYTESWAP_DATADESC(name) BEGIN_SIMPLE_DATADESC(name) +#define BEGIN_BYTESWAP_DATADESC_(name,base) BEGIN_SIMPLE_DATADESC_(name,base) +#define END_BYTESWAP_DATADESC() END_DATADESC() + +//----------------------------------------------------------------------------- + +template +inline void DataMapAccess(T *ignored, datamap_t **p) +{ + *p = &T::m_DataMap; +} + +//----------------------------------------------------------------------------- + +class CDatadescGeneratedNameHolder +{ +public: + CDatadescGeneratedNameHolder( const char *pszBase ) + : m_pszBase(pszBase) + { + m_nLenBase = strlen( m_pszBase ); + } + + ~CDatadescGeneratedNameHolder() + { + for ( int i = 0; i < m_Names.Count(); i++ ) + { + delete m_Names[i]; + } + } + + const char *GenerateName( const char *pszIdentifier ) + { + char *pBuf = new char[m_nLenBase + strlen(pszIdentifier) + 1]; + strcpy( pBuf, m_pszBase ); + strcat( pBuf, pszIdentifier ); + m_Names.AddToTail( pBuf ); + return pBuf; + } + +private: + const char *m_pszBase; + size_t m_nLenBase; + CUtlVector m_Names; +}; + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // DATAMAP_H diff --git a/public/datamodel/attributeflags.h b/public/datamodel/attributeflags.h new file mode 100644 index 0000000..9ba43db --- /dev/null +++ b/public/datamodel/attributeflags.h @@ -0,0 +1,32 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ATTRIBUTEFLAGS_H +#define ATTRIBUTEFLAGS_H + +#ifdef _WIN32 +#pragma once +#endif + +enum +{ + // NOTE: The first 5 flags bits are reserved for attribute type + FATTRIB_TYPEMASK = 0x1F, + + FATTRIB_READONLY = (1<<5), // Don't allow editing value in editors + FATTRIB_DONTSAVE = (1<<6), // Don't persist to .dmx file + FATTRIB_DIRTY = (1<<7), // Indicates the attribute has been changed since the resolve phase + FATTRIB_HAS_CALLBACK = (1<<8), // Indicates that this will notify its owner and/or other elements when it changes + FATTRIB_EXTERNAL = (1<<9), // Indicates this attribute's data is externally owned (in a CDmElement somewhere) + FATTRIB_TOPOLOGICAL = (1<<10), // Indicates this attribute effects the scene's topology (ie it's an attribute name or element) + FATTRIB_MUSTCOPY = (1<<11), // parent element must make a new copy during CopyInto, even for shallow copy + FATTRIB_NEVERCOPY = (1<<12), // parent element shouldn't make a new copy during CopyInto, even for deep copy + FATTRIB_USERDEFINED = (1<<13), // This flag WAS used to sort attributes. Now it's used to guess whether vmtdoc and vmfentity attributes are shaderparams or entitykeys. TODO - remove this + FATTRIB_OPERATOR_DIRTY = (1<<14), // Used and cleared only by operator phase of datamodel + FATTRIB_HIDDEN = (1<<15), // shouldn't be shown in ui +}; + +#endif // ATTRIBUTEFLAGS_H diff --git a/public/datamodel/dmattribute.h b/public/datamodel/dmattribute.h new file mode 100644 index 0000000..36f3c6e --- /dev/null +++ b/public/datamodel/dmattribute.h @@ -0,0 +1,440 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMATTRIBUTE_H +#define DMATTRIBUTE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "datamodel/attributeflags.h" +#include "datamodel/idatamodel.h" +#include "datamodel/dmattributetypes.h" +#include "datamodel/dmvar.h" +#include "tier1/utlhash.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CDmElement; +class CUtlBuffer; + + +//----------------------------------------------------------------------------- +// Used for in-place modification +//----------------------------------------------------------------------------- +typedef void *DmAttributeModifyHandle_t; + + +//----------------------------------------------------------------------------- +// Purpose: A general purpose pAttribute. Eventually will be extensible to arbitrary user types +//----------------------------------------------------------------------------- +class CDmAttribute +{ +public: + // Returns the type + DmAttributeType_t GetType() const; + const char *GetTypeString() const; + template< class T > bool IsA() const; + + // ChangeType fails (and returns false) for external attributes (ones who's data is owned by their element) + bool ChangeType_UNSAFE( DmAttributeType_t type ); + + bool IsStandard() const; + + // Returns the name. NOTE: The utlsymbol + // can be turned into a string by using g_pDataModel->String(); + const char *GetName() const; + CUtlSymbolLarge GetNameSymbol() const; + void SetName( const char *newName ); + + // Gets the attribute value + // NOTE: GetValueUntyped is used with GetType() for use w/ SetValue( type, void* ) + template< class T > const T& GetValue() const; + template< class T > const T& GetValue( const T& defaultValue ) const; + const char *GetValueString() const; + template< class E > E *GetValueElement() const; + const void *GetValueUntyped() const; + + // Sets the attribute value + template< class T > void SetValue( const T &value ); + template< class E > void SetValue( E* pValue ); + void SetValue( const void *pValue, size_t nSize ); + + // Copies w/ type conversion (if possible) from another attribute + void SetValue( const CDmAttribute *pAttribute ); + void SetValue( CDmAttribute *pAttribute ); + void SetValue( DmAttributeType_t valueType, const void *pValue ); + + // Sets the attribute to its default value based on its type + void SetToDefaultValue(); + + // Convert to and from string + void SetValueFromString( const char *pValue ); + const char *GetValueAsString( char *pBuffer, size_t nBufLen ) const; + + // Used for in-place modification of the data; preferable to set value + // for large data like arrays or binary blocks + template< class T > T& BeginModifyValueInPlace( DmAttributeModifyHandle_t *pHandle ); + template< class T > void EndModifyValueInPlace( DmAttributeModifyHandle_t handle ); + + // Used for element and element array attributes; it specifies which type of + // elements are valid to be referred to by this attribute + void SetElementTypeSymbol( CUtlSymbolLarge typeSymbol ); + CUtlSymbolLarge GetElementTypeSymbol() const; + + // Returns the next attribute + CDmAttribute *NextAttribute(); + const CDmAttribute *NextAttribute() const; + + // Returns the owner + CDmElement *GetOwner(); + + // Methods related to flags + void AddFlag( int flags ); + void RemoveFlag( int flags ); + void ClearFlags(); + int GetFlags() const; + bool IsFlagSet( int flags ) const; + + // Serialization + bool Serialize( CUtlBuffer &buf ) const; + bool Unserialize( CUtlBuffer &buf ); + bool IsIdenticalToSerializedValue( CUtlBuffer &buf ) const; + + // Serialization of a single element. + // First version of UnserializeElement adds to tail if it worked + // Second version overwrites, but does not add, the element at the specified index + bool SerializeElement( int nElement, CUtlBuffer &buf ) const; + bool UnserializeElement( CUtlBuffer &buf ); + bool UnserializeElement( int nElement, CUtlBuffer &buf ); + + // Does this attribute serialize on multiple lines? + bool SerializesOnMultipleLines() const; + + // Get the attribute/create an attribute handle + DmAttributeHandle_t GetHandle( bool bCreate = true ); + + // estimate memory overhead + int EstimateMemoryUsage( TraversalDepth_t depth ) const; + +private: + // Class factory + static CDmAttribute *CreateAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName ); + static CDmAttribute *CreateExternalAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pExternalMemory ); + static void DestroyAttribute( CDmAttribute *pAttribute ); + + // Constructor, destructor + CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName ); + CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pMemory ); + ~CDmAttribute(); + + // Used when constructing CDmAttributes + void Init( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName ); + + // Used when shutting down, indicates DmAttributeHandle_t referring to this are invalid + void InvalidateHandle(); + + // Called when the attribute changes + void OnChanged( bool bArrayCountChanged = false, bool bIsTopological = false ); + + // Is modification allowed in this phase? + bool ModificationAllowed() const; + + // Mark the attribute as being dirty + bool MarkDirty(); + + // Is the data inline in a containing element class? + bool IsDataInline() const; + + // Allocates, frees internal data storage + void CreateAttributeData(); + void DeleteAttributeData(); + + // Gets at the internal data storage + void* GetAttributeData(); + const void* GetAttributeData() const; + template < class T > typename CDmAttributeInfo< T >::StorageType_t* GetData(); + template < class T > const typename CDmAttributeInfo< T >::StorageType_t* GetData() const; + template < class T > typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* GetArrayData(); + template < class T > const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* GetArrayData() const; + + // Used by CDmElement to manage the list of attributes it owns + CDmAttribute **GetNextAttributeRef(); + + // Implementational function used for memory consumption estimation computation + int EstimateMemoryUsageInternal( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) const; + + // Called by elements after unserialization of their attributes is complete + void OnUnserializationFinished(); + + template< class T > bool IsTypeConvertable() const; + template< class T > bool ShouldModify( const T& src ); + template< class T > void CopyData( const T& src ); + template< class T > void CopyDataOut( T& dest ) const; + +private: + CDmAttribute *m_pNext; + void *m_pData; + CDmElement *m_pOwner; + DmAttributeHandle_t m_Handle; + uint16 m_nFlags; + CUtlSymbolLarge m_Name; + + friend class CDmElement; + friend class CDmAttributeAccessor; + template< class T > friend class CDmrElementArray; + template< class E > friend class CDmrElementArrayConst; + template< class T > friend class CDmaArrayAccessor; + template< class T, class B > friend class CDmrDecorator; + template< class T, class B > friend class CDmrDecoratorConst; + template< class T > friend class CDmArrayAttributeOp; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline DmAttributeType_t CDmAttribute::GetType() const +{ + return (DmAttributeType_t)( m_nFlags & FATTRIB_TYPEMASK ); +} + +template< class T > inline bool CDmAttribute::IsA() const +{ + return GetType() == CDmAttributeInfo< T >::AttributeType(); +} + +inline const char *CDmAttribute::GetName() const +{ + return m_Name.String(); +} + +inline CUtlSymbolLarge CDmAttribute::GetNameSymbol() const +{ + return m_Name; +} + + +//----------------------------------------------------------------------------- +// Iteration +//----------------------------------------------------------------------------- +inline CDmAttribute *CDmAttribute::NextAttribute() +{ + return m_pNext; +} + +inline const CDmAttribute *CDmAttribute::NextAttribute() const +{ + return m_pNext; +} + + +//----------------------------------------------------------------------------- +// Returns the owner +//----------------------------------------------------------------------------- +inline CDmElement *CDmAttribute::GetOwner() +{ + return m_pOwner; +} + + +//----------------------------------------------------------------------------- +// Value getting methods +//----------------------------------------------------------------------------- +template< class T > +inline const T& CDmAttribute::GetValue( const T& defaultValue ) const +{ + if ( (int)GetType() == (int)CDmAttributeInfo< T >::ATTRIBUTE_TYPE ) + return *reinterpret_cast< const T* >( m_pData ); + + if ( IsTypeConvertable< T >() ) + { + static T tempVal; + CopyDataOut( tempVal ); + return tempVal; + } + + Assert( 0 ); + return defaultValue; +} + +template< class T > +inline const T& CDmAttribute::GetValue() const +{ + static CDmaVar< T > defaultVal; + return GetValue( defaultVal.Get() ); +} + +inline const char *CDmAttribute::GetValueString() const +{ + Assert( GetType() == AT_STRING ); + if ( GetType() != AT_STRING ) + return NULL; + + CUtlSymbolLarge symbol = GetValue< CUtlSymbolLarge >(); + return symbol.String(); +} + +// used with GetType() for use w/ SetValue( type, void* ) +inline const void* CDmAttribute::GetValueUntyped() const +{ + return m_pData; +} + +#ifndef POSIX +template< class E > +inline E* CDmAttribute::GetValueElement() const +{ + Assert( GetType() == AT_ELEMENT ); + if ( GetType() == AT_ELEMENT ) + return GetElement( this->GetValue( ) ); + return NULL; +} +#endif + + +//----------------------------------------------------------------------------- +// Value setting methods +//----------------------------------------------------------------------------- +template< class E > +inline void CDmAttribute::SetValue( E* pValue ) +{ + Assert( GetType() == AT_ELEMENT ); + if ( GetType() == AT_ELEMENT ) + { + SetValue( pValue ? pValue->GetHandle() : DMELEMENT_HANDLE_INVALID ); + } +} + +template<> +inline void CDmAttribute::SetValue( const char *pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + return SetValue( symbol ); +} + +template<> +inline void CDmAttribute::SetValue( char *pValue ) +{ + return SetValue( (const char *)pValue ); +} + +inline void CDmAttribute::SetValue( const void *pValue, size_t nSize ) +{ + CUtlBinaryBlock buf( pValue, nSize ); + return SetValue( buf ); +} + + +//----------------------------------------------------------------------------- +// Methods related to flags +//----------------------------------------------------------------------------- +inline void CDmAttribute::AddFlag( int nFlags ) +{ + m_nFlags |= ( nFlags & ~FATTRIB_TYPEMASK ); +} + +inline void CDmAttribute::RemoveFlag( int nFlags ) +{ + m_nFlags &= ~nFlags | FATTRIB_TYPEMASK; +} + +inline void CDmAttribute::ClearFlags() +{ + RemoveFlag( 0xffffffff ); +} + +inline int CDmAttribute::GetFlags() const +{ + return m_nFlags & ~FATTRIB_TYPEMASK; +} + +inline bool CDmAttribute::IsFlagSet( int nFlags ) const +{ + return ( nFlags & GetFlags() ) != 0; +} + +inline bool CDmAttribute::IsDataInline() const +{ + return !IsFlagSet( FATTRIB_EXTERNAL ); +} + + +//----------------------------------------------------------------------------- +// Gets at the internal data storage +//----------------------------------------------------------------------------- +inline void* CDmAttribute::GetAttributeData() +{ + return m_pData; +} + +inline const void* CDmAttribute::GetAttributeData() const +{ + return m_pData; +} + +template < class T > +inline typename CDmAttributeInfo< T >::StorageType_t* CDmAttribute::GetData() +{ + return ( typename CDmAttributeInfo< T >::StorageType_t* )m_pData; +} + +template < class T > +inline typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* CDmAttribute::GetArrayData() +{ + return ( typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* )m_pData; +} + +template < class T > +inline const typename CDmAttributeInfo< T >::StorageType_t* CDmAttribute::GetData() const +{ + return ( const typename CDmAttributeInfo< T >::StorageType_t* )m_pData; +} + +template < class T > +inline const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* CDmAttribute::GetArrayData() const +{ + return ( const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* )m_pData; +} + + +//----------------------------------------------------------------------------- +// Used by CDmElement to manage the list of attributes it owns +//----------------------------------------------------------------------------- +inline CDmAttribute **CDmAttribute::GetNextAttributeRef() +{ + return &m_pNext; +} + + +//----------------------------------------------------------------------------- +// helper function for determining which attributes/elements to traverse during copy/find/save/etc. +//----------------------------------------------------------------------------- +inline bool ShouldTraverse( const CDmAttribute *pAttr, TraversalDepth_t depth ) +{ + switch ( depth ) + { + case TD_NONE: + return false; + + case TD_SHALLOW: + if ( !pAttr->IsFlagSet( FATTRIB_MUSTCOPY ) ) + return false; + // fall-through intentional + case TD_DEEP: + if ( pAttr->IsFlagSet( FATTRIB_NEVERCOPY ) ) + return false; + // fall-through intentional + case TD_ALL: + return true; + } + + Assert( 0 ); + return false; +} + +#endif // DMATTRIBUTE_H diff --git a/public/datamodel/dmattributetypes.h b/public/datamodel/dmattributetypes.h new file mode 100644 index 0000000..de292b0 --- /dev/null +++ b/public/datamodel/dmattributetypes.h @@ -0,0 +1,351 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMATTRIBUTETYPES_H +#define DMATTRIBUTETYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" +#include "tier1/uniqueid.h" +#include "Color.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "mathlib/vmatrix.h" +#include "datamodel/dmelementhandle.h" +#include "tier1/utlsymbollarge.h" +#include "tier1/timeutils.h" + + +//----------------------------------------------------------------------------- +// Object Id +//----------------------------------------------------------------------------- +typedef UniqueId_t DmObjectId_t; + + +//----------------------------------------------------------------------------- +// Necessary for template specialization for AT_UNKNOWN +//----------------------------------------------------------------------------- +struct DmUnknownAttribute_t +{ + bool operator==( const DmUnknownAttribute_t& src ) const { return true; } +}; + + +//----------------------------------------------------------------------------- +// Element array +//----------------------------------------------------------------------------- +struct DmElementAttribute_t +{ + DmElementAttribute_t() : m_ElementType( UTL_INVAL_SYMBOL_LARGE ) {} + + operator DmElementHandle_t&() { return m_Handle; } + operator const DmElementHandle_t&() const { return m_Handle; } + + DmElementHandle_t m_Handle; + CUtlSymbolLarge m_ElementType; +}; + +struct DmElementArray_t : public CUtlVector< DmElementHandle_t > +{ + DmElementArray_t() : m_ElementType( UTL_INVAL_SYMBOL_LARGE ) {} + + CUtlSymbolLarge m_ElementType; +}; + + +//----------------------------------------------------------------------------- +// Attribute chunk type enum +//----------------------------------------------------------------------------- +enum DmAttributeType_t +{ + AT_UNKNOWN = 0, + + AT_FIRST_VALUE_TYPE, + + AT_ELEMENT = AT_FIRST_VALUE_TYPE, + AT_INT, + AT_FLOAT, + AT_BOOL, + AT_STRING, + AT_VOID, + AT_TIME, + AT_COLOR, //rgba + AT_VECTOR2, + AT_VECTOR3, + AT_VECTOR4, + AT_QANGLE, + AT_QUATERNION, + AT_VMATRIX, + + AT_FIRST_ARRAY_TYPE, + + AT_ELEMENT_ARRAY = AT_FIRST_ARRAY_TYPE, + AT_INT_ARRAY, + AT_FLOAT_ARRAY, + AT_BOOL_ARRAY, + AT_STRING_ARRAY, + AT_VOID_ARRAY, + AT_TIME_ARRAY, + AT_COLOR_ARRAY, + AT_VECTOR2_ARRAY, + AT_VECTOR3_ARRAY, + AT_VECTOR4_ARRAY, + AT_QANGLE_ARRAY, + AT_QUATERNION_ARRAY, + AT_VMATRIX_ARRAY, + AT_TYPE_COUNT, + + AT_TYPE_INVALID, +}; + +const char *GetTypeString( DmAttributeType_t type ); + +inline bool IsValueType( DmAttributeType_t type ) +{ + return type >= AT_FIRST_VALUE_TYPE && type < AT_FIRST_ARRAY_TYPE; +} + +inline bool IsArrayType( DmAttributeType_t type ) +{ + return type >= AT_FIRST_ARRAY_TYPE && type < AT_TYPE_COUNT; +} + +inline bool IsTopological( DmAttributeType_t type ) +{ + return type == AT_ELEMENT || type == AT_ELEMENT_ARRAY; +} + +inline DmAttributeType_t ValueTypeToArrayType( DmAttributeType_t type ) +{ + Assert( IsValueType( type ) ); + return ( DmAttributeType_t )( ( type - AT_FIRST_VALUE_TYPE ) + AT_FIRST_ARRAY_TYPE ); +} + +inline DmAttributeType_t ArrayTypeToValueType( DmAttributeType_t type ) +{ + Assert( IsArrayType( type ) ); + return ( DmAttributeType_t )( ( type - AT_FIRST_ARRAY_TYPE ) + AT_FIRST_VALUE_TYPE ); +} + +inline int NumComponents( DmAttributeType_t type ) +{ + switch ( type ) + { + case AT_BOOL: + case AT_INT: + case AT_FLOAT: + case AT_TIME: + return 1; + + case AT_VECTOR2: + return 2; + + case AT_VECTOR3: + case AT_QANGLE: + return 3; + + case AT_COLOR: //rgba + case AT_VECTOR4: + case AT_QUATERNION: + return 4; + + case AT_VMATRIX: + return 16; + + case AT_ELEMENT: + case AT_STRING: + case AT_VOID: + default: + return 0; + } +} + +template< typename T > +inline float GetComponent( const T &value, int i ) +{ + Assert( 0 ); + return 0.0f; +} + +template <> inline float GetComponent( const bool &value, int i ) +{ + Assert( i == 0 ); + return value ? 1.0f : 0.0f; +} + +template <> inline float GetComponent( const int &value, int i ) +{ + Assert( i == 0 ); + return float( value ); +} + +template <> inline float GetComponent( const float &value, int i ) +{ + Assert( i == 0 ); + return value; +} + +template <> inline float GetComponent( const DmeTime_t &value, int i ) +{ + Assert( i == 0 ); + return value.GetSeconds(); +} + +template <> inline float GetComponent( const Vector2D &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const Vector &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const QAngle &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const Color &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const Vector4D &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const Quaternion &value, int i ) +{ + return value[ i ]; +} + +template <> inline float GetComponent( const VMatrix &value, int i ) +{ + return value.Base()[ i ]; +} + + +//----------------------------------------------------------------------------- +// Attribute info... +//----------------------------------------------------------------------------- + +template +class CDmAttributeInfo +{ +private: + enum { ATTRIBUTE_TYPE = AT_TYPE_INVALID }; + + typedef T StorageType_t; + + static DmAttributeType_t AttributeType() + { + return AT_TYPE_INVALID; + } + + static const char *AttributeTypeName() + { + return "invalid"; + } + + static void SetDefaultValue( T& value ) + { + Assert(0); + } +}; + + +template <> +class CDmAttributeInfo< DmUnknownAttribute_t > +{ +public: + enum { ATTRIBUTE_TYPE = AT_UNKNOWN }; + + typedef DmUnknownAttribute_t StorageType_t; + + static DmAttributeType_t AttributeType() + { + return AT_UNKNOWN; + } + + static const char *AttributeTypeName() + { + return "unknown"; + } + + static void SetDefaultValue( DmUnknownAttribute_t& value ) + { + Assert(0); + } +}; + + +#define DECLARE_ATTRIBUTE_TYPE_INTERNAL( _className, _storageType, _attributeType, _attributeName, _defaultSetStatement ) \ + template< > class CDmAttributeInfo< _className > \ + { \ + public: \ + enum { ATTRIBUTE_TYPE = _attributeType }; \ + typedef _storageType StorageType_t; \ + static DmAttributeType_t AttributeType() { return _attributeType; } \ + static const char *AttributeTypeName() { return _attributeName; } \ + static void SetDefaultValue( _className& value ) { _defaultSetStatement } \ + }; \ + +#define DECLARE_ATTRIBUTE_ARRAY_TYPE_INTERNAL( _className, _storageType, _attributeType, _attributeName ) \ + template< > class CDmAttributeInfo< CUtlVector<_className> > \ + { \ + public: \ + enum { ATTRIBUTE_TYPE = _attributeType }; \ + typedef _storageType StorageType_t; \ + static DmAttributeType_t AttributeType() { return _attributeType; } \ + static const char *AttributeTypeName() { return _attributeName; } \ + static void SetDefaultValue( CUtlVector< _className >& value ) { value.RemoveAll(); } \ + }; \ + +#define DECLARE_ATTRIBUTE_TYPE( _className, _attributeType, _attributeName, _defaultSetStatement ) \ + DECLARE_ATTRIBUTE_TYPE_INTERNAL( _className, _className, _attributeType, _attributeName, _defaultSetStatement ) + +#define DECLARE_ATTRIBUTE_ARRAY_TYPE( _className, _attributeType, _attributeName )\ + DECLARE_ATTRIBUTE_ARRAY_TYPE_INTERNAL( _className, CUtlVector< _className >, _attributeType, _attributeName ) + +// NOTE: If you add an attribute type here, also add it to the list of DEFINE_ATTRIBUTE_TYPES in dmattribute.cpp +DECLARE_ATTRIBUTE_TYPE( int, AT_INT, "int", value = 0; ) +DECLARE_ATTRIBUTE_TYPE( float, AT_FLOAT, "float", value = 0.0f; ) +DECLARE_ATTRIBUTE_TYPE( bool, AT_BOOL, "bool", value = false; ) +DECLARE_ATTRIBUTE_TYPE( Color, AT_COLOR, "color", value.SetColor( 0, 0, 0, 255 ); ) +DECLARE_ATTRIBUTE_TYPE( Vector2D, AT_VECTOR2, "vector2", value.Init( 0.0f, 0.0f ); ) +DECLARE_ATTRIBUTE_TYPE( Vector, AT_VECTOR3, "vector3", value.Init( 0.0f, 0.0f, 0.0f ); ) +DECLARE_ATTRIBUTE_TYPE( Vector4D, AT_VECTOR4, "vector4", value.Init( 0.0f, 0.0f, 0.0f, 0.0f ); ) +DECLARE_ATTRIBUTE_TYPE( QAngle, AT_QANGLE, "qangle", value.Init( 0.0f, 0.0f, 0.0f ); ) +DECLARE_ATTRIBUTE_TYPE( Quaternion, AT_QUATERNION, "quaternion", value.Init( 0.0f, 0.0f, 0.0f, 1.0f ); ) +DECLARE_ATTRIBUTE_TYPE( VMatrix, AT_VMATRIX, "matrix", MatrixSetIdentity( value ); ) +DECLARE_ATTRIBUTE_TYPE( CUtlSymbolLarge, AT_STRING, "string", value = UTL_INVAL_SYMBOL_LARGE; ) +DECLARE_ATTRIBUTE_TYPE( CUtlBinaryBlock, AT_VOID, "binary", value.Set( NULL, 0 ); ) +DECLARE_ATTRIBUTE_TYPE( DmeTime_t, AT_TIME, "time", value.SetTenthsOfMS( 0 ); ) +DECLARE_ATTRIBUTE_TYPE_INTERNAL( DmElementHandle_t, DmElementAttribute_t, AT_ELEMENT, "element", value = DMELEMENT_HANDLE_INVALID; ) + +DECLARE_ATTRIBUTE_ARRAY_TYPE( int, AT_INT_ARRAY, "int_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( float, AT_FLOAT_ARRAY, "float_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( bool, AT_BOOL_ARRAY, "bool_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( Color, AT_COLOR_ARRAY, "color_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( Vector2D, AT_VECTOR2_ARRAY, "vector2_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( Vector, AT_VECTOR3_ARRAY, "vector3_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( Vector4D, AT_VECTOR4_ARRAY, "vector4_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( QAngle, AT_QANGLE_ARRAY, "qangle_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( Quaternion, AT_QUATERNION_ARRAY, "quaternion_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( VMatrix, AT_VMATRIX_ARRAY, "matrix_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( CUtlSymbolLarge, AT_STRING_ARRAY, "string_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( CUtlBinaryBlock, AT_VOID_ARRAY, "binary_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE( DmeTime_t, AT_TIME_ARRAY, "time_array" ) +DECLARE_ATTRIBUTE_ARRAY_TYPE_INTERNAL( DmElementHandle_t, DmElementArray_t, AT_ELEMENT_ARRAY, "element_array" ) + + +#endif // DMATTRIBUTETYPES_H diff --git a/public/datamodel/dmattributevar.h b/public/datamodel/dmattributevar.h new file mode 100644 index 0000000..871df5e --- /dev/null +++ b/public/datamodel/dmattributevar.h @@ -0,0 +1,1518 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMATTRIBUTEVAR_H +#define DMATTRIBUTEVAR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "datamodel/dmattribute.h" +#include "tier1/utlvector.h" +#include "Color.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "mathlib/vmatrix.h" +#include "datamodel/dmelement.h" +#include "datamodel/dmehandle.h" + + +//----------------------------------------------------------------------------- +// Specialization for color +//----------------------------------------------------------------------------- +class CDmaColor : public CDmaVar< Color > +{ +public: + // Set methods + void SetColor( int r, int g, int b, int a = 0 ); + void SetRed( int r ); + void SetGreen( int g ); + void SetBlue( int b ); + void SetAlpha( int a ); + + // Sets the color as a 32-bit integer + void SetRawColor( int color ); + + // Get methods + unsigned char r() const; + unsigned char g() const; + unsigned char b() const; + unsigned char a() const; + const unsigned char &operator[]( int index ) const; +}; + + +//----------------------------------------------------------------------------- +// Specialization for DmeTime_t +//----------------------------------------------------------------------------- +class CDmaTime : public CDmaVar< DmeTime_t > +{ +public: + // Cast operators + operator const DmeTime_t&() const { return Value(); } + const DmeTime_t* operator->() const { return &Value(); } + + // Math utility operations + const DmeTime_t& operator= ( const DmeTime_t &val ) { return Set( val ); } + const DmeTime_t& operator+=( const DmeTime_t &val ) { return Set( Value() + val ); } + const DmeTime_t& operator-=( const DmeTime_t &val ) { return Set( Value() - val ); } + const DmeTime_t& operator/=( float f ) { return Set( Value() / f ); } + const DmeTime_t& operator*=( float f ) { return Set( Value() * f ); } + DmeTime_t operator++() { return Set( Value() + DMETIME_MINDELTA ); } + DmeTime_t operator--() { return Set( Value() - DMETIME_MINDELTA ); } + DmeTime_t operator++( int ) { DmeTime_t t = Value(); Set( t + DMETIME_MINDELTA ); return t; } // postfix version.. + DmeTime_t operator--( int ) { DmeTime_t t = Value(); Set( t - DMETIME_MINDELTA ); return t; } // postfix version.. + + void SetTenthsOfMS( int tms ); + void SetSeconds( float seconds ); + int GetTenthsOfMS() const; + float GetSeconds() const; +}; + + +//----------------------------------------------------------------------------- +// Specialization for binary block +//----------------------------------------------------------------------------- +class CDmaBinaryBlock : public CDmaVar< CUtlBinaryBlock > +{ +public: + void Get( void *pValue, int nMaxLen ) const; + void Set( const void *pValue, int nLen ); + const void *Get() const; + const unsigned char& operator[]( int i ) const; + + // Returns buffer length + int Length() const; +}; + + +//----------------------------------------------------------------------------- +// Specialization for elements +//----------------------------------------------------------------------------- +template +class CDmaElement : public CDmaVar< DmElementHandle_t > +{ + typedef CDmaVar< DmElementHandle_t > BaseClass; + +public: + // Used to initialize the attribute in an element's OnConstruction method + void InitAndCreate( CDmElement *pOwner, const char *pAttributeName, const char *pElementName = NULL, int flags = 0 ); + void Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ); + + // Returns the type of elements allowed into this attribute. UTL_INVAL_SYMBOL allows everything. + CUtlSymbolLarge GetElementType() const; + + // Get/set + void Set( T* pElement ); + T* GetElement() const; + const DmElementHandle_t& GetHandle() const + { + return Value(); + } + + // Cast + T* operator->() const; + operator T*() const; + + // NULL check + bool operator!() const; + + // Assignment.. wish I knew how to un-inline these methods + template CDmaElement &operator=( S* pElement ) + { + Set( static_cast( pElement ) ); + return *this; + } + + template CDmaElement &operator=( const CDmaElement& src ) + { + Set( static_cast( src.Get() ) ); + return *this; + } + + template bool operator==( const CDmaElement& src ) const + { + return Value() == src.Value(); + } + + template bool operator!=( const CDmaElement& src ) const + { + return Value() != src.Value(); + } + +protected: + // hide this to avoid accidental use in code like this: + // if ( m_var.Get() ) + // which returns a DmElementHandle_t, which is true for most handles, including the invalid one + const DmElementHandle_t& Get() const + { + return Value(); + } +}; + + +//----------------------------------------------------------------------------- +// Can access any array attribute, regardless of type +// See below for type-specific array accessors which have more features +//----------------------------------------------------------------------------- +class CDmrGenericArrayConst +{ +public: + CDmrGenericArrayConst( const CDmAttribute* pAttribute ); + CDmrGenericArrayConst( const CDmElement *pElement, const char *pAttributeName ); + + // Array count + int Count() const; + + // Gets + const void* GetUntyped( int i ) const; + + // String conversion + const char* GetAsString( int i, char *pBuffer, size_t nBufLen ) const; + + const CDmAttribute *GetAttribute() const; + bool IsValid() const; + +protected: + CDmrGenericArrayConst(); + void Init( const CDmAttribute *pAttribute ); + void Init( const CDmElement *pElement, const char *pAttributeName ); + + CDmAttribute *m_pAttribute; +}; + +class CDmrGenericArray : public CDmrGenericArrayConst +{ +public: + CDmrGenericArray( CDmAttribute* pAttribute ); + CDmrGenericArray( CDmElement *pElement, const char *pAttributeName ); + + void EnsureCount( int num ); + + // Sets multiple elements at the same time + int AddToTail(); + void Remove( int elem ); // preserves order, shifts elements + void RemoveAll(); // doesn't deallocate memory + void SetMultiple( int i, int nCount, DmAttributeType_t valueType, const void *pValue ); + void Set( int i, DmAttributeType_t valueType, const void *pValue ); + + // String conversion + void SetFromString( int i, const char *pValue ); + + CDmAttribute *GetAttribute(); + const CDmAttribute *GetAttribute() const; +}; + + +//----------------------------------------------------------------------------- +// Helper template for external array attribute vars +// NOTE: To use this class, don't use CDmaArrayBase directly. Instead, use +// CDmaArray var; <- Instantiate an array attribute var as a member of a element class +// CDmrArray var; <- Used to reference an existing array attribute + read/modify it +// CDmrArrayConst var; <- Used to reference an existing array attribute + read it (no modify) +// +// Also, there is a CDmaStringArray/CDmrStringArray/CDmrStringArrayConst for strings +// and a CDmaElementArray/CDmrElementArray/CDmrElementArrayConst for elements +//----------------------------------------------------------------------------- +template< class T, class B > +class CDmaArrayConstBase : public B +{ +public: + // Accessors + const CUtlVector &Get() const; + const T *Base() const; + + // Iteration + int Count() const; + const T& operator[]( int i ) const; + const T& Element( int i ) const; + const T& Get( int i ) const; + const void* GetUntyped( int i ) const; + bool IsValidIndex( int i ) const; + int InvalidIndex( void ) const; + + // Search + int Find( const T &value ) const; + + // Attribute-related methods + const CDmAttribute *GetAttribute() const; + CDmElement *GetOwner(); + bool IsDirty() const; + +protected: + CDmaArrayConstBase( ); + + CDmAttribute *m_pAttribute; +}; + +template< class T, class B > +class CDmaArrayBase : public CDmaArrayConstBase< T, B > +{ +public: + // Insertion + int AddToTail(); + int InsertBefore( int elem ); + int AddToTail( const T& src ); + int InsertBefore( int elem, const T& src ); + int AddMultipleToTail( int num ); + int InsertMultipleBefore( int elem, int num ); + void EnsureCount( int num ); + + // Element Modification + void Set( int i, const T& value ); + void SetMultiple( int i, int nCount, const T* pValue ); + void Swap( int i, int j ); + + // Copy related methods + void CopyArray( const T *pArray, int size ); + + // this is basically just a faster version of CopyArray which uses pointer swap + // NOTE: This doesn't work for element arrays + void SwapArray( CUtlVector< T > &array ); + + // Removal + void FastRemove( int elem ); + void Remove( int elem ); + void RemoveMultiple( int elem, int num ); + void RemoveAll(); + + // Memory management + void EnsureCapacity( int num ); + void Purge(); + + // Attribute-related methods + CDmAttribute *GetAttribute(); + const CDmAttribute *GetAttribute() const; +}; + + +//----------------------------------------------------------------------------- +// Specialization for string arrays +// NOTE: To use this class, don't use CDmaStringArrayBase directly. Instead, use +// CDmaStringArray var; <- Instantiate an array attribute var as a member of a element class +// CDmrStringArray var; <- Used to reference an existing array attribute + read/modify it +// CDmrStringArrayConst var; <- Used to reference an existing array attribute + read it (no modify) +//----------------------------------------------------------------------------- +template< class BaseClass > +class CDmaStringArrayConstBase : public BaseClass +{ +public: + const char *operator[]( int i ) const; + const char *Element( int i ) const; + const char *Get( int i ) const; + const CUtlVector< CUtlSymbolLarge > &Get() const; + + // Returns strlen of element i + int Length( int i ) const; +}; + +template< class B > +class CDmaStringArrayBase : public CDmaStringArrayConstBase< CDmaArrayBase< CUtlSymbolLarge, B > > +{ + typedef CDmaStringArrayConstBase< CDmaArrayBase< CUtlSymbolLarge, B > > BaseClass; + +public: + // Sets an element in the array + void Set( int i, const char * pValue ); + + // Adds an element, uses copy constructor + int AddToTail( const char *pValue ); + int InsertBefore( int elem, const char *pValue ); +}; + + +//----------------------------------------------------------------------------- +// Specialization for elements +// NOTE: To use this class, don't use CDmaElementArrayBase directly. Instead, use +// CDmaElementArray< element_type > var; <- Instantiate an array attribute var as a member of a element class +// CDmrElementArray< element_type > var; <- Used to reference an existing array attribute + read/modify it +// CDmrElementArrayConst< element_type > var; <- Used to reference an existing array attribute + read it (no modify) +//----------------------------------------------------------------------------- +template< class E, class BaseClass > +class CDmaElementArrayConstBase : public BaseClass +{ +public: + // Returns the element type + CUtlSymbolLarge GetElementType() const; + + // Array access + E *operator[]( int i ) const; + E *Element( int i ) const; + E *Get( int i ) const; + const DmElementHandle_t& GetHandle( int i ) const; + const CUtlVector< DmElementHandle_t > &Get() const; + + // Search + int Find( const E *pValue ) const; + int Find( DmElementHandle_t h ) const; +}; + +template < class E, class B > +class CDmaElementArrayBase : public CDmaElementArrayConstBase< E, CDmaArrayBase< DmElementHandle_t, B > > +{ + typedef CDmaElementArrayConstBase< E, CDmaArrayBase< DmElementHandle_t, B > > BaseClass; + +public: + void SetHandle( int i, DmElementHandle_t h ); + void Set( int i, E *pElement ); + + // Insertion + int AddToTail( ); + int AddToTail( DmElementHandle_t src ); + int AddToTail( E *pValue ); + int InsertBefore( int elem ); + int InsertBefore( int elem, DmElementHandle_t src ); + int InsertBefore( int elem, E *pValue ); + + template< class C, HandleType_t HT > int AddToTail( const CDmeHandle& value ) + { + return BaseClass::AddToTail( value.GetHandle() ); + } + + template< class C, HandleType_t HT > int InsertBefore( int elem, const CDmeHandle& value ) + { + return BaseClass::InsertBefore( elem, value.GetHandle() ); + } +}; + + +// NOTE: The next couple classes are implementation details used to create CDmrAray/CDmaArray + +//----------------------------------------------------------------------------- +// Base classes that contain data or refer to it; used for array accessor classes +//----------------------------------------------------------------------------- +template< typename T > +class CDmaDataInternal +{ +protected: + typedef typename CDmAttributeInfo< T >::StorageType_t D; + + const T& Value() const { return m_Storage; } + T& Value( ) { return m_Storage; } + const D& Data() const { return m_Storage; } + D& Data( ) { return m_Storage; } + +private: + D m_Storage; +}; + +template< typename T > +class CDmaDataExternal +{ +protected: + typedef typename CDmAttributeInfo< T >::StorageType_t D; + + CDmaDataExternal() : m_pStorage(0) {} + void Attach( void *pData ) { m_pStorage = (D*)pData; } + const T& Value() const { return *m_pStorage; } + T& Value( ) { return *m_pStorage; } + const D& Data() const { return *m_pStorage; } + D& Data( ) { return *m_pStorage; } + +private: + D* m_pStorage; +}; + + +//----------------------------------------------------------------------------- +// Versions for access, or for attribute vars +//----------------------------------------------------------------------------- +template< class T, class B > +class CDmaDecorator : public B +{ +public: + void Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ); +}; + + +template< class T, class BaseClass > +class CDmrDecoratorConst : public BaseClass +{ +public: + void Init( const CDmAttribute* pAttribute ); + void Init( const CDmElement *pElement, const char *pAttributeName ); + + bool IsValid() const; +}; + +template< class T, class BaseClass > +class CDmrDecorator : public BaseClass +{ +public: + void Init( CDmAttribute* pAttribute ); + void Init( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute = false ); + + bool IsValid() const; +}; + + +#define DECLARE_ATTRIBUTE_ARRAY_VARIABLE( _className, _elementType ) \ + public: \ + _className() {} + +#define DECLARE_ATTRIBUTE_ARRAY_REFERENCE( _className, _elementType ) \ + public: \ + _className() {} \ + _className( CDmAttribute* pAttribute ) { Init( pAttribute ); } \ + _className( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute = false ) { Init( pElement, pAttributeName, bAddAttribute ); } \ + _className( CDmaArray<_elementType>& var ) { Init( var.GetAttribute() ); } \ + _className( CDmrArray<_elementType>& var ) { Init( var.GetAttribute() ); } + +#define DECLARE_ATTRIBUTE_ARRAY_CONST_REFERENCE( _className, _elementType ) \ + public: \ + _className() {} \ + _className( const CDmAttribute* pAttribute ) { Init( pAttribute ); } \ + _className( const CDmElement *pElement, const char *pAttributeName ) { Init( pElement, pAttributeName ); } \ + _className( const CDmaArray<_elementType>& var ) { Init( var.GetAttribute() ); } \ + _className( const CDmrArrayConst<_elementType>& var ) { Init( var.GetAttribute() ); } \ + _className( const CDmrArray<_elementType>& var ) { Init( var.GetAttribute() ); } + +template class CDmrArray; +template class CDmrArrayConst; +template class CDmaArray; + +//----------------------------------------------------------------------------- +// Versions for access, or for attribute vars +//----------------------------------------------------------------------------- +template +class CDmaArray : public CDmaDecorator< T, CDmaArrayBase< T, CDmaDataInternal< CUtlVector< T > > > > +{ + DECLARE_ATTRIBUTE_ARRAY_VARIABLE( CDmaArray, T ); + +public: + const CDmaArray& operator=( const CDmaArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + template< class C > const CDmaArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + +private: + CDmaArray( const CDmaArray& array ) {} +}; + + +template +class CDmrArrayConst : public CDmrDecoratorConst< T, CDmaArrayConstBase< T, CDmaDataExternal< CUtlVector< T > > > > +{ + typedef CDmrDecoratorConst< T, CDmaArrayConstBase< T, CDmaDataExternal< CUtlVector< T > > > > BaseClass; + DECLARE_ATTRIBUTE_ARRAY_CONST_REFERENCE( CDmrArrayConst, T ); +}; + + +template +class CDmrArray : public CDmrDecorator< T, CDmaArrayBase< T, CDmaDataExternal< CUtlVector< T > > > > +{ + typedef CDmrDecorator< T, CDmaArrayBase< T, CDmaDataExternal< CUtlVector< T > > > > BaseClass; + DECLARE_ATTRIBUTE_ARRAY_REFERENCE( CDmrArray, T ); + +public: + const CDmrArray& operator=( const CDmrArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + template< class C > const CDmrArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } +}; + +class CDmrStringArray; + +class CDmaStringArray : public CDmaDecorator< CUtlSymbolLarge, CDmaStringArrayBase< CDmaDataInternal< CUtlVector< CUtlSymbolLarge > > > > +{ + DECLARE_ATTRIBUTE_ARRAY_VARIABLE( CDmaStringArray, CUtlSymbolLarge ); + +public: + const CDmaStringArray& operator=( const CDmaStringArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + template< class C > const CDmaStringArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + +private: + CDmaStringArray( const CDmaStringArray& array ) {} +}; + +class CDmrStringArray : public CDmrDecorator< CUtlSymbolLarge, CDmaStringArrayBase< CDmaDataExternal< CUtlVector< CUtlSymbolLarge > > > > +{ + typedef CDmrDecorator< CUtlSymbolLarge, CDmaStringArrayBase< CDmaDataExternal< CUtlVector< CUtlSymbolLarge > > > > BaseClass; + DECLARE_ATTRIBUTE_ARRAY_REFERENCE( CDmrStringArray, CUtlSymbolLarge ); + +public: + CDmrStringArray( CDmaStringArray& var ) { Init( var.GetAttribute() ); } + CDmrStringArray( CDmrStringArray& var ) { Init( var.GetAttribute() ); } + + const CDmrStringArray& operator=( const CDmrStringArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + template< class C > const CDmrStringArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } +}; + +class CDmrStringArrayConst : public CDmrDecoratorConst< CUtlSymbolLarge, CDmaStringArrayConstBase< CDmaArrayConstBase< CUtlSymbolLarge, CDmaDataExternal< CUtlVector< CUtlSymbolLarge > > > > > +{ + typedef CDmrDecoratorConst< CUtlSymbolLarge, CDmaStringArrayConstBase< CDmaArrayConstBase< CUtlSymbolLarge, CDmaDataExternal< CUtlVector< CUtlSymbolLarge > > > > > BaseClass; + DECLARE_ATTRIBUTE_ARRAY_CONST_REFERENCE( CDmrStringArrayConst, CUtlSymbolLarge ); + +public: + CDmrStringArrayConst( const CDmaStringArray& var ) { Init( var.GetAttribute() ); } + CDmrStringArrayConst( const CDmrStringArray& var ) { Init( var.GetAttribute() ); } + CDmrStringArrayConst( const CDmrStringArrayConst& var ) { Init( var.GetAttribute() ); } +}; + + +//----------------------------------------------------------------------------- +// Prevent CDmaArray for DmElementHandle_t +//----------------------------------------------------------------------------- +template<> class CDmaArray { private: CDmaArray(); }; + + +template< class E > class CDmrElementArray; + +template< class E = CDmElement > +class CDmaElementArray : public CDmaElementArrayBase< E, CDmaDataInternal< CUtlVector< DmElementHandle_t > > > +{ + DECLARE_ATTRIBUTE_ARRAY_VARIABLE( CDmaElementArray, DmElementHandle_t ); + +public: + void Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ) + { + Assert( pOwner ); + this->m_pAttribute = pOwner->AddExternalAttribute( pAttributeName, AT_ELEMENT_ARRAY, &Value() ); + this->m_pAttribute->SetElementTypeSymbol( E::GetStaticTypeSymbol() ); + if ( flags ) + { + this->m_pAttribute->AddFlag( flags ); + } + } + + template< typename C > CDmaElementArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + // NOTE: The copy operator= must be defined in addition to the generic one + const CDmaElementArray& operator=( const CDmaElementArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + +private: + template< class C > CDmaElementArray( const CDmaElementArray& var ); +}; + +template< class E = CDmElement > +class CDmrElementArrayConst : public CDmaElementArrayConstBase< E, CDmaArrayConstBase< DmElementHandle_t, CDmaDataExternal< CUtlVector< DmElementHandle_t > > > > +{ +public: + CDmrElementArrayConst() + { + this->m_pAttribute = NULL; + } + + CDmrElementArrayConst( const CDmAttribute* pAttribute ) + { + Init( pAttribute ); + } + + CDmrElementArrayConst( const CDmElement *pElement, const char *pAttributeName ) + { + Init( pElement, pAttributeName ); + } + + template< typename C > CDmrElementArrayConst( const CDmaElementArray& var ) + { + Init( var.GetAttribute() ); + } + + template< typename C > CDmrElementArrayConst( const CDmrElementArray& var ) + { + Init( var.GetAttribute() ); + } + + template< typename C > CDmrElementArrayConst( const CDmrElementArrayConst& var ) + { + Init( var.GetAttribute() ); + } + + void Init( const CDmAttribute* pAttribute ) + { + if ( pAttribute && pAttribute->GetType() == AT_ELEMENT_ARRAY ) + { + this->m_pAttribute = const_cast( pAttribute ); + Attach( this->m_pAttribute->GetAttributeData() ); + } + else + { + this->m_pAttribute = NULL; + Attach( NULL ); + } + } + + void Init( const CDmElement *pElement, const char *pAttributeName ) + { + const CDmAttribute *pAttribute = NULL; + if ( pElement && pAttributeName && pAttributeName[0] ) + { + pAttribute = pElement->GetAttribute( pAttributeName ); + } + Init( pAttribute ); + } + + int Count() const + { + return IsValid() ? Value().Count() : 0; + } + + bool IsValid() const + { + return m_pAttribute != NULL; + } +}; + +template< class T = CDmElement > +class CDmrElementArray : public CDmaElementArrayBase< T, CDmaDataExternal< CUtlVector< DmElementHandle_t > > > +{ +public: + CDmrElementArray() + { + this->m_pAttribute = NULL; + } + + CDmrElementArray( CDmAttribute* pAttribute ) + { + Init( pAttribute ); + } + + CDmrElementArray( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute = false ) + { + Init( pElement, pAttributeName, bAddAttribute ); + } + + template< typename C > CDmrElementArray( CDmaElementArray& var ) + { + Init( var.GetAttribute() ); + } + + template< typename C > CDmrElementArray( CDmrElementArray& var ) + { + Init( var.GetAttribute() ); + } + + void Init( CDmAttribute* pAttribute ) + { + if ( pAttribute && pAttribute->GetType() == AT_ELEMENT_ARRAY ) + { + this->m_pAttribute = pAttribute; + Attach( this->m_pAttribute->GetAttributeData() ); + } + else + { + this->m_pAttribute = NULL; + Attach( NULL ); + } + } + + void Init( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute = false ) + { + CDmAttribute *pAttribute = NULL; + if ( pElement && pAttributeName && pAttributeName[0] ) + { + pAttribute = pElement->GetAttribute( pAttributeName ); + if ( bAddAttribute && !pAttribute ) + { + pAttribute = pElement->CreateAttribute( pAttributeName, AT_ELEMENT_ARRAY ); + + // FIXME: Should we do this? + pAttribute->SetElementTypeSymbol( T::GetStaticTypeSymbol() ); + } + } + Init( pAttribute ); + } + + int Count() const + { + return IsValid() ? Value().Count() : 0; + } + + bool IsValid() const + { + return m_pAttribute != NULL; + } + + template< typename C > CDmrElementArray& operator=( const C &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } + + // NOTE: The copy operator= must be defined in addition to the generic one + const CDmrElementArray& operator=( const CDmrElementArray &val ) + { + CopyArray( val.Base(), val.Count() ); + return *this; + } +}; + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaVar +// +//----------------------------------------------------------------------------- +template< class T > inline CDmaVar::CDmaVar( ) +{ + m_pAttribute = NULL; + CDmAttributeInfo::SetDefaultValue( m_Storage ); +} + +template< class T > inline void CDmaVar::Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ) +{ + Assert( pOwner ); + m_pAttribute = pOwner->AddExternalAttribute( pAttributeName, CDmAttributeInfo::AttributeType(), &m_Storage ); + Assert( m_pAttribute ); + if ( flags ) + { + m_pAttribute->AddFlag( flags ); + } +} + +template< class T > inline void CDmaVar::InitAndSet( CDmElement *pOwner, const char *pAttributeName, const T &value, int flags = 0 ) +{ + Init( pOwner, pAttributeName ); + Set( value ); + + // this has to happen AFTER set so the set happens before FATTRIB_READONLY + if ( flags ) + { + m_pAttribute->AddFlag( flags ); + } +} + +template< class T > inline const T& CDmaVar::Set( const T &val ) +{ + Assert( m_pAttribute ); + m_pAttribute->SetValue( val ); + return m_Storage; +} + +template< class T > inline const T& CDmaVar::operator=( const T &val ) +{ + return Set( val ); +} + +template< class T > inline const CDmaVar& CDmaVar::operator=( const CDmaVar& src ) +{ + Set( src.Get() ); + return *this; +} + +template< class T > inline const T& CDmaVar::operator+=( const T &val ) +{ + return Set( Value() + val ); +} + +template< class T > inline const T& CDmaVar::operator-=( const T &val ) +{ + return Set( Value() - val ); +} + +template< class T > inline const T& CDmaVar::operator/=( const T &val ) +{ + return Set( Value() / val ); +} + +template< class T > inline const T& CDmaVar::operator*=( const T &val ) +{ + return Set( Value() * val ); +} + +template< class T > inline const T& CDmaVar::operator^=( const T &val ) +{ + return Set( Value() ^ val ); +} + +template< class T > inline const T& CDmaVar::operator|=( const T &val ) +{ + return Set( Value() | val ); +} + +template< class T > inline const T& CDmaVar::operator&=( const T &val ) +{ + return Set( Value() & val ); +} + +template< class T > inline T CDmaVar::operator++() +{ + return Set( Value() + 1 ); +} + +template< class T > inline T CDmaVar::operator--() +{ + return Set( Value() - 1 ); +} + +template< class T > inline T CDmaVar::operator++( int ) // postfix version.. +{ + T oldValue = Value(); + Set( Value() + 1 ); + return oldValue; +} + +template< class T > inline T CDmaVar::operator--( int ) // postfix version.. +{ + T oldValue = Value(); + Set( Value() - 1 ); + return oldValue; +} + +template< class T > inline CDmaVar::operator const T&() const +{ + return Value(); +} + +template< class T > inline const T& CDmaVar::Get() const +{ + return Value(); +} + +template< class T > inline const T* CDmaVar::operator->() const +{ + return &Value(); +} + +template< class T > inline CDmAttribute *CDmaVar::GetAttribute() +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + +template< class T > inline const CDmAttribute *CDmaVar::GetAttribute() const +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + +template< class T > inline bool CDmaVar::IsDirty() const +{ + Assert( m_pAttribute ); + return m_pAttribute->IsFlagSet( FATTRIB_DIRTY ); +} + +template< class T > inline const T& CDmaVar::Value() const +{ + return m_Storage; +} + +template< class T > inline T& CDmaVar::Value() +{ + return m_Storage; +} + +template<> inline const DmElementHandle_t& CDmaVar< DmElementHandle_t >::Value() const +{ + return m_Storage.m_Handle; +} + +template<> inline DmElementHandle_t& CDmaVar< DmElementHandle_t >::Value() +{ + return m_Storage.m_Handle; +} + +template< class T > inline const typename CDmaVar::D& CDmaVar::Storage() const +{ + return m_Storage; +} + +template< class T > inline typename CDmaVar::D& CDmaVar::Storage() +{ + return m_Storage; +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaColor +// +//----------------------------------------------------------------------------- +inline void CDmaColor::SetColor( int r, int g, int b, int a ) +{ + Color clr( r, g, b, a ); + m_pAttribute->SetValue( clr ); +} + +inline void CDmaColor::SetRed( int r ) +{ + Color org = Value(); + org[ 0 ] = r; + m_pAttribute->SetValue( org ); +} + +inline void CDmaColor::SetGreen( int g ) +{ + Color org = Value(); + org[ 1 ] = g; + m_pAttribute->SetValue( org ); +} + +inline void CDmaColor::SetBlue( int b ) +{ + Color org = Value(); + org[ 2 ] = b; + m_pAttribute->SetValue( org ); +} + +inline void CDmaColor::SetAlpha( int a ) +{ + Color org = Value(); + org[ 3 ] = a; + m_pAttribute->SetValue( org ); +} + +inline unsigned char CDmaColor::r() const +{ + return (unsigned char)Value().r(); +} + +inline unsigned char CDmaColor::g() const +{ + return (unsigned char)Value().g(); +} + +inline unsigned char CDmaColor::b() const +{ + return (unsigned char)Value().b(); +} + +inline unsigned char CDmaColor::a() const +{ + return (unsigned char)Value().a(); +} + +inline const unsigned char &CDmaColor::operator[](int index) const +{ + return Value()[index]; +} + +inline void CDmaColor::SetRawColor( int color ) +{ + Color clr; + clr.SetRawColor( color ); + m_pAttribute->SetValue( clr ); +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaTime +// +//----------------------------------------------------------------------------- +inline void CDmaTime::SetTenthsOfMS( int tms ) +{ + Set( DmeTime_t( tms ) ); +} + +inline void CDmaTime::SetSeconds( float seconds ) +{ + Set( DmeTime_t( seconds ) ); +} + +inline int CDmaTime::GetTenthsOfMS() const +{ + return Value().GetTenthsOfMS(); +} + +inline float CDmaTime::GetSeconds() const +{ + return Value().GetSeconds(); +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaString +// +//----------------------------------------------------------------------------- +inline const char *CDmaString::Get( ) const +{ + return Value().String(); +} + +inline CDmaString::operator const char*() const +{ + return Value().String(); +} + + +inline void CDmaString::InitAndSet( CDmElement *pOwner, const char *pAttributeName, const char *pValue, int flags ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + CDmaVar< CUtlSymbolLarge >::InitAndSet( pOwner, pAttributeName, symbol, flags ); +} + + +inline void CDmaString::Set( const char *pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + m_pAttribute->SetValue( symbol ); +} + +// Returns strlen +inline int CDmaString::Length() const +{ + return V_strlen( Value().String() ); +} + +inline bool CDmaString::IsEmpty() const +{ + if ( Value() == UTL_INVAL_SYMBOL_LARGE ) + return true; + + const char *pString = Value().String(); + if ( pString == NULL ) + return true; + + if ( *pString == 0 ) + return true; + + return false; +} + +inline CDmaString &CDmaString::operator=( const char *src ) +{ + Set( src ); + return *this; +} + +inline const CDmaString& CDmaString::operator=( const CDmaString& src ) +{ + Set( src.Get() ); + return *this; +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaBinaryBlock +// +//----------------------------------------------------------------------------- +inline void CDmaBinaryBlock::Get( void *pValue, int nMaxLen ) const +{ + Value().Get( pValue, nMaxLen ); +} + +inline void CDmaBinaryBlock::Set( const void *pValue, int nLen ) +{ + CUtlBinaryBlock block( pValue, nLen ); + m_pAttribute->SetValue( block ); +} + +inline const void *CDmaBinaryBlock::Get() const +{ + return Value().Get(); +} + +inline const unsigned char& CDmaBinaryBlock::operator[]( int i ) const +{ + return Value()[i]; +} + +inline int CDmaBinaryBlock::Length() const +{ + return Value().Length(); +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaElement +// +//----------------------------------------------------------------------------- +template +inline void CDmaElement::InitAndCreate( CDmElement *pOwner, const char *pAttributeName, const char *pElementName, int flags ) +{ + Init( pOwner, pAttributeName ); + + DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID; + if ( !g_pDataModel->IsUnserializing() ) + { + hElement = g_pDataModel->CreateElement( T::GetStaticTypeSymbol(), pElementName, pOwner->GetFileId() ); + } + Assert( m_pAttribute ); + m_pAttribute->SetValue( hElement ); + + // this has to happen AFTER set so the set happens before FATTRIB_READONLY + m_pAttribute->AddFlag( flags | FATTRIB_MUSTCOPY ); +} + +template +inline void CDmaElement::Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ) +{ + BaseClass::Init( pOwner, pAttributeName ); + + Assert( m_pAttribute ); + m_pAttribute->SetElementTypeSymbol( T::GetStaticTypeSymbol() ); + if ( flags ) + { + m_pAttribute->AddFlag( flags ); + } +} + +template +inline CUtlSymbolLarge CDmaElement::GetElementType() const +{ + return Data().m_ElementType; +} + +template +inline T* CDmaElement::GetElement() const +{ + CDmElement *pElement = g_pDataModel->GetElement( Value() ); + Assert( !pElement || pElement->IsA( T::GetStaticTypeSymbol() ) ); + return static_cast< T* >( pElement ); +} + +template +inline T* CDmaElement::operator->() const +{ + return GetElement(); +} + +template +inline CDmaElement::operator T*() const +{ + return GetElement(); +} + +template +inline void CDmaElement::Set( T* pElement ) +{ + Assert( m_pAttribute ); + m_pAttribute->SetValue( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID ); +} + +template +inline bool CDmaElement::operator!() const +{ + return ( GetElement() == NULL ); +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaArrayBase +// +//----------------------------------------------------------------------------- +template< class T, class B > +inline const CUtlVector& CDmaArrayConstBase::Get() const +{ + return Value(); +} + +template< class T, class B > +inline const T *CDmaArrayConstBase::Base() const +{ + return Value().Base(); +} + +template< class T, class B > +inline const T& CDmaArrayConstBase::operator[]( int i ) const +{ + return Value()[ i ]; +} + +template< class T, class B > +const T& CDmaArrayConstBase::Element( int i ) const +{ + return Value()[ i ]; +} + +template< class T, class B > +inline const T& CDmaArrayConstBase::Get( int i ) const +{ + return Value()[ i ]; +} + +template< class T, class B > +const void* CDmaArrayConstBase::GetUntyped( int i ) const +{ + return &( Value()[ i ] ); +} + +template< class T, class B > +inline int CDmaArrayConstBase::Count() const +{ + return Value().Count(); +} + +template< class T, class B > +inline bool CDmaArrayConstBase::IsValidIndex( int i ) const +{ + return Value().IsValidIndex( i ); +} + +template< class T, class B > +inline int CDmaArrayConstBase::InvalidIndex( void ) const +{ + return Value().InvalidIndex(); +} + +template< class T, class B > +inline const CDmAttribute *CDmaArrayConstBase::GetAttribute() const +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + +template< class T, class B > +inline CDmElement *CDmaArrayConstBase::GetOwner() +{ + return m_pAttribute->GetOwner(); +} + +template< class T, class B > +inline bool CDmaArrayConstBase::IsDirty() const +{ + return m_pAttribute->IsFlagSet( FATTRIB_DIRTY ); +} + + +template< class T, class B > +inline CDmAttribute *CDmaArrayBase::GetAttribute() +{ + Assert( this->m_pAttribute ); + return this->m_pAttribute; +} + +template< class T, class B > +inline const CDmAttribute *CDmaArrayBase::GetAttribute() const +{ + Assert( this->m_pAttribute ); + return this->m_pAttribute; +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaStringArrayBase +// +//----------------------------------------------------------------------------- +template< class B > +inline const char *CDmaStringArrayConstBase::operator[]( int i ) const +{ + return Value()[ i ].String(); +} + +template< class B > +inline const char *CDmaStringArrayConstBase::Element( int i ) const +{ + return Value()[ i ].String(); +} + +template< class B > +inline const char *CDmaStringArrayConstBase::Get( int i ) const +{ + return Value()[ i ].String(); +} + +template< class B > +inline const CUtlVector< CUtlSymbolLarge > &CDmaStringArrayConstBase::Get() const +{ + return Value(); +} + +// Returns strlen of element i +template< class B > +inline int CDmaStringArrayConstBase::Length( int i ) const +{ + return Value()[i].Length(); +} + +template< class B > +inline void CDmaStringArrayBase::Set( int i, const char * pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + BaseClass::Set( i, symbol ); +} + +// Adds an element, uses copy constructor +template< class B > +inline int CDmaStringArrayBase::AddToTail( const char *pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + return BaseClass::AddToTail( symbol ); +} + +template< class B > +inline int CDmaStringArrayBase::InsertBefore( int elem, const char *pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + return BaseClass::InsertBefore( elem, symbol ); +} + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmaElementArrayBase +// +//----------------------------------------------------------------------------- +template< class E, class B > +inline CUtlSymbolLarge CDmaElementArrayConstBase::GetElementType() const +{ + return Data().m_ElementType; +} + +template< class E, class B > +inline E *CDmaElementArrayConstBase::operator[]( int i ) const +{ + return GetElement( Value()[i] ); +} + +template< class E, class B > +inline E *CDmaElementArrayConstBase::Element( int i ) const +{ + return GetElement( Value()[i] ); +} + +template< class E, class B > +inline E *CDmaElementArrayConstBase::Get( int i ) const +{ + return GetElement( Value()[i] ); +} + +template< class E, class B > +inline const DmElementHandle_t& CDmaElementArrayConstBase::GetHandle( int i ) const +{ + return Value()[i]; +} + +template< class E, class B > +inline const CUtlVector< DmElementHandle_t > &CDmaElementArrayConstBase::Get() const +{ + return Value(); +} + +// Search +template< class E, class B > +inline int CDmaElementArrayConstBase::Find( const E *pValue ) const +{ + if ( !pValue ) + return -1; + return B::Find( pValue->GetHandle() ); +} + +template< class E, class B > +inline int CDmaElementArrayConstBase::Find( DmElementHandle_t h ) const +{ + return B::Find( h ); +} + +template< class E, class B > +inline void CDmaElementArrayBase::SetHandle( int i, DmElementHandle_t h ) +{ + BaseClass::Set( i, h ); +} + +template< class E, class B > +inline void CDmaElementArrayBase::Set( int i, E *pElement ) +{ + BaseClass::Set( i, pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID ); +} + +// Adds an element, uses copy constructor +template< class E, class B > +inline int CDmaElementArrayBase::AddToTail( ) +{ + return BaseClass::AddToTail( ); +} + +template< class E, class B > +inline int CDmaElementArrayBase::AddToTail( E *pValue ) +{ + return BaseClass::AddToTail( pValue ? pValue->GetHandle() : DMELEMENT_HANDLE_INVALID ); +} + +template< class E, class B > +inline int CDmaElementArrayBase::AddToTail( DmElementHandle_t src ) +{ + return BaseClass::AddToTail( src ); +} + +template< class E, class B > +inline int CDmaElementArrayBase::InsertBefore( int elem ) +{ + return BaseClass::InsertBefore( elem ); +} + +template< class E, class B > +inline int CDmaElementArrayBase::InsertBefore( int elem, E *pValue ) +{ + return BaseClass::InsertBefore( elem, pValue ? pValue->GetHandle() : DMELEMENT_HANDLE_INVALID ); +} + +template< class E, class B > +inline int CDmaElementArrayBase::InsertBefore( int elem, DmElementHandle_t src ) +{ + return BaseClass::InsertBefore( elem, src ); +} + + + +//----------------------------------------------------------------------------- +// +// Inline methods for CDmrGenericArray +// +//----------------------------------------------------------------------------- +inline const CDmAttribute *CDmrGenericArrayConst::GetAttribute() const +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + +inline bool CDmrGenericArrayConst::IsValid() const +{ + return m_pAttribute != NULL; +} + +inline CDmAttribute *CDmrGenericArray::GetAttribute() +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + +inline const CDmAttribute *CDmrGenericArray::GetAttribute() const +{ + Assert( m_pAttribute ); + return m_pAttribute; +} + + +#endif // DMATTRIBUTEVAR_H diff --git a/public/datamodel/dmehandle.h b/public/datamodel/dmehandle.h new file mode 100644 index 0000000..c3531c2 --- /dev/null +++ b/public/datamodel/dmehandle.h @@ -0,0 +1,263 @@ +//========= Copyright © 1996-2005, Valve LLC, All rights reserved. ============ +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef DMEHANDLE_H +#define DMEHANDLE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "datamodel/idatamodel.h" +#include "datamodel/dmelement.h" + + +//----------------------------------------------------------------------------- +// Purpose: CDmeHandle is a templatized wrapper around DmElementHandle_t +//----------------------------------------------------------------------------- + +template< class DmeType, HandleType_t HandleType = HT_WEAK > +class CDmeHandle : public CDmeElementRefHelper +{ +public: + CDmeHandle() : m_handle( DMELEMENT_HANDLE_INVALID ) + { + } + + explicit CDmeHandle( CDmElement *pObject ) : m_handle( DMELEMENT_HANDLE_INVALID ) + { + Set( pObject ); + } + + CDmeHandle( DmElementHandle_t h ) : m_handle( DMELEMENT_HANDLE_INVALID ) + { + Set( h ); + } + + CDmeHandle( const CDmeHandle< DmeType, HandleType > &handle ) : m_handle( DMELEMENT_HANDLE_INVALID ) + { + Set( handle.m_handle ); + } + + template < class T, HandleType_t HT > + CDmeHandle( const CDmeHandle< T, HT > &handle ) : m_handle( DMELEMENT_HANDLE_INVALID ) + { + DmeType *p = ( T* )NULL; // triggers compiler error if converting from invalid handle type + NOTE_UNUSED( p ); + + Set( handle.GetHandle() ); + } + + ~CDmeHandle() + { + if ( !g_pDataModel ) + return; // some handles are static, and don't get destroyed until program termination + + Unref( m_handle, HandleType ); + } + + template < class T, HandleType_t HT > + CDmeHandle& operator=( const CDmeHandle< T, HT > &handle ) + { + DmeType *p = ( T* )NULL; // triggers compiler error if converting from invalid handle type + NOTE_UNUSED( p ); + + Set( handle.GetHandle() ); + return *this; + } + + DmeType *Get() + { + return static_cast< DmeType* >( g_pDataModel->GetElement( m_handle ) ); + } + + const DmeType *Get() const + { + return static_cast< DmeType* >( g_pDataModel->GetElement( m_handle ) ); + } + + DmElementHandle_t GetHandle() const + { + return m_handle; + } + + void Set( CDmElement *pObject ) + { + Set( pObject ? pObject->GetHandle() : DMELEMENT_HANDLE_INVALID ); + } + + void Set( DmElementHandle_t h ) + { + if ( h == m_handle ) + return; + + Unref( m_handle, HandleType ); + + m_handle = h; + if ( h != DMELEMENT_HANDLE_INVALID ) + { + CDmElement *pElement = g_pDataModel->GetElement( m_handle ); + Assert( pElement ); + if ( pElement && !pElement->IsA( DmeType::GetStaticTypeSymbol() ) ) + { + m_handle = DMELEMENT_HANDLE_INVALID; + } + } + + Ref( m_handle, HandleType ); + } + + operator DmeType*() + { + return Get(); + } + + operator const DmeType*() const + { + return Get(); + } + + operator DmElementHandle_t() const + { + return m_handle; + } + + DmeType* operator->() + { + return Get(); + } + + const DmeType* operator->() const + { + return Get(); + } + + CDmeHandle& operator=( DmElementHandle_t h ) + { + Set( h ); + return *this; + } + + CDmeHandle& operator=( CDmElement *pObject ) + { + Set( pObject ); + return *this; + } + + bool operator==( const CDmeHandle< DmeType > &h ) const + { + return m_handle == h.m_handle; + } + + bool operator!=( const CDmeHandle< DmeType > &h ) const + { + return !operator==( h ); + } + + bool operator<( const CDmeHandle< DmeType > &h ) const + { + return m_handle < h.m_handle; + } + + bool operator==( DmeType *pObject ) const + { + DmElementHandle_t h = pObject ? pObject->GetHandle() : DMELEMENT_HANDLE_INVALID; + return m_handle == h; + } + + bool operator!=( DmeType *pObject ) const + { + return !operator==( pObject ); + } + + bool operator==( DmElementHandle_t h ) const + { + return ( m_handle == h ); + } + + bool operator!=( DmElementHandle_t h ) const + { + return ( m_handle != h ); + } + + operator bool() const + { + return ( Get() != NULL ); + } + + bool operator!() const + { + return ( Get() == NULL ); + } + +private: + DmElementHandle_t m_handle; +}; + +typedef CDmeHandle< CDmElement, HT_STRONG > CDmeCountedHandle; +typedef CDmeHandle< CDmElement, HT_UNDO > CDmeUndoHandle; + + +//----------------------------------------------------------------------------- +// Vector of element handles +//----------------------------------------------------------------------------- +typedef CUtlVector< CDmeHandle > DmeHandleVec_t; + +// NOTE: these methods only append, so if there is already data in the list, it will remain +template< typename T > +inline void ConvertHandleToPtrVector( const CUtlVector< CDmeHandle< T > > &in, CUtlVector< T * > &out ) +{ + int c = in.Count(); + for ( int i = 0; i < c; ++i ) + { + out.AddToTail( in[ i ].Get() ); + } +} + +template< typename T > +inline void ConvertPtrToHandleVector( const CUtlVector< T * > &in, CUtlVector< CDmeHandle< T > > &out ) +{ + int c = in.Count(); + for ( int i = 0; i < c; ++i ) + { + out.AddToTail( CDmeHandle< T >( in[ i ] ) ); + } +} + + +//----------------------------------------------------------------------------- +// helper class for undo classes to allow them to hold onto refcounted element handles +//----------------------------------------------------------------------------- + +template< typename T > +class CDmAttributeUndoStorageType +{ +public: + typedef T UndoStorageType; +}; + +template<> +class CDmAttributeUndoStorageType< DmElementHandle_t > +{ +public: + typedef CDmeUndoHandle UndoStorageType; +}; + +template<> +class CDmAttributeUndoStorageType< CUtlVector< DmElementHandle_t > > +{ +public: + typedef CUtlVector< CDmeUndoHandle > UndoStorageType; +}; + +#endif // DMEHANDLE_H diff --git a/public/datamodel/dmelement.h b/public/datamodel/dmelement.h new file mode 100644 index 0000000..d735f5e --- /dev/null +++ b/public/datamodel/dmelement.h @@ -0,0 +1,1210 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMELEMENT_H +#define DMELEMENT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlmap.h" +#include "tier1/utlhash.h" +#include "tier1/utlvector.h" +#include "tier1/utlsymbol.h" +#include "tier1/mempool.h" +#include "datamodel/attributeflags.h" +#include "datamodel/idatamodel.h" +#include "datamodel/dmattribute.h" +#include "datamodel/dmvar.h" +#include "tier0/vprof.h" +#include "tier1/utlsymbollarge.h" + +//----------------------------------------------------------------------------- +// Forward declarations: +//----------------------------------------------------------------------------- +class CDmAttribute; +class Color; +class Vector; +class QAngle; +class Quaternion; +class VMatrix; +class CDmElement; + + +//----------------------------------------------------------------------------- +// Suppress some SWIG warnings, only for SWIG. Here because many SWIG +// projects %import this header directly +//----------------------------------------------------------------------------- +#ifdef SWIG +%ignore CAttributeReferenceIterator::operator++; +%warnfilter( 302 ) FindReferringElement; +%warnfilter( 302 ) CopyElements; +%warnfilter( 509 ) CDmElement::SetParity; +#endif // SWIG + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +typedef bool (CDmElement::*pfnCommandMethod)( const char *command, const char *args ); + +// element/element array traversal path item - assumes the full path does NOT contain cycles +struct ElementPathItem_t +{ + ElementPathItem_t( DmElementHandle_t hElem = DMELEMENT_HANDLE_INVALID, + DmAttributeHandle_t hAttr = DMATTRIBUTE_HANDLE_INVALID, + int idx = -1 ) + : hElement( hElem ), hAttribute( hAttr ), nIndex( idx ) + { + } + + // only uses hElement so that it can be used to search for elements + bool operator==( const ElementPathItem_t &that ) const + { + return hElement == that.hElement; + } + + DmElementHandle_t hElement; + DmAttributeHandle_t hAttribute; + int nIndex; +}; + + +//----------------------------------------------------------------------------- +// singly-linked attribute list +//----------------------------------------------------------------------------- +struct DmAttributeList_t +{ + DmAttributeList_t() : m_hAttribute( DMATTRIBUTE_HANDLE_INVALID ), m_pNext( NULL ) {} + DmAttributeHandle_t m_hAttribute; + DmAttributeList_t *m_pNext; + +private: + DECLARE_FIXEDSIZE_ALLOCATOR( DmAttributeList_t ); +}; + +//----------------------------------------------------------------------------- +// helper class to allow CDmeHandle access to g_pDataModelImp +//----------------------------------------------------------------------------- +enum HandleType_t +{ + HT_WEAK, + HT_STRONG, + HT_UNDO, +}; + +class CDmeElementRefHelper +{ +protected: + void Ref ( DmElementHandle_t hElement, HandleType_t handleType ); + void Unref( DmElementHandle_t hElement, HandleType_t handleType ); +}; + +//----------------------------------------------------------------------------- +// element reference struct - containing attribute referrers and handle refcount +//----------------------------------------------------------------------------- +struct DmElementReference_t +{ + explicit DmElementReference_t( DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID ) : + m_hElement( hElement ), m_nWeakHandleCount( 0 ), m_nStrongHandleCount( 0 ), m_nUndoHandleCount( 0 ), m_bHasEverBeenReferenced( false ) + { + } + DmElementReference_t( const DmElementReference_t &that ) : + m_hElement( that.m_hElement ), m_nWeakHandleCount( that.m_nWeakHandleCount ), + m_nStrongHandleCount( that.m_nStrongHandleCount ), m_nUndoHandleCount( that.m_nUndoHandleCount ), + m_bHasEverBeenReferenced( that.m_bHasEverBeenReferenced ), m_attributes( that.m_attributes ) + { + } + DmElementReference_t &operator=( const DmElementReference_t &that ) + { + m_hElement = that.m_hElement; + m_nWeakHandleCount = that.m_nWeakHandleCount; + m_nStrongHandleCount = that.m_nStrongHandleCount; + m_nUndoHandleCount = that.m_nUndoHandleCount; + m_bHasEverBeenReferenced = that.m_bHasEverBeenReferenced; + m_attributes.m_hAttribute = that.m_attributes.m_hAttribute; + m_attributes.m_pNext = that.m_attributes.m_pNext; + return *this; + } + ~DmElementReference_t() + { + // Assert( !IsStronglyReferenced() ); + } + + void AddAttribute( CDmAttribute *pAttribute ); + void RemoveAttribute( CDmAttribute *pAttribute ); + bool FindAttribute( CDmAttribute *pAttribute ); + + bool IsStronglyReferenced() // should this element be kept around (even if it's DmElementHandle_t is invalidated) + { + return m_attributes.m_hAttribute != DMATTRIBUTE_HANDLE_INVALID || m_nStrongHandleCount > 0; + } + + bool IsWeaklyReferenced() // should we keep this element's DmElementHandle_t mapped to it's id (even if the element is deleted) + { + return IsStronglyReferenced() || IsReferencedByUndo() || m_nWeakHandleCount > 0; + } + + bool IsReferencedByUndo() + { + return m_nUndoHandleCount > 0; + } + + int EstimateMemoryOverhead() + { + int nBytes = 0; + for ( DmAttributeList_t *pLink = m_attributes.m_pNext; pLink; pLink = pLink->m_pNext ) + { + nBytes += sizeof( DmAttributeList_t ); + } + return nBytes; + } + + DmElementHandle_t m_hElement; + unsigned int m_nWeakHandleCount : 10; // CDmeHandle - for auto-hookup once the element comes back, mainly used by UI + unsigned int m_nStrongHandleCount : 10; // CDmeCountedElementRef - for preventing elements from being truly deleted, mainly used by undo and file root + unsigned int m_nUndoHandleCount : 10; // CDmeUndoHandle - for undo only, to allow it to keep handles to elements that may be conceptually deleted + bool m_bHasEverBeenReferenced : 1; + DmAttributeList_t m_attributes; +}; + + +//----------------------------------------------------------------------------- +// Base DmElement we inherit from in higher-level classes +//----------------------------------------------------------------------------- +class CDmElement +{ +public: + // Can be overridden by derived classes + virtual void OnAttributeChanged( CDmAttribute *pAttribute ) {} + virtual void OnAttributeArrayElementAdded( CDmAttribute *pAttribute, int nFirstElem, int nLastElem ) {} + virtual void OnAttributeArrayElementRemoved( CDmAttribute *pAttribute, int nFirstElem, int nLastElem ) {} + virtual void Resolve() {} + virtual bool IsA( CUtlSymbolLarge typeSymbol ) const; + virtual int GetInheritanceDepth( CUtlSymbolLarge typeSymbol ) const; + virtual void OnElementUnserialized() {} + virtual void OnElementSerialized() {} + virtual int AllocatedSize() const { return sizeof( CDmElement ); } + + // Returns the element handle + DmElementHandle_t GetHandle() const; + + // Attribute iteration, finding + // NOTE: Passing a type into GetAttribute will return NULL if the attribute exists but isn't that type + bool HasAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ) const; + CDmAttribute *GetAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ); + const CDmAttribute *GetAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ) const; + int AttributeCount() const; + CDmAttribute* FirstAttribute(); + const CDmAttribute* FirstAttribute() const; + + // Element name, type, ID + // WARNING: SetType() should only be used by format conversion methods (dmxconvert) + CUtlSymbolLarge GetType() const; + const char * GetTypeString() const; + const char * GetName() const; + const DmObjectId_t& GetId() const; + void SetType( const char *pType ); + void SetName( const char* pName ); + + // Attribute management + CDmAttribute * AddAttribute( const char *pAttributeName, DmAttributeType_t type ); + template< class E > CDmAttribute* AddAttributeElement( const char *pAttributeName ); + template< class E > CDmAttribute* AddAttributeElementArray( const char *pAttributeName ); + void RemoveAttribute( const char *pAttributeName ); + void RemoveAttributeByPtr( CDmAttribute *pAttributeName ); + void RenameAttribute( const char *pAttributeName, const char *pNewName ); + + // get attribute value + template< class T > const T& GetValue( const char *pAttributeName ) const; + template< class T > const T& GetValue( const char *pAttributeName, const T& defaultValue ) const; + const char * GetValueString( const char *pAttributeName ) const; + template< class E > E* GetValueElement( const char *pAttributeName ) const; + + // set attribute value + CDmAttribute* SetValue( const char *pAttributeName, const void *value, size_t size, bool bCreateIfNotFound = true ); + template< class T > CDmAttribute* SetValue( const char *pAttributeName, const T& value, bool bCreateIfNotFound = true ); + template< class E > CDmAttribute* SetValue( const char *pAttributeName, E* value, bool bCreateIfNotFound = true ); + + // set attribute value if the attribute doesn't already exist + CDmAttribute* InitValue( const char *pAttributeName, const void *value, size_t size ); + template< class T > CDmAttribute* InitValue( const char *pAttributeName, const T& value ); + template< class E > CDmAttribute* InitValue( const char *pAttributeName, E* value ); + + // Parses an attribute from a string + // Doesn't create an attribute if it doesn't exist and always preserves attribute type + void SetValueFromString( const char *pAttributeName, const char *value ); + const char *GetValueAsString( const char *pAttributeName, char *pBuffer, size_t buflen ) const; + + // Helpers for our RTTI + template< class E > bool IsA() const; + bool IsA( const char *pTypeName ) const; + int GetInheritanceDepth( const char *pTypeName ) const; + static CUtlSymbolLarge GetStaticTypeSymbol(); + + // Indicates whether this element should be copied or not + void SetShared( bool bShared ); + bool IsShared() const; + + // Copies an element and all its attributes + CDmElement* Copy( TraversalDepth_t depth = TD_DEEP ) const; + + // Copies attributes from a specified element + void CopyAttributesTo( CDmElement *pCopy, TraversalDepth_t depth = TD_DEEP ) const; + + // recursively set fileid's, with option to only change elements in the matched file + void SetFileId( DmFileId_t fileid, TraversalDepth_t depth, bool bOnlyIfMatch = false ); + DmFileId_t GetFileId() const; + + bool GetParity( int bit = 0 ) const; + void SetParity( bool bParity, int bit = 0 ); + void SetParity( bool bParity, TraversalDepth_t depth, int bit = 0 ); // assumes that all elements that should be traversed have a parity of !bParity + + bool IsOnlyInUndo() const; + void SetOnlyInUndo( bool bOnlyInUndo ); + + // returns the first path to the element found traversing all element/element + // array attributes - not necessarily the shortest. + // cycle-safe (skips any references to elements in the current path) + // but may re-traverse elements via different paths + bool FindElement( const CDmElement *pElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth ) const; + bool FindReferer( DmElementHandle_t hElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth ) const; + void RemoveAllReferencesToElement( CDmElement *pElement ); + bool IsStronglyReferenced() { return m_ref.IsStronglyReferenced(); } + + // Estimates the memory usage of the element, its attributes, and child elements + int EstimateMemoryUsage( TraversalDepth_t depth = TD_DEEP ); + + // mostly used for internal stuff, but it's occasionally useful to mark yourself dirty... + bool IsDirty() const; + void MarkDirty( bool dirty = true ); + +protected: + // NOTE: These are protected to ensure that the factory is the only thing that can create these + CDmElement( DmElementHandle_t handle, const char *objectType, const DmObjectId_t &id, const char *objectName, DmFileId_t fileid ); + virtual ~CDmElement(); + + // Used by derived classes to do construction and setting up CDmaVars + void OnConstruction() { } + void OnDestruction() { } + virtual void PerformConstruction(); + virtual void PerformDestruction(); + + virtual void OnAdoptedFromUndo() {} + virtual void OnOrphanedToUndo() {} + + // Internal methods related to RTII + static void SetTypeSymbol( CUtlSymbolLarge sym ); + static bool IsA_Implementation( CUtlSymbolLarge typeSymbol ); + static int GetInheritanceDepth_Implementation( CUtlSymbolLarge typeSymbol, int nCurrentDepth ); + + // Internal method for creating a copy of this element + CDmElement* CopyInternal( TraversalDepth_t depth = TD_DEEP ) const; + + // helper for making attributevarelementarray cleanup easier + template< class T > static void DeleteAttributeVarElementArray( T &array ); + +private: + typedef CUtlMap< DmElementHandle_t, DmElementHandle_t, int > CRefMap; + + // Bogus constructor + CDmElement(); + + // internal recursive copy method - builds refmap of old element's handle -> copy's handle, and uses it to fixup references + void CopyAttributesTo( CDmElement *pCopy, CRefMap &refmap, TraversalDepth_t depth ) const; + void CopyElementAttribute( const CDmAttribute *pAttr, CDmAttribute *pCopyAttr, CRefMap &refmap, TraversalDepth_t depth ) const; + void CopyElementArrayAttribute( const CDmAttribute *pAttr, CDmAttribute *pCopyAttr, CRefMap &refmap, TraversalDepth_t depth ) const; + void FixupReferences( CUtlHashFast< DmElementHandle_t > &visited, const CRefMap &refmap, TraversalDepth_t depth ); + + void SetFileId( DmFileId_t fileid ); + void SetFileId_R( CUtlHashFast< DmElementHandle_t > &visited, DmFileId_t fileid, TraversalDepth_t depth, DmFileId_t match, bool bOnlyIfMatch ); + + CDmAttribute* CreateAttribute( const char *pAttributeName, DmAttributeType_t type ); + void RemoveAttribute( CDmAttribute **pAttrRef ); + CDmAttribute* AddExternalAttribute( const char *pAttributeName, DmAttributeType_t type, void *pMemory ); + CDmAttribute *FindAttribute( const char *pAttributeName ) const; + + void Purge(); + void SetId( const DmObjectId_t &id ); + + void MarkAttributesClean(); + + void DisableOnChangedCallbacks(); + void EnableOnChangedCallbacks(); + bool AreOnChangedCallbacksEnabled(); + void FinishUnserialization(); + + // Used by the undo system only. + void AddAttributeByPtr( CDmAttribute *ptr ); + void RemoveAttributeByPtrNoDelete( CDmAttribute *ptr ); + + // Should only be called from datamodel, who will take care of changing the fileset entry as well + void ChangeHandle( DmElementHandle_t handle ); + + // returns element reference struct w/ list of referrers and handle count + DmElementReference_t* GetReference(); + void SetReference( const DmElementReference_t &ref ); + + // Estimates memory usage + int EstimateMemoryUsage( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ); + +private: + DmObjectId_t m_Id; // UUID's like to be quad-aligned + +protected: + CDmaString m_Name; + +private: + DmElementReference_t m_ref; + CDmAttribute *m_pAttributes; + CUtlSymbolLarge m_Type; + DmFileId_t m_fileId; + + bool m_bDirty : 1; + bool m_bOnChangedCallbacksEnabled : 1; + bool m_bOnlyInUndo : 1; // only accessibly from the undo system + uint m_nParityBits : 28; // used as temporary state during traversal to avoid searching + + // Stores the type symbol + static CUtlSymbolLarge m_classType; + + // Factories can access our constructors + template friend class CDmElementFactory; + template friend class CDmAbstractElementFactory; + template< class T > friend class CDmaVar; + template< class T > friend class CDmaArray; + template< class T > friend class CDmaElementArray; + template< class T, class B > friend class CDmaDecorator; + template< class T > friend class CDmrElementArray; + + friend class CDmElementFactoryDefault; + friend class CDmeElementAccessor; + friend class CDmeOperator; + + template< class T > + friend void CopyElements( const CUtlVector< T* > &from, CUtlVector< T* > &to, TraversalDepth_t depth ); + + DECLARE_FIXEDSIZE_ALLOCATOR( CDmElement ); + + typedef CDmElement BaseClass; // only CDmElement has itself as it's BaseClass - this lets us know we're at the top of the hierarchy +}; + + +//----------------------------------------------------------------------------- +// Fast dynamic cast +//----------------------------------------------------------------------------- +template< class E > +inline E *CastElement( CDmElement *pElement ) +{ + if ( pElement && pElement->IsA( E::GetStaticTypeSymbol() ) ) + return static_cast< E* >( pElement ); + return NULL; +} + +template< class E > +inline const E *CastElement( const CDmElement *pElement ) +{ + if ( pElement && pElement->IsA( E::GetStaticTypeSymbol() ) ) + return static_cast< const E* >( pElement ); + return NULL; +} + + +//----------------------------------------------------------------------------- +// Constant fast dynamic cast +//----------------------------------------------------------------------------- +template< class E > +const inline E *CastElementConst( const CDmElement *pElement ) +{ + if ( pElement && pElement->IsA( E::GetStaticTypeSymbol() ) ) + return static_cast< const E* >( pElement ); + return NULL; +} + + +//----------------------------------------------------------------------------- +// type-safe element creation and accessor helpers - infers type name string from actual type +//----------------------------------------------------------------------------- +template< class E > +inline E *GetElement( DmElementHandle_t hElement ) +{ + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + return CastElement< E >( pElement ); +} + + +//----------------------------------------------------------------------------- +// Typesafe element creation + destruction +//----------------------------------------------------------------------------- +template< class E > +inline E *CreateElement( const char *pObjectName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL ) +{ + return GetElement< E >( g_pDataModel->CreateElement( E::GetStaticTypeSymbol(), pObjectName, fileid, pObjectID ) ); +} + +template< class E > +inline E *CreateElement( const char *pElementType, const char *pObjectName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL ) +{ + return GetElement< E >( g_pDataModel->CreateElement( pElementType, pObjectName, fileid, pObjectID ) ); +} + +inline void DestroyElement( CDmElement *pElement ) +{ + if ( pElement ) + { + g_pDataModel->DestroyElement( pElement->GetHandle() ); + } +} + +void DestroyElement( CDmElement *pElement, TraversalDepth_t depth ); + + +//----------------------------------------------------------------------------- +// allows elements to chain OnAttributeChanged up to their parents (or at least, referrers) +//----------------------------------------------------------------------------- +void InvokeOnAttributeChangedOnReferrers( DmElementHandle_t hElement, CDmAttribute *pChangedAttr ); + + +//----------------------------------------------------------------------------- +// Gets attributes +//----------------------------------------------------------------------------- +inline CDmAttribute *CDmElement::GetAttribute( const char *pAttributeName, DmAttributeType_t type ) +{ + CDmAttribute *pAttribute = FindAttribute( pAttributeName ); + if ( ( type != AT_UNKNOWN ) && pAttribute && ( pAttribute->GetType() != type ) ) + return NULL; + return pAttribute; +} + +inline const CDmAttribute *CDmElement::GetAttribute( const char *pAttributeName, DmAttributeType_t type ) const +{ + CDmAttribute *pAttribute = FindAttribute( pAttributeName ); + if ( ( type != AT_UNKNOWN ) && pAttribute && ( pAttribute->GetType() != type ) ) + return NULL; + return pAttribute; +} + + +//----------------------------------------------------------------------------- +// AddAttribute calls +//----------------------------------------------------------------------------- +inline CDmAttribute *CDmElement::AddAttribute( const char *pAttributeName, DmAttributeType_t type ) +{ + CDmAttribute *pAttribute = FindAttribute( pAttributeName ); + if ( pAttribute ) + return ( pAttribute->GetType() == type ) ? pAttribute : NULL; + pAttribute = CreateAttribute( pAttributeName, type ); + return pAttribute; +} + +template< class E > inline CDmAttribute *CDmElement::AddAttributeElement( const char *pAttributeName ) +{ + CDmAttribute *pAttribute = AddAttribute( pAttributeName, AT_ELEMENT ); + if ( !pAttribute ) + return NULL; + + // FIXME: If the attribute exists but has a different element type symbol, should we complain? + pAttribute->SetElementTypeSymbol( E::GetStaticTypeSymbol() ); + return pAttribute; +} + +template< class E > inline CDmAttribute *CDmElement::AddAttributeElementArray( const char *pAttributeName ) +{ + CDmAttribute *pAttribute = AddAttribute( pAttributeName, AT_ELEMENT_ARRAY ); + if ( !pAttribute ) + return NULL; + + // FIXME: If the attribute exists but has a different element type symbol, should we complain? + pAttribute->SetElementTypeSymbol( E::GetStaticTypeSymbol() ); + return pAttribute; +} + + +//----------------------------------------------------------------------------- +// GetValue methods +//----------------------------------------------------------------------------- +template< class T > +inline const T& CDmElement::GetValue( const char *pAttributeName, const T& defaultVal ) const +{ + const CDmAttribute *pAttribute = FindAttribute( pAttributeName ); + if ( pAttribute != NULL ) + return pAttribute->GetValue(); + return defaultVal; +} + +template< class T > +inline const T& CDmElement::GetValue( const char *pAttributeName ) const +{ + static CDmaVar defaultVal; + return GetValue( pAttributeName, defaultVal.Get() ); +} + +inline const char *CDmElement::GetValueString( const char *pAttributeName ) const +{ + CUtlSymbolLarge symbol = GetValue( pAttributeName ); + if ( symbol == UTL_INVAL_SYMBOL_LARGE ) + return NULL; + + return symbol.String(); +} + +template< class E > +inline E* CDmElement::GetValueElement( const char *pAttributeName ) const +{ + DmElementHandle_t h = GetValue< DmElementHandle_t >( pAttributeName ); + return GetElement( h ); +} + + +//----------------------------------------------------------------------------- +// SetValue methods +//----------------------------------------------------------------------------- +template< class T > +inline CDmAttribute* CDmElement::SetValue( const char *pAttributeName, const T& value, bool bCreateIfNotFound /*= true*/ ) +{ + CDmAttribute *pAttribute = FindAttribute( pAttributeName ); + if ( !pAttribute && bCreateIfNotFound ) + { + pAttribute = CreateAttribute( pAttributeName, CDmAttributeInfo::AttributeType() ); + } + if ( pAttribute ) + { + pAttribute->SetValue( value ); + return pAttribute; + } + return NULL; +} + +template< class E > +inline CDmAttribute* CDmElement::SetValue( const char *pAttributeName, E* pElement, bool bCreateIfNotFound /*= true*/ ) +{ + DmElementHandle_t hElement = pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID; + return SetValue( pAttributeName, hElement, bCreateIfNotFound ); +} + +template<> +inline CDmAttribute* CDmElement::SetValue( const char *pAttributeName, const char *pValue, bool bCreateIfNotFound /*= true*/ ) +{ + // We don't want to add any extra entries into the string table so if bCreateIfNotFound is + // false, then check to see if the attribute exists before adding the string to the table. + if ( !bCreateIfNotFound ) + { + if ( HasAttribute( pAttributeName, AT_STRING ) == false ) + return NULL; + } + + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + return SetValue( pAttributeName, symbol ); +} + +template<> +inline CDmAttribute* CDmElement::SetValue( const char *pAttributeName, char *pValue, bool bCreateIfNotFound /*= true*/ ) +{ + return SetValue( pAttributeName, (const char *)pValue, bCreateIfNotFound ); +} + +inline CDmAttribute* CDmElement::SetValue( const char *pAttributeName, const void *pValue, size_t nSize, bool bCreateIfNotFound /*= true*/ ) +{ + CUtlBinaryBlock buf( pValue, nSize ); + return SetValue( pAttributeName, buf, bCreateIfNotFound ); +} + + +//----------------------------------------------------------------------------- +// AddValue methods( set value if not found ) +//----------------------------------------------------------------------------- +template< class T > +inline CDmAttribute* CDmElement::InitValue( const char *pAttributeName, const T& value ) +{ + CDmAttribute *pAttribute = GetAttribute( pAttributeName ); + if ( !pAttribute ) + return SetValue( pAttributeName, value ); + return pAttribute; +} + +template< class E > +inline CDmAttribute* CDmElement::InitValue( const char *pAttributeName, E* pElement ) +{ + DmElementHandle_t hElement = pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID; + return InitValue( pAttributeName, hElement ); +} + +template<> +inline CDmAttribute* CDmElement::InitValue( const char *pAttributeName, const char *pValue ) +{ + CUtlSymbolLarge symbol = g_pDataModel->GetSymbol( pValue ); + return InitValue( pAttributeName, symbol ); +} + +inline CDmAttribute* CDmElement::InitValue( const char *pAttributeName, const void *pValue, size_t size ) +{ + CDmAttribute *pAttribute = GetAttribute( pAttributeName ); + if ( !pAttribute ) + return SetValue( pAttributeName, pValue, size ); + return pAttribute; +} + + +//----------------------------------------------------------------------------- +// Returns the type, name, id, fileId +//----------------------------------------------------------------------------- +inline CUtlSymbolLarge CDmElement::GetType() const +{ + return m_Type; +} + +inline const char *CDmElement::GetTypeString() const +{ + return m_Type.String(); +} + +inline const char *CDmElement::GetName() const +{ + return m_Name.Get(); +} + +inline void CDmElement::SetName( const char* pName ) +{ + m_Name.Set( pName ); +} + +inline const DmObjectId_t& CDmElement::GetId() const +{ + return m_Id; +} + +inline DmFileId_t CDmElement::GetFileId() const +{ + return m_fileId; +} + + +//----------------------------------------------------------------------------- +// Controls whether the element should be copied by default +//----------------------------------------------------------------------------- +inline void CDmElement::SetShared( bool bShared ) +{ + if ( bShared ) + { + SetValue< bool >( "shared", true ); + } + else + { + RemoveAttribute( "shared" ); + } +} + +inline bool CDmElement::IsShared() const +{ + return GetValue< bool >( "shared" ); // if attribute doesn't exist, returns default bool value, which is false +} + + +//----------------------------------------------------------------------------- +// Copies attributes from a specified element +//----------------------------------------------------------------------------- +inline CDmElement* CDmElement::Copy( TraversalDepth_t depth ) const +{ + return CopyInternal( depth ); +} + + +//----------------------------------------------------------------------------- +// RTTI +//----------------------------------------------------------------------------- +inline bool CDmElement::IsA_Implementation( CUtlSymbolLarge typeSymbol ) +{ + return ( m_classType == typeSymbol ) || ( UTL_INVAL_SYMBOL_LARGE == typeSymbol ); +} + +inline int CDmElement::GetInheritanceDepth_Implementation( CUtlSymbolLarge typeSymbol, int nCurrentDepth ) +{ + return IsA_Implementation( typeSymbol ) ? nCurrentDepth : -1; +} + +inline CUtlSymbolLarge CDmElement::GetStaticTypeSymbol() +{ + return m_classType; +} + +inline bool CDmElement::IsA( const char *pTypeName ) const +{ + CUtlSymbolLarge typeSymbol = g_pDataModel->GetSymbol( pTypeName ); + return IsA( typeSymbol ); +} + +template< class E > inline bool CDmElement::IsA() const +{ + return IsA( E::GetStaticTypeSymbol() ); +} + + +//----------------------------------------------------------------------------- +// Helper for finding elements that refer to this element +//----------------------------------------------------------------------------- +class CAttributeReferenceIterator +{ +public: + explicit CAttributeReferenceIterator( const CDmElement *pElement ) : + m_curr ( pElement ? g_pDataModel->FirstAttributeReferencingElement( pElement->GetHandle() ) : DMATTRIBUTE_REFERENCE_ITERATOR_INVALID ), + m_fileid( pElement ? pElement->GetFileId() : DMFILEID_INVALID ) + { + } + + operator bool() const { return m_curr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; } + + CDmAttribute* operator*() const { return GetAttribute(); } + + CDmAttribute *GetAttribute() const { return g_pDataModel->GetAttribute( m_curr ); } + CDmElement *GetOwner() const { if ( CDmAttribute *pAttr = GetAttribute() ) return pAttr->GetOwner(); return NULL; } + + CAttributeReferenceIterator& operator++() // prefix + { + m_curr = g_pDataModel->NextAttributeReferencingElement( m_curr ); + return *this; + } + CAttributeReferenceIterator operator++( int ) // postfix + { + CAttributeReferenceIterator prev = *this; + m_curr = g_pDataModel->NextAttributeReferencingElement( m_curr ); + return prev; + } + + template< class T > + T *FilterReference( CUtlSymbolLarge symAttrName = UTL_INVAL_SYMBOL_LARGE, bool bMustBeInSameFile = false, TraversalDepth_t depth = TD_ALL ) const + { + CDmAttribute *pAttribute = g_pDataModel->GetAttribute( m_curr ); + Assert( pAttribute ); + if ( !pAttribute ) + return NULL; + + if ( !ShouldTraverse( pAttribute, depth ) ) + return NULL; + + T *pParent = CastElement< T >( pAttribute->GetOwner() ); + if ( !pParent ) + return NULL; + + if ( symAttrName != UTL_INVAL_SYMBOL_LARGE && pAttribute->GetNameSymbol() != symAttrName ) + return NULL; + + if ( bMustBeInSameFile && ( pParent->GetFileId() != m_fileid ) ) + return NULL; + + return pParent; + } + +private: + DmAttributeReferenceIterator_t m_curr; + DmFileId_t m_fileid; +}; + +template< class T > +T *FindReferringElement( const CDmElement *pElement, const char *pAttrName = NULL, bool bMustBeInSameFile = true, TraversalDepth_t depth = TD_ALL ) +{ + CUtlSymbolLarge sym = pAttrName ? g_pDataModel->GetSymbol( pAttrName ) : UTL_INVAL_SYMBOL_LARGE; + return FindReferringElement< T >( pElement, sym, bMustBeInSameFile, depth ); +} + +template< class T > +T *FindReferringElement( const CDmElement *pElement, CUtlSymbolLarge symAttrName = UTL_INVAL_SYMBOL_LARGE, bool bMustBeInSameFile = true, TraversalDepth_t depth = TD_ALL ) +{ + for ( CAttributeReferenceIterator it( pElement ); it; ++it ) + { + if ( T *pParent = it.FilterReference< T >( symAttrName, bMustBeInSameFile, depth ) ) + return pParent; + } + + return NULL; +} + +template< class T > +bool FindReferringElements( CUtlVector< T * >& list, const CDmElement *pElement, CUtlSymbolLarge symAttrName, bool bMustBeInSameFile = true, TraversalDepth_t depth = TD_ALL ) +{ + for ( CAttributeReferenceIterator it( pElement ); it; ++it ) + { + if ( T *pParent = it.FilterReference< T >( symAttrName, bMustBeInSameFile, depth ) ) + { + list.AddToTail( pParent ); + } + } + + return list.Count() > 0; +} + + +void RemoveElementFromRefereringAttributes( CDmElement *pElement, bool bPreserveOrder = true ); + + +template< class T > +void FindAncestorsReferencingElement( const CDmElement *target, CUtlVector< T* >& list ) +{ + FindReferringElements( list, target, UTL_INVAL_SYMBOL_LARGE, false ); +} + +template< class T > +T *FindAncestorReferencingElement( const CDmElement *target ) +{ + return FindReferringElement< T >( target, UTL_INVAL_SYMBOL_LARGE, false ); +} + +template< class T > +T *FindAncestorReferencingElement_R_Impl( CUtlRBTree< CDmElement * >& visited, CDmElement *check ) +{ + if ( visited.Find( check ) != visited.InvalidIndex() ) + return NULL; + + visited.Insert( check ); + + // Pass one, see if it's in this ancestor list + for ( CAttributeReferenceIterator it( check ); it; ++it ) + { + if ( T *pParent = it.FilterReference< T >() ) + return pParent; + } + + for ( CAttributeReferenceIterator it( check ); it; ++it ) + { + if ( CDmElement *pParent = it.GetOwner() ) + { + T *found = FindAncestorReferencingElement_R_Impl< T >( visited, pParent ); + if ( found ) + return found; + } + } + return NULL; +} + +template< class T > +T *FindAncestorReferencingElement_R( CDmElement *target ) +{ + if ( !target ) + return NULL; + + CUtlRBTree< CDmElement * > visited( 0, 0, DefLessFunc( CDmElement * ) ); + return FindAncestorReferencingElement_R_Impl< T >( visited, target ); +} + +// finds elements of type T that indirectly reference pElement +template< class T > +void FindAncestorsOfElement_Impl( CUtlRBTree< CDmElement * >& visited, CDmElement *pElement, CUtlVector< T* > &ancestors, bool bRecursePastFoundAncestors ) +{ + // Pass one, see if it's in this ancestor list + for ( CAttributeReferenceIterator it( pElement ); it; ++it ) + { + if ( CDmElement *pParent = it.GetOwner() ) + { + if ( visited.Find( pParent ) != visited.InvalidIndex() ) + continue; + + visited.Insert( pParent ); + + if ( T *pT = CastElement< T >( pParent ) ) + { + ancestors.AddToTail( pT ); + if ( !bRecursePastFoundAncestors ) + continue; + } + FindAncestorsOfElement_Impl( visited, pParent, ancestors, bRecursePastFoundAncestors ); + } + } +} + +// finds elements of type T that indirectly reference pElement +template< class T > +void FindAncestorsOfElement( CDmElement *pElement, CUtlVector< T* > &ancestors, bool bRecursePastFoundAncestors ) +{ + if ( !pElement ) + return; + + CUtlRBTree< CDmElement * > visited( 0, 0, DefLessFunc( CDmElement * ) ); + FindAncestorsOfElement_Impl< T >( visited, pElement, ancestors, bRecursePastFoundAncestors ); +} + + +//----------------------------------------------------------------------------- +// +// generic element tree traversal helper class +// +//----------------------------------------------------------------------------- + +class CElementTreeTraversal +{ +public: + CElementTreeTraversal( CDmElement *pRoot, const char *pAttrName ); + + enum { NOT_VISITED = -2, VISITING = -1 }; + + void Reset( CDmElement *pRoot, const char *pAttrName ); + + bool IsValid() { return m_state.Count() > 0; } + CDmElement *Next( bool bSkipChildren = false ); + + int CurrentDepth() { return m_state.Count() - 1; } + CDmElement *GetElement(); + CDmElement *GetParent ( int i ); + int GetChildIndex( int i ); + +private: + struct State_t + { + State_t( CDmElement *p, int i ) : pElement( p ), nIndex( i ) {} + CDmElement *pElement; + int nIndex; // -2: not yet visited, -1: visiting self, 0+: visiting children + }; + + CUtlVector< State_t > m_state; + const char *m_pAttrName; +}; + + +//----------------------------------------------------------------------------- +// +// element-specific unique name generation methods +// +//----------------------------------------------------------------------------- + +template< class T > +struct ElementArrayNameAccessor +{ + ElementArrayNameAccessor( const CUtlVector< T > &array ) : m_array( array ) {} + int Count() const + { + return m_array.Count(); + } + const char *operator[]( int i ) const + { + CDmElement *pElement = GetElement< CDmElement >( m_array[ i ] ); + return pElement ? pElement->GetName() : NULL; + } +private: + const CUtlVector< T > &m_array; +}; + +template< class E > +struct ElementArrayNameAccessor< E* > +{ + ElementArrayNameAccessor( const CUtlVector< E* > &array ) : m_array( array ) {} + int Count() const + { + return m_array.Count(); + } + const char *operator[]( int i ) const + { + E *pElement = m_array[ i ]; + return pElement ? pElement->GetName() : NULL; + } +private: + const CUtlVector< E* > &m_array; +}; + + +// returns startindex if none found, 2 if only "prefix" found, and n+1 if "prefixn" found +int GenerateUniqueNameIndex( const char *prefix, const CUtlVector< DmElementHandle_t > &array, int startindex = 0 ); + +bool GenerateUniqueName( char *name, int memsize, const char *prefix, const CUtlVector< DmElementHandle_t > &array ); + +int SplitStringIntoBaseAndIntegerSuffix( const char *pName, int len, char *pBaseName ); + +void MakeElementNameUnique( CDmElement *pElement, const CUtlVector< DmElementHandle_t > &array ); + + +//----------------------------------------------------------------------------- +// helper for making attributevarelementarray cleanup easier +//----------------------------------------------------------------------------- +template< class T > +inline void CDmElement::DeleteAttributeVarElementArray( T &array ) +{ + int nElements = array.Count(); + for ( int i = 0; i < nElements; ++i ) + { + g_pDataModel->DestroyElement( array.GetHandle( i ) ); + } + array.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Default size computation +//----------------------------------------------------------------------------- +template< class T > +int DmeEstimateMemorySize( T* pElement ) +{ + return sizeof( T ); +} + +//----------------------------------------------------------------------------- +// copy groups of elements together so that references between them are maintained +//----------------------------------------------------------------------------- +template< class T > +void CopyElements( const CUtlVector< T* > &from, CUtlVector< T* > &to, TraversalDepth_t depth = TD_DEEP ) +{ + CDisableUndoScopeGuard sg; + + CUtlMap< DmElementHandle_t, DmElementHandle_t, int > refmap( DefLessFunc( DmElementHandle_t ) ); + + int c = from.Count(); + for ( int i = 0; i < c; ++i ) + { + T *pCopy = NULL; + + if ( CDmElement *pFrom = from[ i ] ) + { + int idx = refmap.Find( pFrom->GetHandle() ); + if ( idx != refmap.InvalidIndex() ) + { + pCopy = GetElement< T >( refmap[ idx ] ); + } + else + { + pCopy = GetElement< T >( g_pDataModel->CreateElement( pFrom->GetType(), pFrom->GetName(), pFrom->GetFileId() ) ); + if ( pCopy ) + { + pFrom->CopyAttributesTo( pCopy, refmap, depth ); + } + } + } + + to.AddToTail( pCopy ); + } + + CUtlHashFast< DmElementHandle_t > visited; + uint nPow2Size = 1; + while( nPow2Size < refmap.Count() ) + { + nPow2Size <<= 1; + } + visited.Init( nPow2Size ); + + for ( int i = 0; i < c; ++i ) + { + CDmElement *pTo = to[ i ]; + if ( !pTo ) + continue; + + to[ i ]->FixupReferences( visited, refmap, depth ); + } +} + + +//----------------------------------------------------------------------------- +// Helper macro to create an element; this is used for elements that are helper base classes +//----------------------------------------------------------------------------- +#define DEFINE_UNINSTANCEABLE_ELEMENT( className, baseClassName ) \ + protected: \ + className( DmElementHandle_t handle, const char *pElementTypeName, const DmObjectId_t &id, const char *pElementName, DmFileId_t fileid ) : \ + baseClassName( handle, pElementTypeName, id, pElementName, fileid ) \ + { \ + } \ + virtual ~className() \ + { \ + } \ + void OnConstruction(); \ + void OnDestruction(); \ + virtual void PerformConstruction() \ + { \ + BaseClass::PerformConstruction(); \ + OnConstruction(); \ + } \ + virtual void PerformDestruction() \ + { \ + OnDestruction(); \ + BaseClass::PerformDestruction(); \ + } \ + virtual int AllocatedSize() const { return DmeEstimateMemorySize( this ); } \ + \ + private: \ + typedef baseClassName BaseClass; \ + + +//----------------------------------------------------------------------------- +// Helper macro to create the class factory +//----------------------------------------------------------------------------- +#define DEFINE_ELEMENT( className, baseClassName ) \ + public: \ + virtual bool IsA( CUtlSymbolLarge typeSymbol ) const \ + { \ + return IsA_Implementation( typeSymbol );\ + } \ + \ + bool IsA( const char *pTypeName ) const \ + { \ + CUtlSymbolLarge typeSymbol = g_pDataModel->GetSymbol( pTypeName ); \ + return IsA( typeSymbol ); \ + } \ + \ + template< class T > bool IsA() const \ + { \ + return IsA( T::GetStaticTypeSymbol() ); \ + } \ + \ + virtual int GetInheritanceDepth( CUtlSymbolLarge typeSymbol ) const \ + { \ + return GetInheritanceDepth_Implementation( typeSymbol, 0 ); \ + } \ + \ + static CUtlSymbolLarge GetStaticTypeSymbol( ) \ + { \ + return m_classType; \ + } \ + \ + className* Copy( TraversalDepth_t depth = TD_DEEP ) const \ + { \ + return static_cast< className* >( CopyInternal( depth ) ); \ + } \ + protected: \ + className( DmElementHandle_t handle, const char *pElementTypeName, const DmObjectId_t &id, const char *pElementName, DmFileId_t fileid ) : \ + baseClassName( handle, pElementTypeName, id, pElementName, fileid ) \ + { \ + } \ + virtual ~className() \ + { \ + } \ + void OnConstruction(); \ + void OnDestruction(); \ + virtual void PerformConstruction() \ + { \ + BaseClass::PerformConstruction(); \ + OnConstruction(); \ + } \ + virtual void PerformDestruction() \ + { \ + OnDestruction(); \ + BaseClass::PerformDestruction(); \ + } \ + static void SetTypeSymbol( CUtlSymbolLarge typeSymbol ) \ + { \ + m_classType = typeSymbol; \ + } \ + \ + static bool IsA_Implementation( CUtlSymbolLarge typeSymbol ) \ + { \ + if ( typeSymbol == m_classType ) \ + return true; \ + return BaseClass::IsA_Implementation( typeSymbol ); \ + } \ + \ + static int GetInheritanceDepth_Implementation( CUtlSymbolLarge typeSymbol, int nCurrentDepth ) \ + { \ + if ( typeSymbol == m_classType ) \ + return nCurrentDepth; \ + return BaseClass::GetInheritanceDepth_Implementation( typeSymbol, nCurrentDepth+1 );\ + } \ + virtual int AllocatedSize() const { return DmeEstimateMemorySize( this ); } \ + \ + private: \ + DECLARE_FIXEDSIZE_ALLOCATOR( className ); \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; \ + template friend class CDmElementFactory; \ + template friend class CDmAbstractElementFactory; \ + static CUtlSymbolLarge m_classType + +#define IMPLEMENT_ELEMENT( className ) \ + CUtlSymbolLarge className::m_classType = UTL_INVAL_SYMBOL_LARGE; \ + DEFINE_FIXEDSIZE_ALLOCATOR( className, 1024, CUtlMemoryPool::GROW_SLOW ); + + +#endif // DMELEMENT_H diff --git a/public/datamodel/dmelementfactoryhelper.h b/public/datamodel/dmelementfactoryhelper.h new file mode 100644 index 0000000..5274a73 --- /dev/null +++ b/public/datamodel/dmelementfactoryhelper.h @@ -0,0 +1,248 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +//http://www.nfl.com/gamecenter/2010010900/2009/POST18/eagles@cowboys#tab:watch +// Purpose: +// +//============================================================================= + +#ifndef DMELEMENTFACTORYHELPER_H +#define DMELEMENTFACTORYHELPER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "datamodel/idatamodel.h" +#include "datamodel/dmelement.h" +#include "datamodel/dmattribute.h" +#include "datamodel/dmattributevar.h" +#include "tier1/utlvector.h" +#include "tier1/utlsymbollarge.h" + + +//----------------------------------------------------------------------------- +// Internal interface for IDmElementFactory +//----------------------------------------------------------------------------- +class CDmElementFactoryInternal : public IDmElementFactory +{ +public: + virtual void SetElementTypeSymbol( CUtlSymbolLarge sym ) = 0; + virtual bool IsAbstract() const = 0; + + virtual CUtlSymbolLarge GetElementTypeSymbol() const = 0; + virtual CUtlSymbolLarge GetParentElementTypeSymbol() const = 0; + + virtual void AddOnElementCreatedCallback( IDmeElementCreated *pCallback ); + virtual void RemoveOnElementCreatedCallback( IDmeElementCreated *pCallback ); + virtual void OnElementCreated( CDmElement* pElement ); + +private: + CUtlVector< IDmeElementCreated* > m_CallBackList; +}; + + + + + + +//----------------------------------------------------------------------------- +// Class used to register factories into a global list +//----------------------------------------------------------------------------- +class CDmElementFactoryHelper +{ +public: + // Static list of helpers + static CDmElementFactoryHelper *s_pHelpers[2]; + + // Create all the hud elements + static void InstallFactories( ); + +public: + // Construction + CDmElementFactoryHelper( const char *pClassName, CDmElementFactoryInternal *pFactory, bool bIsStandardFactory ); + + // Accessors + CDmElementFactoryHelper *GetNext () { return m_pNext; } + CDmElementFactoryHelper *GetParent () { return m_pParent; } + CDmElementFactoryHelper *GetChild () { return m_pChild; } + CDmElementFactoryHelper *GetSibling() { return m_pSibling; } + + const char *GetClassname(); + CDmElementFactoryInternal *GetFactory(); + +private: + CDmElementFactoryHelper() {} + + // Next factory in list + CDmElementFactoryHelper *m_pNext; + + // class hierarchy links + CDmElementFactoryHelper *m_pParent; + CDmElementFactoryHelper *m_pChild; + CDmElementFactoryHelper *m_pSibling; + + // Creation function to use for this technology + CDmElementFactoryInternal *m_pFactory; + const char *m_pszClassname; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline const char *CDmElementFactoryHelper::GetClassname() +{ + return m_pszClassname; +} + +inline CDmElementFactoryInternal *CDmElementFactoryHelper::GetFactory() +{ + return m_pFactory; +} + + +//----------------------------------------------------------------------------- +// Helper Template factory for simple creation of factories +//----------------------------------------------------------------------------- +template < class T > +class CDmElementFactory : public CDmElementFactoryInternal +{ +public: + CDmElementFactory( const char *pLookupName ) : m_pLookupName( pLookupName ) {} + + // Creation, destruction + virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id ) + { + return new T( handle, m_pLookupName, id, pElementName, fileid ); + } + + virtual void Destroy( DmElementHandle_t hElement ) + { + CDmElement *pElement = g_pDataModel->GetElement( hElement ); + if ( pElement ) + { + T *pActualElement = static_cast< T* >( pElement ); + delete pActualElement; + } + } + + // Sets the type symbol, used for "isa" implementation + virtual void SetElementTypeSymbol( CUtlSymbolLarge sym ) + { + T::SetTypeSymbol( sym ); + } + + virtual bool IsAbstract() const { return false; } + + virtual CUtlSymbolLarge GetElementTypeSymbol() const + { + return T::GetStaticTypeSymbol(); + } + virtual CUtlSymbolLarge GetParentElementTypeSymbol() const + { + CUtlSymbolLarge baseClassSym = T::BaseClass::GetStaticTypeSymbol(); + if ( baseClassSym == T::GetStaticTypeSymbol() ) + return UTL_INVAL_SYMBOL_LARGE; // only CDmElement has itself as it's BaseClass - this lets us know we're at the top of the hierarchy + return baseClassSym; + } + +private: + const char *m_pLookupName; +}; + + +template < class T > +class CDmAbstractElementFactory : public CDmElementFactoryInternal +{ +public: + CDmAbstractElementFactory() {} + + // Creation, destruction + virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id ) + { + return NULL; + } + + virtual void Destroy( DmElementHandle_t hElement ) + { + } + + // Sets the type symbol, used for "isa" implementation + virtual void SetElementTypeSymbol( CUtlSymbolLarge sym ) + { + T::SetTypeSymbol( sym ); + } + + virtual bool IsAbstract() const { return true; } + + virtual CUtlSymbolLarge GetElementTypeSymbol() const + { + return T::GetStaticTypeSymbol(); + } + virtual CUtlSymbolLarge GetParentElementTypeSymbol() const + { + CUtlSymbolLarge baseClassSym = T::BaseClass::GetStaticTypeSymbol(); + if ( baseClassSym == T::GetStaticTypeSymbol() ) + return UTL_INVAL_SYMBOL_LARGE; // only CDmElement has itself as it's BaseClass - this lets us know we're at the top of the hierarchy + return baseClassSym; + } + +private: +}; + + +//----------------------------------------------------------------------------- +// Helper macro to create the class factory +//----------------------------------------------------------------------------- +#if defined( MOVIEOBJECTS_LIB ) || defined ( DATAMODEL_LIB ) || defined ( DMECONTROLS_LIB ) || defined ( MDLOBJECTS_LIB ) + +#define IMPLEMENT_ELEMENT_FACTORY( lookupName, className ) \ + IMPLEMENT_ELEMENT( className ) \ + CDmElementFactory< className > g_##className##_Factory( #lookupName ); \ + CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, true ); \ + className *g_##className##LinkerHack = NULL; + +#define IMPLEMENT_ABSTRACT_ELEMENT( lookupName, className ) \ + IMPLEMENT_ELEMENT( className ) \ + CDmAbstractElementFactory< className > g_##className##_Factory; \ + CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, true ); \ + className *g_##className##LinkerHack = NULL; + +#else + +#define IMPLEMENT_ELEMENT_FACTORY( lookupName, className ) \ + IMPLEMENT_ELEMENT( className ) \ + CDmElementFactory< className > g_##className##_Factory( #lookupName ); \ + CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false ); \ + className *g_##className##LinkerHack = NULL; + +#define IMPLEMENT_ABSTRACT_ELEMENT( lookupName, className ) \ + IMPLEMENT_ELEMENT( className ) \ + CDmAbstractElementFactory< className > g_##className##_Factory; \ + CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false ); \ + className *g_##className##LinkerHack = NULL; + +#endif + + +// Used by classes defined in movieobjects or scenedatabase that must be explicitly installed +#define IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( lookupName, className ) \ + IMPLEMENT_ELEMENT( className ) \ + CDmElementFactory< className > g_##className##_Factory( #lookupName ); \ + CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false ); \ + className *g_##className##LinkerHack = NULL; + + +//----------------------------------------------------------------------------- +// Used to instantiate classes in libs from dlls/exes +//----------------------------------------------------------------------------- +#define USING_ELEMENT_FACTORY( className ) \ + extern C##className *g_##C##className##LinkerHack; \ + C##className *g_##C##className##PullInModule = g_##C##className##LinkerHack; + + +//----------------------------------------------------------------------------- +// Installs dm element factories +//----------------------------------------------------------------------------- +void InstallDmElementFactories( ); + + +#endif // DMELEMENTFACTORYHELPER_H diff --git a/public/datamodel/dmelementhandle.h b/public/datamodel/dmelementhandle.h new file mode 100644 index 0000000..6db4320 --- /dev/null +++ b/public/datamodel/dmelementhandle.h @@ -0,0 +1,47 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMELEMENTHANDLE_H +#define DMELEMENTHANDLE_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// handle to an CDmElement +//----------------------------------------------------------------------------- +#define PERFORM_HANDLE_TYPECHECKING 0 +#if PERFORM_HANDLE_TYPECHECKING + +// this is here to make sure we're being type-safe about element handles +// otherwise, the compiler lets us cast to bool incorrectly +// the other solution would be to redefine DmElementHandle_t s.t. DMELEMENT_HANDLE_INVALID==0 +struct DmElementHandle_t +{ + DmElementHandle_t() : handle( 0xffffffff ) {} + explicit DmElementHandle_t( int h ) : handle( h ) {} + inline bool operator==( const DmElementHandle_t &h ) const { return handle == h.handle; } + inline bool operator!=( const DmElementHandle_t &h ) const { return handle != h.handle; } + inline bool operator<( const DmElementHandle_t &h ) const { return handle < h.handle; } +// inline operator int() const { return handle; } // if we're okay with implicit int casts, uncomment this method + int handle; +}; +const DmElementHandle_t DMELEMENT_HANDLE_INVALID; + +#else // PERFORM_HANDLE_TYPECHECKING + +enum DmElementHandle_t +{ + DMELEMENT_HANDLE_INVALID = 0xffffffff +}; + +#endif // PERFORM_HANDLE_TYPECHECKING + + + +#endif // DMELEMENTHANDLE_H diff --git a/public/datamodel/dmvar.h b/public/datamodel/dmvar.h new file mode 100644 index 0000000..a2fa434 --- /dev/null +++ b/public/datamodel/dmvar.h @@ -0,0 +1,95 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMVAR_H +#define DMVAR_H +#ifdef _WIN32 +#pragma once +#endif + + +class CDmAttribute; + +//----------------------------------------------------------------------------- +// Helper template for external attributes +//----------------------------------------------------------------------------- +template< typename T > +class CDmaVar +{ + typedef typename CDmAttributeInfo< T >::StorageType_t D; + +public: + CDmaVar( ); + + // Setup to be used in OnConstruction methods of DmElements + void Init( CDmElement *pOwner, const char *pAttributeName, int flags = 0 ); + void InitAndSet( CDmElement *pOwner, const char *pAttributeName, const T &value, int flags = 0 ); + + // Set/get + const T& Set( const T &val ); + const T& Get() const; + + // Cast operators + operator const T&() const; + const T* operator->() const; + + // Assignment operator + const CDmaVar& operator=( const CDmaVar& src ); + + // Math utility operations + const T& operator=( const T &val ); + const T& operator+=( const T &val ); + const T& operator-=( const T &val ); + const T& operator/=( const T &val ); + const T& operator*=( const T &val ); + const T& operator^=( const T &val ); + const T& operator|=( const T &val ); + const T& operator&=( const T &val ); + T operator++(); + T operator--(); + T operator++( int ); // postfix version.. + T operator--( int ); // postfix version.. + + // Returns the attribute associated with the var + CDmAttribute *GetAttribute(); + const CDmAttribute *GetAttribute() const; + + // Is the attribute dirty? + bool IsDirty() const; + +protected: + const T& Value() const; + T& Value(); + const D& Storage() const; + D& Storage(); + +private: + D m_Storage; + +protected: + CDmAttribute *m_pAttribute; +}; + +//----------------------------------------------------------------------------- +// Specialization for string +//----------------------------------------------------------------------------- +class CDmaString : public CDmaVar< CUtlSymbolLarge > +{ +public: + const char *Get( ) const; + operator const char*() const; + + void InitAndSet( CDmElement *pOwner, const char *pAttributeName, const char *pValue, int flags = 0 ); + void Set( const char *pValue ); + CDmaString &operator=( const char *src ); + const CDmaString& operator=( const CDmaString& src ); + + // Returns strlen + int Length() const; + bool IsEmpty() const; +}; + +#endif // DMVAR_H diff --git a/public/datamodel/idatamodel.h b/public/datamodel/idatamodel.h new file mode 100644 index 0000000..2c25c78 --- /dev/null +++ b/public/datamodel/idatamodel.h @@ -0,0 +1,942 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef IDATAMODEL_H +#define IDATAMODEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "tier1/utlvector.h" +#include "tier1/utlsymbollarge.h" +#include "appframework/IAppSystem.h" +#include "datamodel/dmattributetypes.h" +#include "datamodel/dmxheader.h" + +//----------------------------------------------------------------------------- +// Forward declarations: +//----------------------------------------------------------------------------- +class CDmAttribute; +class CDmElement; +class IDmeOperator; +class IElementForKeyValueCallback; +class CDmElementFactoryHelper; + +struct DmValueBase_t; +class CUtlBuffer; +class KeyValues; +class CUtlCharConversion; +class CElementIdHash; + +// Use this for profiling dmx binary unserialization times +//#define DMX_PROFILE_UNSERIALIZE 2 +// If you set it to 2, then SFM will exit(-1) right after the first session is loaded (so you can see the PROFILE_SCOPE counters) + +#if defined( DMX_PROFILE_UNSERIALIZE ) +#define DMX_PROFILE_SCOPE( name ) PROFILE_SCOPE( name ) +#else +#define DMX_PROFILE_SCOPE( name ) +#endif + + +//----------------------------------------------------------------------------- +// element framework phases +//----------------------------------------------------------------------------- +enum DmPhase_t +{ + PH_EDIT, + PH_EDIT_APPLY, + PH_EDIT_RESOLVE, + PH_DEPENDENCY, + PH_OPERATE, + PH_OPERATE_RESOLVE, + PH_OUTPUT, +}; + +//----------------------------------------------------------------------------- +// Handle to an CDmAttribute +//----------------------------------------------------------------------------- +enum DmAttributeHandle_t +{ + DMATTRIBUTE_HANDLE_INVALID = 0xffffffff +}; + +//----------------------------------------------------------------------------- +// Handle to an DmAttributeList_t +//----------------------------------------------------------------------------- +enum DmAttributeReferenceIterator_t +{ + DMATTRIBUTE_REFERENCE_ITERATOR_INVALID = 0 +}; + +//----------------------------------------------------------------------------- +// element framework interface +//----------------------------------------------------------------------------- +abstract_class IDmElementFramework : public IAppSystem +{ +public: + // Methods of IAppSystem + virtual bool Connect( CreateInterfaceFn factory ) = 0; + virtual void Disconnect() = 0; + virtual void *QueryInterface( const char *pInterfaceName ) = 0; + virtual InitReturnVal_t Init() = 0; + virtual void Shutdown() = 0; + + virtual DmPhase_t GetPhase() = 0; + + virtual void SetOperators( const CUtlVector< IDmeOperator* > &operators ) = 0; + + virtual void BeginEdit() = 0; // ends in edit phase, forces apply/resolve if from edit phase + virtual void Operate( bool bResolve ) = 0; // ends in output phase + virtual void Resolve() = 0; + + virtual const CUtlVector< IDmeOperator* > &GetSortedOperators() const = 0; +}; + + + +abstract_class IDmeElementCreated +{ + public: + virtual void OnElementCreated( CDmElement *pElement ) = 0; +}; + +//----------------------------------------------------------------------------- +// Used only by aplpications to hook in the element framework +//----------------------------------------------------------------------------- +#define VDMELEMENTFRAMEWORK_VERSION "VDmElementFrameworkVersion001" + + +//----------------------------------------------------------------------------- +// Main interface +//----------------------------------------------------------------------------- +extern IDmElementFramework *g_pDmElementFramework; + + +//----------------------------------------------------------------------------- +// datamodel operator interface - for all elements that need to be sorted in the operator dependency graph +//----------------------------------------------------------------------------- +abstract_class IDmeOperator +{ +public: + virtual bool IsDirty() = 0; // ie needs to operate + virtual void Operate() = 0; + virtual const char* GetOperatorName() const { return "Unknown"; } + + virtual void GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs ) = 0; + virtual void GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs ) = 0; + + virtual void SetSortKey( int key ) = 0; + virtual int GetSortKey() const = 0; +}; + +//----------------------------------------------------------------------------- +// Class factory methods: +//----------------------------------------------------------------------------- +class IDmElementFactory +{ +public: + // Creation, destruction + virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id ) = 0; + virtual void Destroy( DmElementHandle_t hElement ) = 0; + virtual void AddOnElementCreatedCallback( IDmeElementCreated *callback ) = 0; + virtual void RemoveOnElementCreatedCallback( IDmeElementCreated *callback ) = 0; + virtual void OnElementCreated( CDmElement* pElement ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Various serialization methods can be installed into the data model factory +//----------------------------------------------------------------------------- +enum DmConflictResolution_t +{ + CR_DELETE_NEW, + CR_DELETE_OLD, + CR_COPY_NEW, + CR_FORCE_COPY, +}; + +// convert files to elements and back +// current file encodings supported: binary, keyvalues2, keyvalues2_flat, keyvalues (vmf/vmt/actbusy), text? (qc/obj) +class IDmSerializer +{ +public: + virtual const char *GetName() const = 0; + virtual const char *GetDescription() const = 0; + virtual bool IsBinaryFormat() const = 0; + virtual bool StoresVersionInFile() const = 0; + virtual int GetCurrentVersion() const = 0; + + // Write into the UtlBuffer, return true if successful + // if we decide to implement non-identity conversions between formats on write, then the source and dest format will need to be passed in here + virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot ) = 0; + + // Read from the UtlBuffer, return true if successful, and return the read-in root in ppRoot. + virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion, + const char *pSourceFormatName, int nSourceFormatVersion, + DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot ) = 0; + + // Methods used for importing (only should return non-NULL for serializers that return false from StoresVersionInFile) + virtual const char *GetImportedFormat() const = 0; + virtual int GetImportedVersion() const = 0; +}; + +// convert legacy elements to non-legacy elements +// legacy formats include: sfm_vN, binary_vN, keyvalues2_v1, keyvalues2_flat_v1 +// where N is a version number (1..9 for sfm, 1..2 for binary) +class IDmLegacyUpdater +{ +public: + virtual const char *GetName() const = 0; + virtual bool IsLatestVersion() const = 0; + + // Updates ppRoot to first non-legacy generic dmx format, returns false if the conversion fails + virtual bool Update( CDmElement **ppRoot ) = 0; +}; + +// converts old elements to new elements +// current formats include: sfm session, animset presets, particle definitions, exported maya character, etc. +class IDmFormatUpdater +{ +public: + virtual const char *GetName() const = 0; + virtual const char *GetDescription() const = 0; + virtual const char *GetExtension() const = 0; + virtual int GetCurrentVersion() const = 0; + virtual const char *GetDefaultEncoding() const = 0; + + // Converts pSourceRoot from nSourceVersion to the current version, returns false if the conversion fails + virtual bool Update( CDmElement **pRoot, int nSourceVersion ) = 0; +}; + +//----------------------------------------------------------------------------- +// Interface for callbacks to supply element types for specific keys inside keyvalues files +//----------------------------------------------------------------------------- +class IElementForKeyValueCallback +{ +public: + virtual const char *GetElementForKeyValue( const char *pszKeyName, int iNestingLevel ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Optional helper passed in with clipboard data which is called when it's time to clean up the clipboard data in case the application +// had some dynamically allocated data attached to a KV SetPtr object... +//----------------------------------------------------------------------------- +abstract_class IClipboardCleanup +{ +public: + virtual void ReleaseClipboardData( CUtlVector< KeyValues * >& list ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Can be installed to be called back when data changes +//----------------------------------------------------------------------------- +enum DmNotifySource_t +{ + // Sources + NOTIFY_SOURCE_APPLICATION = 0, + NOTIFY_SOURCE_UNDO, + NOTIFY_SOURCE_FIRST_DME_CONTROL_SOURCE = 4, // Sources from dme_controls starts here + NOTIFY_SOURCE_FIRST_APPLICATION_SOURCE = 8, // Sources from applications starts here +}; + +enum DmNotifyFlags_t +{ + // Does this dirty the document? + NOTIFY_SOURCE_BITS = 8, + NOTIFY_SETDIRTYFLAG = (1<& list ) = 0; + + virtual void AddUndoElement( IUndoElement *pElement ) = 0; + virtual CUtlSymbolLarge GetUndoDescInternal( const char *context ) = 0; + virtual CUtlSymbolLarge GetRedoDescInternal( const char *context ) = 0; + + virtual void EmptyClipboard() = 0; + virtual void SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction = 0 ) = 0; + virtual void AddToClipboardData( KeyValues *add ) = 0; + virtual void GetClipboardData( CUtlVector< KeyValues * >& data ) = 0; + virtual bool HasClipboardData() const = 0; + + // Handles to attributes + virtual CDmAttribute * GetAttribute( DmAttributeHandle_t h ) = 0; + virtual bool IsAttributeHandleValid( DmAttributeHandle_t h ) const = 0; + + // file id reference methods + virtual int NumFileIds() = 0; + virtual DmFileId_t GetFileId( int i ) = 0; + virtual DmFileId_t FindOrCreateFileId( const char *pFilename ) = 0; + virtual void RemoveFileId( DmFileId_t fileid ) = 0; + virtual DmFileId_t GetFileId( const char *pFilename ) = 0; + virtual const char * GetFileName( DmFileId_t fileid ) = 0; + virtual void SetFileName( DmFileId_t fileid, const char *pFileName ) = 0; + virtual const char * GetFileFormat( DmFileId_t fileid ) = 0; + virtual void SetFileFormat( DmFileId_t fileid, const char *pFormat ) = 0; + virtual DmElementHandle_t GetFileRoot( DmFileId_t fileid ) = 0; + virtual void SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot ) = 0; + virtual long GetFileModificationUTCTime( DmFileId_t fileid ) = 0; + virtual long GetCurrentUTCTime() = 0; + virtual void UTCTimeToString( char *pString, int maxChars, long fileTime ) = 0; + virtual bool IsFileLoaded( DmFileId_t fileid ) = 0; + virtual void MarkFileLoaded( DmFileId_t fileid ) = 0; + virtual void UnloadFile( DmFileId_t fileid ) = 0; + virtual int NumElementsInFile( DmFileId_t fileid ) = 0; + + virtual void DontAutoDelete( DmElementHandle_t hElement ) = 0; + + // handle validity methods - these shouldn't really be here, but the undo system needs them... + virtual void MarkHandleInvalid( DmElementHandle_t hElement ) = 0; + virtual void MarkHandleValid( DmElementHandle_t hElement ) = 0; + + virtual DmElementHandle_t FindElement( const DmObjectId_t &id ) = 0; + virtual void GetExistingElements( CElementIdHash &hash ) const = 0; + + virtual DmAttributeReferenceIterator_t FirstAttributeReferencingElement( DmElementHandle_t hElement ) = 0; + virtual DmAttributeReferenceIterator_t NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter ) = 0; + virtual CDmAttribute * GetAttribute( DmAttributeReferenceIterator_t hAttrIter ) = 0; + + // Install, remove notify callbacks associated w/ undo contexts + virtual bool InstallNotificationCallback( IDmNotify *pNotify ) = 0; + virtual void RemoveNotificationCallback( IDmNotify *pNotify ) = 0; + virtual bool IsSuppressingNotify( ) const = 0; + virtual void SetSuppressingNotify( bool bSuppress ) = 0; + virtual void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags ) = 0; + virtual void PopNotificationScope( bool bAbort = false ) = 0; + virtual const char *GetUndoString( CUtlSymbolLarge sym ) = 0; + + virtual bool HasElementFactory( const char *pElementType ) const = 0; + + // Call before you make any undo records + virtual void SetUndoDepth( int nSize ) = 0; + + // Displays memory stats to the console (passing in invalid handle causes memory for all elements to be estimated, otherwise it's only + // the element and it's recursive children) + virtual void DisplayMemoryStats( DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID ) = 0; + + // Is this file a DMX file? + virtual bool IsDMXFormat( CUtlBuffer &buf ) const = 0; + + // Dump the symbol table to the console + virtual void DumpSymbolTable() = 0; + + virtual void AddOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback ) = 0; + virtual void RemoveOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Used only by applications to hook in the data model +//----------------------------------------------------------------------------- +#define VDATAMODEL_INTERFACE_VERSION "VDataModelVersion001" + + +//----------------------------------------------------------------------------- +// Main interface accessor +//----------------------------------------------------------------------------- +extern IDataModel *g_pDataModel; + + +//----------------------------------------------------------------------------- +// Allows clients to implement customized undo elements +//----------------------------------------------------------------------------- +class CUndoElement : public IUndoElement +{ +public: + CUndoElement( const char *pDesc ) + { + m_UndoDesc = g_pDataModel->GetUndoDescInternal( pDesc ); + m_RedoDesc = g_pDataModel->GetRedoDescInternal( pDesc ); + m_pDesc = pDesc; + m_bEndOfStream = false; + } + + virtual void Release() + { + delete this; + } + + virtual const char *UndoDesc() const + { + return g_pDataModel->GetUndoString( m_UndoDesc ); + } + + virtual const char *RedoDesc() const + { + return g_pDataModel->GetUndoString( m_RedoDesc ); + } + + virtual const char *GetDesc() const + { + return m_pDesc; + } + +protected: + virtual bool IsEndOfStream() const + { + return m_bEndOfStream; + } + + virtual void SetEndOfStream( bool end ) + { + m_bEndOfStream = end; + } + + const char *m_pDesc; + CUtlSymbolLarge m_UndoDesc; + CUtlSymbolLarge m_RedoDesc; + bool m_bEndOfStream; + +private: + friend class CUndoManager; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Simple helper class +//----------------------------------------------------------------------------- +class CUndoScopeGuard +{ +public: + explicit CUndoScopeGuard( const char *udesc, const char *rdesc = NULL ) + { + m_bReleased = false; + m_bNotify = false; + m_pNotify = NULL; + g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc ); + } + + explicit CUndoScopeGuard( int nChainingID, char const *udesc ) + { + m_bReleased = false; + m_bNotify = false; + m_pNotify = NULL; + g_pDataModel->StartUndo( udesc, udesc, nChainingID ); + } + + explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 ) + { + m_bReleased = false; + m_bNotify = true; + m_pNotify = NULL; + g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID ); + g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags ); + } + + explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, IDmNotify *pNotify, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 ) + { + m_bReleased = false; + m_bNotify = true; + m_pNotify = NULL; + g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID ); + if ( pNotify ) + { + if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) + { + m_pNotify = pNotify; + } + } + g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags ); + } + + ~CUndoScopeGuard() + { + Release(); + } + + void Release() + { + if ( !m_bReleased ) + { + g_pDataModel->FinishUndo(); + if ( m_bNotify ) + { + g_pDataModel->PopNotificationScope( ); + m_bNotify = false; + } + if ( m_pNotify ) + { + g_pDataModel->RemoveNotificationCallback( m_pNotify ); + m_pNotify = NULL; + } + m_bReleased = true; + } + } + + void Abort() + { + if ( !m_bReleased ) + { + g_pDataModel->AbortUndoableOperation(); + if ( m_bNotify ) + { + g_pDataModel->PopNotificationScope( true ); + m_bNotify = false; + } + if ( m_pNotify ) + { + g_pDataModel->RemoveNotificationCallback( m_pNotify ); + m_pNotify = NULL; + } + m_bReleased = true; + } + } + +private: + IDmNotify *m_pNotify; + bool m_bReleased; + bool m_bNotify; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Simple helper class to disable Undo/Redo operations when in scope +//----------------------------------------------------------------------------- +class CChangeUndoScopeGuard +{ +public: + CChangeUndoScopeGuard( bool bNewState ) + { + m_bReleased = false; + m_bNotify = false; + m_pNotify = NULL; + m_bOldValue = g_pDataModel->IsUndoEnabled(); + g_pDataModel->SetUndoEnabled( bNewState ); + }; + + CChangeUndoScopeGuard( bool bNewState, const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) + { + m_bReleased = false; + m_bOldValue = g_pDataModel->IsUndoEnabled(); + g_pDataModel->SetUndoEnabled( bNewState ); + + m_bNotify = true; + m_pNotify = NULL; + if ( pNotify ) + { + if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) + { + m_pNotify = pNotify; + } + } + g_pDataModel->PushNotificationScope( pDesc, nNotifySource, nNotifyFlags ); + }; + + ~CChangeUndoScopeGuard() + { + Release(); + } + + void Release() + { + // Releases the guard... + if ( !m_bReleased ) + { + g_pDataModel->SetUndoEnabled( m_bOldValue ); + m_bReleased = true; + if ( m_bNotify ) + { + g_pDataModel->PopNotificationScope( ); + m_bNotify = false; + } + if ( m_pNotify ) + { + g_pDataModel->RemoveNotificationCallback( m_pNotify ); + m_pNotify = NULL; + } + } + } + +private: + IDmNotify *m_pNotify; + bool m_bOldValue; + bool m_bReleased; + bool m_bNotify; +}; + +class CDisableUndoScopeGuard : public CChangeUndoScopeGuard +{ + typedef CChangeUndoScopeGuard BaseClass; + +public: + CDisableUndoScopeGuard() : BaseClass( false ) { } + CDisableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) : + BaseClass( false, pDesc, nNotifySource, nNotifyFlags, pNotify ) {} +}; + +class CEnableUndoScopeGuard : public CChangeUndoScopeGuard +{ + typedef CChangeUndoScopeGuard BaseClass; + +public: + CEnableUndoScopeGuard( ) : BaseClass( true ) { } + CEnableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) : + BaseClass( true, pDesc, nNotifySource, nNotifyFlags, pNotify ) {} +}; + + +#define DEFINE_SOURCE_UNDO_SCOPE_GUARD( _classnameprefix, _source ) \ + class C ## _classnameprefix ## UndoScopeGuard : public CUndoScopeGuard \ + { \ + typedef CUndoScopeGuard BaseClass; \ + \ + public: \ + C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \ + BaseClass( _source, nNotifyFlags, pUndoDesc, pRedoDesc, nChainingID ) \ + { \ + } \ + C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, IDmNotify *pNotify, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \ + BaseClass( _source, nNotifyFlags, pNotify, pUndoDesc, pRedoDesc, nChainingID ) \ + { \ + } \ + C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, int nChainingID ) : \ + BaseClass( _source, nNotifyFlags, pUndoDesc, pUndoDesc, nChainingID ) \ + { \ + } \ + }; \ + class C ## _classnameprefix ## DisableUndoScopeGuard : public CDisableUndoScopeGuard \ + { \ + typedef CDisableUndoScopeGuard BaseClass; \ + \ + public: \ + C ## _classnameprefix ## DisableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ + BaseClass( pDesc, _source, nNotifyFlags, pNotify ) \ + { \ + } \ + }; \ + class C ## _classnameprefix ## EnableUndoScopeGuard : public CEnableUndoScopeGuard \ + { \ + typedef CEnableUndoScopeGuard BaseClass; \ + \ + public: \ + C ## _classnameprefix ## EnableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ + BaseClass( pDesc, _source, nNotifyFlags, pNotify ) \ + { \ + } \ + } + + +//----------------------------------------------------------------------------- +// Purpose: Simple helper class to disable NotifyDataChanged from current scope +//----------------------------------------------------------------------------- +class CNotifyScopeGuard +{ +public: + CNotifyScopeGuard( const char *pReason, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) + { + m_bReleased = false; + m_pNotify = NULL; + g_pDataModel->PushNotificationScope( pReason, nNotifySource, nNotifyFlags ); + if ( pNotify ) + { + if ( g_pDataModel->InstallNotificationCallback( pNotify ) ) + { + m_pNotify = pNotify; + } + } + }; + + ~CNotifyScopeGuard() + { + Release(); + } + + void Release() + { + // Releases the guard... + if ( !m_bReleased ) + { + g_pDataModel->PopNotificationScope( ); + if ( m_pNotify ) + { + g_pDataModel->RemoveNotificationCallback( m_pNotify ); + m_pNotify = NULL; + } + m_bReleased = true; + } + } + +private: + CNotifyScopeGuard( const CNotifyScopeGuard& g ); + +private: + IDmNotify *m_pNotify; + bool m_bReleased; +}; + + +#define DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( _classnameprefix, _source ) \ + class C ## _classnameprefix ## NotifyScopeGuard : public CNotifyScopeGuard \ + { \ + typedef CNotifyScopeGuard BaseClass; \ + \ + public: \ + C ## _classnameprefix ## NotifyScopeGuard( const char *pReason, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \ + BaseClass( pReason, _source, nNotifyFlags, pNotify )\ + { \ + } \ + } + + +//----------------------------------------------------------------------------- +// Purpose: Simple helper class to disable notifications when in scope +//----------------------------------------------------------------------------- +class CChangeNotifyScopeGuard +{ +public: + CChangeNotifyScopeGuard( bool bNewState ) + { + m_bReleased = false; + m_bOldValue = g_pDataModel->IsSuppressingNotify(); + g_pDataModel->SetSuppressingNotify( bNewState ); + }; + + ~CChangeNotifyScopeGuard() + { + Release(); + } + + void Release() + { + // Releases the guard... + if ( !m_bReleased ) + { + g_pDataModel->SetSuppressingNotify( m_bOldValue ); + m_bReleased = true; + } + } + +private: + bool m_bOldValue; + bool m_bReleased; +}; + +class CDisableNotifyScopeGuard : public CChangeNotifyScopeGuard +{ + typedef CChangeNotifyScopeGuard BaseClass; + +public: + CDisableNotifyScopeGuard() : BaseClass( true ) { } + +private: + CDisableNotifyScopeGuard( const CDisableNotifyScopeGuard& g ); +}; + +class CEnableNotifyScopeGuard : public CChangeNotifyScopeGuard +{ + typedef CChangeNotifyScopeGuard BaseClass; + +public: + CEnableNotifyScopeGuard( ) : BaseClass( false ) { } + +private: + CEnableNotifyScopeGuard( const CEnableNotifyScopeGuard& g ); +}; + + +//----------------------------------------------------------------------------- +// Standard undo/notify guards for the application +//----------------------------------------------------------------------------- +DEFINE_SOURCE_UNDO_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION ); +DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION ); + + +#endif // IDATAMODEL_H diff --git a/public/disp_common.cpp b/public/disp_common.cpp new file mode 100644 index 0000000..0ba3733 --- /dev/null +++ b/public/disp_common.cpp @@ -0,0 +1,1630 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "disp_common.h" +#include "disp_powerinfo.h" +#include "builddisp.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +class CNodeVert +{ +public: + CNodeVert() {} + CNodeVert( int ix, int iy ) {x=ix; y=iy;} + + inline int& operator[]( int i ) {return ((int*)this)[i];} + inline int const& operator[]( int i ) const {return ((int*)this)[i];} + + int x, y; +}; + +static CNodeVert const g_NodeChildLookup[4][2] = +{ + {CNodeVert(0,0), CNodeVert(1,1)}, + {CNodeVert(1,0), CNodeVert(2,1)}, + {CNodeVert(0,1), CNodeVert(1,2)}, + {CNodeVert(1,1), CNodeVert(2,2)} +}; + +static CNodeVert const g_NodeTriWinding[9] = +{ + CNodeVert(0, 1), + CNodeVert(0, 0), + CNodeVert(1, 0), + CNodeVert(2, 0), + CNodeVert(2, 1), + CNodeVert(2, 2), + CNodeVert(1, 2), + CNodeVert(0, 2), + CNodeVert(0, 1) +}; + +// Indexed by CORNER_. These store NEIGHBOREDGE_ defines and tell which edges butt up against the corner. +static int g_CornerEdges[4][2] = +{ + { NEIGHBOREDGE_BOTTOM, NEIGHBOREDGE_LEFT }, // CORNER_LOWER_LEFT + { NEIGHBOREDGE_TOP, NEIGHBOREDGE_LEFT }, // CORNER_UPPER_LEFT + { NEIGHBOREDGE_TOP, NEIGHBOREDGE_RIGHT }, // CORNER_UPPER_RIGHT + { NEIGHBOREDGE_BOTTOM, NEIGHBOREDGE_RIGHT } // CORNER_LOWER_RIGHT +}; + +int g_EdgeDims[4] = +{ + 0, // NEIGHBOREDGE_LEFT = X + 1, // NEIGHBOREDGE_TOP = Y + 0, // NEIGHBOREDGE_RIGHT = X + 1 // NEIGHBOREDGE_BOTTOM = Y +}; + +CShiftInfo g_ShiftInfos[3][3] = +{ + { + {0, 0, true}, // CORNER_TO_CORNER -> CORNER_TO_CORNER + {0, -1, true}, // CORNER_TO_CORNER -> CORNER_TO_MIDPOINT + {2, -1, true} // CORNER_TO_CORNER -> MIDPOINT_TO_CORNER + }, + + { + {0, 1, true}, // CORNER_TO_MIDPOINT -> CORNER_TO_CORNER + {0, 0, false}, // CORNER_TO_MIDPOINT -> CORNER_TO_MIDPOINT (invalid) + {0, 0, false} // CORNER_TO_MIDPOINT -> MIDPOINT_TO_CORNER (invalid) + }, + + { + {-1, 1, true}, // MIDPOINT_TO_CORNER -> CORNER_TO_CORNER + {0, 0, false}, // MIDPOINT_TO_CORNER -> CORNER_TO_MIDPOINT (invalid) + {0, 0, false} // MIDPOINT_TO_CORNER -> MIDPOINT_TO_CORNER (invalid) + } +}; + +int g_EdgeSideLenMul[4] = +{ + 0, + 1, + 1, + 0 +}; + + +// --------------------------------------------------------------------------------- // +// Helper functions. +// --------------------------------------------------------------------------------- // + +inline int SignedBitShift( int val, int shift ) +{ + if( shift > 0 ) + return val << shift; + else + return val >> -shift; +} + +static inline void RotateVertIndex( + NeighborOrientation neighor, + int sideLengthMinus1, + CVertIndex const &in, + CVertIndex &out ) +{ + if( neighor == ORIENTATION_CCW_0 ) + { + out = in; + } + else if( neighor == ORIENTATION_CCW_90 ) + { + out.x = in.y; + out.y = sideLengthMinus1 - in.x; + } + else if( neighor == ORIENTATION_CCW_180 ) + { + out.x = sideLengthMinus1 - in.x; + out.y = sideLengthMinus1 - in.y; + } + else + { + out.x = sideLengthMinus1 - in.y; + out.y = in.x; + } +} + +static inline void RotateVertIncrement( + NeighborOrientation neighor, + CVertIndex const &in, + CVertIndex &out ) +{ + if( neighor == ORIENTATION_CCW_0 ) + { + out = in; + } + else if( neighor == ORIENTATION_CCW_90 ) + { + out.x = in.y; + out.y = -in.x; + } + else if( neighor == ORIENTATION_CCW_180 ) + { + out.x = -in.x; + out.y = -in.y; + } + else + { + out.x = -in.y; + out.y = in.x; + } +} + + +// --------------------------------------------------------------------------------- // +// CDispHelper functions. +// --------------------------------------------------------------------------------- // + +int GetEdgeIndexFromPoint( CVertIndex const &index, int iMaxPower ) +{ + int sideLengthMinus1 = 1 << iMaxPower; + + if( index.x == 0 ) + return NEIGHBOREDGE_LEFT; + else if( index.y == sideLengthMinus1 ) + return NEIGHBOREDGE_TOP; + else if( index.x == sideLengthMinus1 ) + return NEIGHBOREDGE_RIGHT; + else if( index.y == 0 ) + return NEIGHBOREDGE_BOTTOM; + else + return -1; +} + + +int GetCornerIndexFromPoint( CVertIndex const &index, int iPower ) +{ + int sideLengthMinus1 = 1 << iPower; + + if( index.x == 0 && index.y == 0 ) + return CORNER_LOWER_LEFT; + + else if( index.x == 0 && index.y == sideLengthMinus1 ) + return CORNER_UPPER_LEFT; + + else if( index.x == sideLengthMinus1 && index.y == sideLengthMinus1 ) + return CORNER_UPPER_RIGHT; + + else if( index.x == sideLengthMinus1 && index.y == 0 ) + return CORNER_LOWER_RIGHT; + + else + return -1; +} + + +int GetNeighborEdgePower( CDispUtilsHelper *pDisp, int iEdge, int iSub ) +{ + CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + CDispSubNeighbor *pSub = &pEdge->m_SubNeighbors[iSub]; + if ( !pSub->IsValid() ) + return -1; + + CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex( pSub->GetNeighborIndex() ); + + CShiftInfo *pInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan]; + Assert( pInfo->m_bValid ); + + return pNeighbor->GetPower() + pInfo->m_PowerShiftAdd; +} + + +CDispUtilsHelper* SetupEdgeIncrements( + CDispUtilsHelper *pDisp, + int iEdge, + int iSub, + CVertIndex &myIndex, + CVertIndex &myInc, + CVertIndex &nbIndex, + CVertIndex &nbInc, + int &myEnd, + int &iFreeDim ) +{ + int iEdgeDim = g_EdgeDims[iEdge]; + iFreeDim = !iEdgeDim; + + CDispNeighbor *pSide = pDisp->GetEdgeNeighbor( iEdge ); + CDispSubNeighbor *pSub = &pSide->m_SubNeighbors[iSub]; + if ( !pSub->IsValid() ) + return NULL; + + CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex( pSub->m_iNeighbor ); + + CShiftInfo *pShiftInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan]; + Assert( pShiftInfo->m_bValid ); + + // Setup a start point and edge increment (NOTE: just precalculate these + // and store them in the CDispSubNeighbors). + CVertIndex tempInc; + + const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo(); + myIndex[iEdgeDim] = g_EdgeSideLenMul[iEdge] * pPowerInfo->m_SideLengthM1; + myIndex[iFreeDim] = pPowerInfo->m_MidPoint * iSub; + TransformIntoSubNeighbor( pDisp, iEdge, iSub, myIndex, nbIndex ); + + int myPower = pDisp->GetPowerInfo()->m_Power; + int nbPower = pNeighbor->GetPowerInfo()->m_Power + pShiftInfo->m_PowerShiftAdd; + + myInc[iEdgeDim] = tempInc[iEdgeDim] = 0; + if( nbPower > myPower ) + { + myInc[iFreeDim] = 1; + tempInc[iFreeDim] = 1 << (nbPower - myPower); + } + else + { + myInc[iFreeDim] = 1 << (myPower - nbPower); + tempInc[iFreeDim] = 1; + } + RotateVertIncrement( pSub->GetNeighborOrientation(), tempInc, nbInc ); + + // Walk along the edge. + if( pSub->m_Span == CORNER_TO_MIDPOINT ) + myEnd = pDisp->GetPowerInfo()->m_SideLength >> 1; + else + myEnd = pDisp->GetPowerInfo()->m_SideLength - 1; + + return pNeighbor; +} + + +int GetSubNeighborIndex( + CDispUtilsHelper *pDisp, + int iEdge, + CVertIndex const &nodeIndex ) +{ + const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo(); + const CDispNeighbor *pSide = pDisp->GetEdgeNeighbor( iEdge ); + + // Figure out if this is a vertical or horizontal edge. + int iEdgeDim = g_EdgeDims[iEdge]; + int iFreeDim = !iEdgeDim; + + int iFreeIndex = nodeIndex[iFreeDim]; + + // Figure out which of the (up to two) neighbors it lies in. + int iSub = 0; + if( iFreeIndex == pPowerInfo->m_MidPoint ) + { + // If it's in the middle, we only are interested if there's one neighbor + // next to us (so we can enable its middle vert). If there are any neighbors + // that touch the midpoint, then we have no need to return them because it would + // touch their corner verts which are always active. + if( pSide->m_SubNeighbors[0].m_Span != CORNER_TO_CORNER ) + return -1; + } + else if ( iFreeIndex > pPowerInfo->m_MidPoint ) + { + iSub = 1; + } + + // Make sure we get a valid neighbor. + if( !pSide->m_SubNeighbors[iSub].IsValid() ) + { + if( iSub == 1 && + pSide->m_SubNeighbors[0].IsValid() && + pSide->m_SubNeighbors[0].m_Span == CORNER_TO_CORNER ) + { + iSub = 0; + } + else + { + return -1; + } + } + + return iSub; +} + + +void SetupSpan( int iPower, int iEdge, NeighborSpan span, CVertIndex &viStart, CVertIndex &viEnd ) +{ + int iFreeDim = !g_EdgeDims[iEdge]; + const CPowerInfo *pPowerInfo = GetPowerInfo( iPower ); + + viStart = pPowerInfo->GetCornerPointIndex( iEdge ); + viEnd = pPowerInfo->GetCornerPointIndex( (iEdge+1) & 3 );; + + if ( iEdge == NEIGHBOREDGE_RIGHT || iEdge == NEIGHBOREDGE_BOTTOM ) + { + // CORNER_TO_MIDPOINT and MIDPOINT_CORNER are defined where the edge moves up or right, + // but pPowerInfo->GetCornerPointIndex walks around the edges clockwise, so on the + // bottom and right edges (where GetCornerPointIndex has us moving down and left) we need to + // reverse the sense here to make sure we return the right span. + if ( span == CORNER_TO_MIDPOINT ) + viStart[iFreeDim] = pPowerInfo->GetMidPoint(); + else if ( span == MIDPOINT_TO_CORNER ) + viEnd[iFreeDim] = pPowerInfo->GetMidPoint(); + } + else + { + if ( span == CORNER_TO_MIDPOINT ) + viEnd[iFreeDim] = pPowerInfo->GetMidPoint(); + else if ( span == MIDPOINT_TO_CORNER ) + viStart[iFreeDim] = pPowerInfo->GetMidPoint(); + } +} + + +CDispUtilsHelper* TransformIntoSubNeighbor( + CDispUtilsHelper *pDisp, + int iEdge, + int iSub, + CVertIndex const &nodeIndex, + CVertIndex &out + ) +{ + const CDispSubNeighbor *pSub = &pDisp->GetEdgeNeighbor( iEdge )->m_SubNeighbors[iSub]; + + // Find the part of pDisp's edge that this neighbor covers. + CVertIndex viSrcStart, viSrcEnd; + SetupSpan( pDisp->GetPower(), iEdge, pSub->GetSpan(), viSrcStart, viSrcEnd ); + + // Find the corresponding parts on the neighbor. + CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex( pSub->GetNeighborIndex() ); + int iNBEdge = (iEdge + 2 + pSub->GetNeighborOrientation()) & 3; + + CVertIndex viDestStart, viDestEnd; + SetupSpan( pNeighbor->GetPower(), iNBEdge, pSub->GetNeighborSpan(), viDestEnd, viDestStart ); + + + // Now map the one into the other. + int iFreeDim = !g_EdgeDims[iEdge]; + int fixedPercent = ((nodeIndex[iFreeDim] - viSrcStart[iFreeDim]) * (1<<16)) / (viSrcEnd[iFreeDim] - viSrcStart[iFreeDim]); + Assert( fixedPercent >= 0 && fixedPercent <= (1<<16) ); + + int nbDim = g_EdgeDims[iNBEdge]; + out[nbDim] = viDestStart[nbDim]; + out[!nbDim] = viDestStart[!nbDim] + ((viDestEnd[!nbDim] - viDestStart[!nbDim]) * fixedPercent) / (1<<16); + + Assert( out.x >= 0 && out.x < pNeighbor->GetSideLength() ); + Assert( out.y >= 0 && out.y < pNeighbor->GetSideLength() ); + + return pNeighbor; +} + + +CDispUtilsHelper* TransformIntoNeighbor( + CDispUtilsHelper *pDisp, + int iEdge, + CVertIndex const &nodeIndex, + CVertIndex &out + ) +{ + if ( iEdge == -1 ) + iEdge = GetEdgeIndexFromPoint( nodeIndex, pDisp->GetPower() ); + + int iSub = GetSubNeighborIndex( pDisp, iEdge, nodeIndex ); + if ( iSub == -1 ) + return NULL; + + CDispUtilsHelper *pRet = TransformIntoSubNeighbor( pDisp, iEdge, iSub, nodeIndex, out ); + +#if 0 + // Debug check.. make sure it comes back to the same point from the other side. + #if defined( _DEBUG ) + static bool bTesting = false; + if ( pRet && !bTesting ) + { + bTesting = true; + + // We could let TransformIntoNeighbor figure out the index but if this is a corner vert, then + // it may pick the wrong edge and we'd get a benign assert. + int nbOrientation = pDisp->GetEdgeNeighbor( iEdge )->m_SubNeighbors[iSub].GetNeighborOrientation(); + int iNeighborEdge = (iEdge + 2 + nbOrientation) & 3; + + CVertIndex testIndex; + CDispUtilsHelper *pTest = TransformIntoNeighbor( pRet, iNeighborEdge, out, testIndex ); + Assert( pTest == pDisp ); + Assert( testIndex == nodeIndex ); + + bTesting = false; + } + #endif +#endif + + return pRet; +} + + +bool DoesPointHaveAnyNeighbors( + CDispUtilsHelper *pDisp, + const CVertIndex &index ) +{ + // See if it connects to a neighbor on the edge. + CVertIndex dummy; + if ( TransformIntoNeighbor( pDisp, -1, index, dummy ) ) + return true; + + // See if it connects to a neighbor on a corner. + int iCorner = GetCornerIndexFromPoint( index, pDisp->GetPower() ); + if ( iCorner == -1 ) + return false; + + // If there are any neighbors on the specified corner, then the point has neighbors. + if ( pDisp->GetCornerNeighbors( iCorner )->m_nNeighbors > 0 ) + return true; + + // Since points on corners touch two edges, we actually want to test two edges to see + // if the point has a neighbor on either edge. + for ( int i=0; i < 2; i++ ) + { + if ( TransformIntoNeighbor( pDisp, g_CornerEdges[iCorner][i], index, dummy ) ) + return true; + } + + return false; +} + + +// ------------------------------------------------------------------------------------ // +// CDispSubEdgeIterator. +// ------------------------------------------------------------------------------------ // + +CDispSubEdgeIterator::CDispSubEdgeIterator() +{ + m_pNeighbor = 0; + m_FreeDim = m_Index.x = m_Inc.x = m_End = 0; // Setup so Next returns false. +} + + +void CDispSubEdgeIterator::Start( CDispUtilsHelper *pDisp, int iEdge, int iSub, bool bTouchCorners ) +{ + m_pNeighbor = SetupEdgeIncrements( pDisp, iEdge, iSub, m_Index, m_Inc, m_NBIndex, m_NBInc, m_End, m_FreeDim ); + if ( m_pNeighbor ) + { + if ( bTouchCorners ) + { + // Back up our current position by 1 so we hit the corner first, and extend the endpoint + // so we hit the other corner too. + m_Index -= m_Inc; + m_NBIndex -= m_NBInc; + + m_End += m_Inc[m_FreeDim]; + } + } + else + { + m_FreeDim = m_Index.x = m_Inc.x = m_End = 0; // Setup so Next returns false. + } +} + + +bool CDispSubEdgeIterator::Next() +{ + m_Index += m_Inc; + m_NBIndex += m_NBInc; + + // Were we just at the last point on the edge? + return m_Index[m_FreeDim] < m_End; +} + + +bool CDispSubEdgeIterator::IsLastVert() const +{ + return (m_Index[m_FreeDim] + m_Inc[m_FreeDim]) >= m_End; +} + + +// ------------------------------------------------------------------------------------ // +// CDispEdgeIterator. +// ------------------------------------------------------------------------------------ // + +CDispEdgeIterator::CDispEdgeIterator( CDispUtilsHelper *pDisp, int iEdge ) +{ + m_pDisp = pDisp; + m_iEdge = iEdge; + m_iCurSub = -1; +} + + +bool CDispEdgeIterator::Next() +{ + while ( !m_It.Next() ) + { + // Ok, move up to the next sub. + if ( m_iCurSub == 1 ) + return false; + + ++m_iCurSub; + m_It.Start( m_pDisp, m_iEdge, m_iCurSub ); + } + return true; +} + + +// ------------------------------------------------------------------------------------ // +// CDispCircumferenceIterator. +// ------------------------------------------------------------------------------------ // + +CDispCircumferenceIterator::CDispCircumferenceIterator( int sideLength ) +{ + m_iCurEdge = -1; + m_SideLengthM1 = sideLength - 1; +} + + +bool CDispCircumferenceIterator::Next() +{ + switch ( m_iCurEdge ) + { + case -1: + { + m_iCurEdge = NEIGHBOREDGE_LEFT; + m_VertIndex.Init( 0, 0 ); + } + break; + + case NEIGHBOREDGE_LEFT: + { + ++m_VertIndex.y; + if ( m_VertIndex.y == m_SideLengthM1 ) + m_iCurEdge = NEIGHBOREDGE_TOP; + } + break; + + case NEIGHBOREDGE_TOP: + { + ++m_VertIndex.x; + if ( m_VertIndex.x == m_SideLengthM1 ) + m_iCurEdge = NEIGHBOREDGE_RIGHT; + } + break; + + case NEIGHBOREDGE_RIGHT: + { + --m_VertIndex.y; + if ( m_VertIndex.y == 0 ) + m_iCurEdge = NEIGHBOREDGE_BOTTOM; + } + break; + + case NEIGHBOREDGE_BOTTOM: + { + --m_VertIndex.x; + if ( m_VertIndex.x == 0 ) + return false; // Done! + } + break; + } + + return true; +} + + + +// Helper function to setup an index either on the edges or the center +// of the box defined by [bottomleft,topRight]. +static inline void SetupCoordXY( CNodeVert &out, CNodeVert const &bottomLeft, CNodeVert const &topRight, CNodeVert const &info ) +{ + for( int i=0; i < 2; i++ ) + { + if( info[i] == 0 ) + out[i] = bottomLeft[i]; + else if( info[i] == 1 ) + out[i] = (bottomLeft[i] + topRight[i]) >> 1; + else + out[i] = topRight[i]; + } +} + + +static unsigned short* DispCommon_GenerateTriIndices_R( + CNodeVert const &bottomLeft, + CNodeVert const &topRight, + unsigned short *indices, + int power, + int sideLength ) +{ + if( power == 1 ) + { + // Ok, add triangles. All we do here is follow a list of verts (g_NodeTriWinding) + // around the center vert of this node and make triangles. + int iCurTri = 0; + CNodeVert verts[3]; + + // verts[0] is always the center vert. + SetupCoordXY( verts[0], bottomLeft, topRight, CNodeVert(1,1) ); + int iCurVert = 1; + + for( int i=0; i < 9; i++ ) + { + SetupCoordXY( verts[iCurVert], bottomLeft, topRight, g_NodeTriWinding[i] ); + ++iCurVert; + + if( iCurVert == 3 ) + { + for( int iTriVert=2; iTriVert >= 0; iTriVert-- ) + { + int index = verts[iTriVert].y * sideLength + verts[iTriVert].x; + *indices = index; + ++indices; + } + + // Setup for the next triangle. + verts[1] = verts[2]; + iCurVert = 2; + iCurTri++; + } + } + } + else + { + // Recurse into the children. + for( int i=0; i < 4; i++ ) + { + CNodeVert childBottomLeft, childTopRight; + SetupCoordXY( childBottomLeft, bottomLeft, topRight, g_NodeChildLookup[i][0] ); + SetupCoordXY( childTopRight, bottomLeft, topRight, g_NodeChildLookup[i][1] ); + + indices = DispCommon_GenerateTriIndices_R( childBottomLeft, childTopRight, indices, power-1, sideLength ); + } + } + + return indices; +} + + +// ------------------------------------------------------------------------------------------- // +// CDispUtilsHelper functions. +// ------------------------------------------------------------------------------------------- // + +int CDispUtilsHelper::GetPower() const +{ + return GetPowerInfo()->GetPower(); +} + +int CDispUtilsHelper::GetSideLength() const +{ + return GetPowerInfo()->GetSideLength(); +} + +const CVertIndex& CDispUtilsHelper::GetCornerPointIndex( int iCorner ) const +{ + return GetPowerInfo()->GetCornerPointIndex( iCorner ); +} + +int CDispUtilsHelper::VertIndexToInt( const CVertIndex &i ) const +{ + Assert( i.x >= 0 && i.x < GetSideLength() && i.y >= 0 && i.y < GetSideLength() ); + return i.y * GetSideLength() + i.x; +} + +CVertIndex CDispUtilsHelper::GetEdgeMidPoint( int iEdge ) const +{ + int end = GetSideLength() - 1; + int mid = GetPowerInfo()->GetMidPoint(); + + if ( iEdge == NEIGHBOREDGE_LEFT ) + return CVertIndex( 0, mid ); + + else if ( iEdge == NEIGHBOREDGE_TOP ) + return CVertIndex( mid, end ); + + else if ( iEdge == NEIGHBOREDGE_RIGHT ) + return CVertIndex( end, mid ); + + else if ( iEdge == NEIGHBOREDGE_BOTTOM ) + return CVertIndex( mid, 0 ); + + Assert( false ); + return CVertIndex( 0, 0 ); +} + +int DispCommon_GetNumTriIndices( int power ) +{ + return (1<GetEdgeNeighbor( i )->SetInvalid(); + pDisp->GetCornerNeighbors( i )->SetInvalid(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void GetDispBox( CCoreDispInfo *pDisp, CDispBox &box ) +{ + // Calculate the bbox for this displacement. + Vector vMin( 1e24, 1e24, 1e24 ); + Vector vMax( -1e24, -1e24, -1e24 ); + + for ( int iVert = 0; iVert < 4; ++iVert ) + { + const Vector &vTest = pDisp->GetSurface()->GetPoint( iVert ); + VectorMin( vTest, vMin, vMin ); + VectorMax( vTest, vMax, vMax ); + } + + // Puff the box out a little. + static float flPuff = 0.1f; + vMin -= Vector( flPuff, flPuff, flPuff ); + vMax += Vector( flPuff, flPuff, flPuff ); + + box.m_Min = vMin; + box.m_Max = vMax; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void SetupDispBoxes( CCoreDispInfo **ppListBase, int nListSize, CUtlVector &out ) +{ + out.SetSize( nListSize ); + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + CCoreDispInfo *pDisp = ppListBase[iDisp]; + GetDispBox( pDisp, out[iDisp] ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline bool DoBBoxesTouch( const CDispBox &a, const CDispBox &b ) +{ + for ( int i=0; i < 3; i++ ) + { + if ( a.m_Max[i] < b.m_Min[i] ) + return false; + + if ( a.m_Min[i] > b.m_Max[i] ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool FindEdge( CCoreDispInfo *pInfo, Vector const &vPoint1, Vector const &vPoint2, int &iEdge ) +{ + CCoreDispSurface *pSurface = pInfo->GetSurface(); + + for( iEdge=0; iEdge < 4; iEdge++ ) + { + if( VectorsAreEqual( vPoint1, pSurface->GetPoint( iEdge ), 0.01f ) && + VectorsAreEqual( vPoint2, pSurface->GetPoint( (iEdge+1) & 3), 0.01f ) ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +NeighborSpan NeighborSpanFlip( int iEdge, NeighborSpan span ) +{ + if ( g_bEdgeNeighborFlip[iEdge] ) + return g_SpanFlip[span]; + else + return span; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void AddNeighbor( CCoreDispInfo *pMain, + int iEdge, // Which of pMain's sides this is on. + int iSub, // Which sub neighbor this takes up in pSide. + NeighborSpan span, // What span this fills in pMain. + CCoreDispInfo *pOther, int iNeighborEdge, NeighborSpan nbSpan ) +{ + // The edge iteration before coming in here goes 0-1, 1-2, 2-3, 3-4. + // This flips the sense of CORNER_TO_MIDPOINT/MIDPOINT_TO_CORNER on the right and + // bottom edges and is undone here. + span = NeighborSpanFlip( iEdge, span ); + nbSpan = NeighborSpanFlip( iNeighborEdge, nbSpan ); + + // Get the subspan this fills on our displacement. + CDispSubNeighbor *pSub = &pMain->GetEdgeNeighbor(iEdge)->m_SubNeighbors[iSub]; + + // Which subspan does this use in the neighbor? + CDispSubNeighbor *pNeighborSub; + if ( nbSpan == MIDPOINT_TO_CORNER ) + { + pNeighborSub = &pOther->GetEdgeNeighbor(iNeighborEdge)->m_SubNeighbors[1]; + } + else + { + pNeighborSub = &pOther->GetEdgeNeighbor(iNeighborEdge)->m_SubNeighbors[0]; + } + + // Make sure this slot isn't used on either displacement. + if ( pSub->IsValid() || pNeighborSub->IsValid() ) + { + ExecuteOnce( Warning( "Found a displacement edge abutting multiple other edges.\n" ) ); + return; + } + + // Now just copy the data into each displacement. + pSub->m_iNeighbor = pOther->GetListIndex(); + pSub->m_NeighborOrientation = g_CoreDispNeighborOrientationMap[iEdge][iNeighborEdge]; + pSub->m_Span = span; + pSub->m_NeighborSpan = nbSpan; + + pNeighborSub->m_iNeighbor = pMain->GetListIndex(); + pNeighborSub->m_NeighborOrientation = g_CoreDispNeighborOrientationMap[iNeighborEdge][iEdge]; + pNeighborSub->m_Span = nbSpan; + pNeighborSub->m_NeighborSpan = span; + +#if defined( _DEBUG ) + // Walk an iterator over the new connection to make sure it works. + CDispSubEdgeIterator it; + it.Start( pMain, iEdge, iSub ); + while ( it.Next() ) + { + CVertIndex nbIndex; + TransformIntoNeighbor( pMain, iEdge, it.GetVertIndex(), nbIndex ); + } +#endif +} + +//----------------------------------------------------------------------------- +// This function is symmetric wrt pMain and pOther. It sets up valid neighboring data for +// the relationship between both of them. +//----------------------------------------------------------------------------- +void SetupEdgeNeighbors( CCoreDispInfo *pMain, CCoreDispInfo *pOther ) +{ + // Initialize.. + for( int iEdge=0; iEdge < 4; iEdge++ ) + { + // Setup the edge points and the midpoint. + Vector pt[2], mid; + pMain->GetSurface()->GetPoint( iEdge, pt[0] ); + pMain->GetSurface()->GetPoint( (iEdge + 1) & 3, pt[1] ); + mid = (pt[0] + pt[1]) * 0.5f; + + // Find neighbors. + int iNBEdge; + if( FindEdge( pOther, pt[1], pt[0], iNBEdge ) ) + { + AddNeighbor( pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, CORNER_TO_CORNER ); + } + else + { + // Look for one that takes up our whole side. + if( FindEdge( pOther, pt[1], pt[0]*2 - pt[1], iNBEdge ) ) + { + AddNeighbor( pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, CORNER_TO_MIDPOINT ); + } + else if( FindEdge( pOther, pt[1]*2 - pt[0], pt[0], iNBEdge ) ) + { + AddNeighbor( pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, MIDPOINT_TO_CORNER ); + } + else + { + // Ok, look for 1 or two that abut this side. + if( FindEdge( pOther, mid, pt[0], iNBEdge ) ) + { + AddNeighbor( pMain, iEdge, g_bEdgeNeighborFlip[iEdge], CORNER_TO_MIDPOINT, pOther, iNBEdge, CORNER_TO_CORNER ); + } + + if( FindEdge( pOther, pt[1], mid, iNBEdge ) ) + { + AddNeighbor( pMain, iEdge, !g_bEdgeNeighborFlip[iEdge], MIDPOINT_TO_CORNER, pOther, iNBEdge, CORNER_TO_CORNER ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Returns true if the displacement has an edge neighbor with the given index. +//----------------------------------------------------------------------------- +bool HasEdgeNeighbor( const CCoreDispInfo *pMain, int iNeighbor ) +{ + for ( int i=0; i < 4; i++ ) + { + const CDispCornerNeighbors *pCorner = pMain->GetCornerNeighbors( i ); + for ( int iNB=0; iNB < pCorner->m_nNeighbors; iNB++ ) + if ( pCorner->m_Neighbors[iNB] == iNeighbor ) + return true; + + const CDispNeighbor *pEdge = pMain->GetEdgeNeighbor( i ); + if ( pEdge->m_SubNeighbors[0].GetNeighborIndex() == iNeighbor || + pEdge->m_SubNeighbors[1].GetNeighborIndex() == iNeighbor ) + { + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void SetupCornerNeighbors( CCoreDispInfo *pMain, CCoreDispInfo *pOther, int *nOverflows ) +{ + if ( HasEdgeNeighbor( pMain, pOther->GetListIndex() ) ) + return; + + // Do these two share a vertex? + int nShared = 0; + int iMainSharedCorner = -1; + int iOtherSharedCorner = -1; + + for ( int iMainCorner=0; iMainCorner < 4; iMainCorner++ ) + { + Vector const &vMainCorner = pMain->GetCornerPoint( iMainCorner ); + + for ( int iOtherCorner=0; iOtherCorner < 4; iOtherCorner++ ) + { + Vector const &vOtherCorner = pOther->GetCornerPoint( iOtherCorner ); + + if ( VectorsAreEqual( vMainCorner, vOtherCorner, 0.001f ) ) + { + iMainSharedCorner = iMainCorner; + iOtherSharedCorner = iOtherCorner; + ++nShared; + } + } + } + + if ( nShared == 1 ) + { + CDispCornerNeighbors *pMainCorner = pMain->GetCornerNeighbors( iMainSharedCorner ); + CDispCornerNeighbors *pOtherCorner = pOther->GetCornerNeighbors( iOtherSharedCorner ); + + if ( pMainCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS && + pOtherCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS ) + { + pMainCorner->m_Neighbors[pMainCorner->m_nNeighbors++] = pOther->GetListIndex(); + pOtherCorner->m_Neighbors[pOtherCorner->m_nNeighbors++] = pMain->GetListIndex(); + } + else + { + ++(*nOverflows); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool VerifyNeighborVertConnection( CDispUtilsHelper *pDisp, const CVertIndex &nodeIndex, + const CDispUtilsHelper *pTestNeighbor, const CVertIndex &testNeighborIndex, + int mySide ) +{ + CVertIndex nbIndex( -1, -1 ); + CDispUtilsHelper *pNeighbor = NULL; + if( (pNeighbor = TransformIntoNeighbor( pDisp, mySide, nodeIndex, nbIndex ) ) != NULL ) + { + if ( pTestNeighbor != pNeighbor || nbIndex != testNeighborIndex ) + return false; + + CVertIndex testIndex( -1, -1 ); + int iSide = GetEdgeIndexFromPoint( nbIndex, pNeighbor->GetPowerInfo()->m_Power ); + if ( iSide == -1 ) + { + return false; + } + + CDispUtilsHelper *pTest = TransformIntoNeighbor( pNeighbor, iSide, nbIndex, testIndex ); + + if( pTest != pDisp || nodeIndex != testIndex ) + { + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void VerifyNeighborConnections( CCoreDispInfo **ppListBase, int nDisps ) +{ + while ( 1 ) + { + bool bHappy = true; + + int iDisp; + for ( iDisp = 0; iDisp < nDisps; ++iDisp ) + { + CCoreDispInfo *pDisp = ppListBase[iDisp]; + CDispUtilsHelper *pHelper = pDisp; + + for ( int iEdge=0; iEdge < 4; iEdge++ ) + { + CDispEdgeIterator it( pHelper, iEdge ); + while ( it.Next() ) + { + if ( !VerifyNeighborVertConnection( pHelper, it.GetVertIndex(), it.GetCurrentNeighbor(), it.GetNBVertIndex(), iEdge ) ) + { + pDisp->GetEdgeNeighbor( iEdge )->SetInvalid(); + Warning( "Warning: invalid neighbor connection on displacement near (%.2f %.2f %.2f)\n", VectorExpand( pDisp->GetCornerPoint(0) ) ); + bHappy = false; + } + } + } + } + + if ( bHappy ) + break; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void FindNeighboringDispSurfs( CCoreDispInfo **ppListBase, int nListSize ) +{ + // First, clear all neighboring data. + int iDisp; + for ( iDisp = 0; iDisp < nListSize; ++iDisp ) + { + ClearNeighborData( ppListBase[iDisp] ); + } + + CUtlVector boxes; + SetupDispBoxes( ppListBase, nListSize, boxes ); + + int nCornerOverflows = 0; + + // Now test all pairs of displacements and setup neighboring relations between them. + for( iDisp = 0; iDisp < nListSize; ++iDisp ) + { + CCoreDispInfo *pMain = ppListBase[iDisp]; + + for ( int iDisp2 = iDisp+1; iDisp2 < nListSize; ++iDisp2 ) + { + CCoreDispInfo *pOther = ppListBase[iDisp2]; + + // Trivial reject. + if ( !DoBBoxesTouch( boxes[iDisp], boxes[iDisp2] ) ) + continue; + + SetupEdgeNeighbors( pMain, pOther ); + + // NOTE: this must come after SetupEdgeNeighbors because it makes sure not to add + // corner neighbors for disps that are already edge neighbors. + SetupCornerNeighbors( pMain, pOther, &nCornerOverflows ); + } + } + + if ( nCornerOverflows ) + { + Warning( "Warning: overflowed %d displacement corner-neighbor lists.", nCornerOverflows ); + } + + // Debug check.. make sure the neighbor connections are intact (make sure that any + // edge vert that gets mapped into a neighbor gets mapped back the same way). + VerifyNeighborConnections( ppListBase, nListSize ); +} + +//============================================================================= +// +// Allowable verts. +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int IsCorner( CVertIndex const &index, int sideLength ) +{ + if ( index.x == 0 ) + { + if ( index.y == 0 ) + return true; + else if ( index.y == sideLength-1 ) + return true; + } + else if ( index.x == sideLength-1 ) + { + if ( index.y == 0 ) + return true; + else if ( index.y == sideLength-1 ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool IsVertAllowed( CDispUtilsHelper *pDisp, CVertIndex const &sideVert, int iLevel ) +{ + if ( IsCorner( sideVert, pDisp->GetPowerInfo()->GetSideLength() ) ) + return true; + + int iSide = GetEdgeIndexFromPoint( sideVert, pDisp->GetPowerInfo()->GetPower() ); + if ( iSide == -1 ) + return true; + + int iSub = GetSubNeighborIndex( pDisp, iSide, sideVert ); + if ( iSub == -1 ) + return true; + + CDispSubNeighbor *pSub = &pDisp->GetEdgeNeighbor( iSide )->m_SubNeighbors[iSub]; + CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex( pSub->m_iNeighbor ); + Assert( pNeighbor ); + + // Ok, there is a neighbor.. see if this vertex exists in the neighbor. + CShiftInfo *pShiftInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan]; + Assert( pShiftInfo->m_bValid ); + + if ( ( pNeighbor->GetPowerInfo()->GetPower() + pShiftInfo->m_PowerShiftAdd ) < ( iLevel+1 ) ) + { + return false; + } + + // Ok, it exists. Make sure the neighbor hasn't disallowed it. + CVertIndex nbIndex; + TransformIntoSubNeighbor( pDisp, iSide, iSub, sideVert, nbIndex ); + + CBitVec &allowedVerts = CCoreDispInfo::FromDispUtils( pNeighbor )->GetAllowedVerts(); + return !!allowedVerts.Get( pNeighbor->VertIndexToInt( nbIndex ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void UnallowVerts_R( CDispUtilsHelper *pDisp, CVertIndex const &nodeIndex, int &nUnallowed ) +{ + int iNodeIndex = pDisp->VertIndexToInt( nodeIndex ); + + CCoreDispInfo *pCoreDisp = CCoreDispInfo::FromDispUtils( pDisp ); + if ( !pCoreDisp->GetAllowedVerts().Get( iNodeIndex ) ) + return; + + nUnallowed++; + pCoreDisp->GetAllowedVerts().Clear( iNodeIndex ); + + for ( int iDep=0; iDep < CVertInfo::NUM_REVERSE_DEPENDENCIES; iDep++ ) + { + CVertDependency &dep = pDisp->GetPowerInfo()->m_pVertInfo[iNodeIndex].m_ReverseDependencies[iDep]; + + if( dep.m_iVert.x != -1 && dep.m_iNeighbor == -1 ) + { + UnallowVerts_R( pDisp, dep.m_iVert, nUnallowed ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void DisableUnallowedVerts_R( CDispUtilsHelper *pDisp, CVertIndex const &nodeIndex, int iLevel, int &nUnallowed ) +{ + int iNodeIndex = pDisp->VertIndexToInt( nodeIndex ); + + // This vertex is not allowed if it is on an edge with a neighbor + // that does not have this vertex. + + // Test side verts. + for( int iSide=0; iSide < 4; iSide++ ) + { + CVertIndex const &sideVert = pDisp->GetPowerInfo()->m_pSideVerts[iNodeIndex].m_Verts[iSide]; + + if( !IsVertAllowed( pDisp, sideVert, iLevel ) ) + { + // This vert (and its dependencies) can't exist. + UnallowVerts_R( pDisp, sideVert, nUnallowed ); + } + } + +#if 0 + // Test dependencies. + for( int iDep=0; iDep < 2; iDep++ ) + { + CVertDependency const &dep = pDisp->GetPowerInfo()->m_pVertInfo[iNodeIndex].m_Dependencies[iDep]; + + if( dep.m_iNeighbor == -1 && !IsVertAllowed( pDisp, dep.m_iVert, iLevel ) ) + { + UnallowVerts_R( pDisp, nodeIndex, nUnallowed ); + } + } +#endif + + // Recurse. + if( iLevel+1 < pDisp->GetPower() ) + { + for( int iChild=0; iChild < 4; iChild++ ) + { + DisableUnallowedVerts_R( pDisp, pDisp->GetPowerInfo()->m_pChildVerts[iNodeIndex].m_Verts[iChild], iLevel+1, nUnallowed ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void SetupAllowedVerts( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Set all verts to allowed to start with. + int iDisp; + for ( iDisp = 0; iDisp < nListSize; ++iDisp ) + { + ppListBase[iDisp]->GetAllowedVerts().SetAll(); + } + + // Disable verts that need to be disabled so higher-powered displacements remove + // the necessary triangles when bordering lower-powered displacements. + // It is necessary to loop around here because disabling verts can accumulate into + // neighbors. + bool bContinue; + do + { + bContinue = false; + for( iDisp = 0; iDisp < nListSize; ++iDisp ) + { + CDispUtilsHelper *pDisp = ppListBase[iDisp]; + + int nUnallowed = 0; + DisableUnallowedVerts_R( pDisp, pDisp->GetPowerInfo()->m_RootNode, 0, nUnallowed ); + if ( nUnallowed ) + bContinue = true; + } + } while( bContinue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pDisp - +// &vecPoint - +// Output : int +//----------------------------------------------------------------------------- +static int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vecPoint ) +{ + CDispUtilsHelper *pDispHelper = pDisp; + + int iClosest = 0; + float flClosest = 1e24; + for ( int iCorner = 0; iCorner < 4; ++iCorner ) + { + + // Has it been touched? + CVertIndex viCornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner ); + int iCornerVert = pDispHelper->VertIndexToInt( viCornerVert ); + const Vector &vecCornerVert = pDisp->GetVert( iCornerVert ); + + float flDist = vecCornerVert.DistTo( vecPoint ); + if ( flDist < flClosest ) + { + iClosest = iCorner; + flClosest = flDist; + } + } + + if ( flClosest <= 0.1f ) + return iClosest; + else + return -1; +} + +// sets a new normal/tangentS, recomputes tangent T +static void UpdateTangentSpace(CCoreDispInfo *pDisp, int iVert, const Vector &vNormal, const Vector &vTanS) +{ + Vector tanT; + pDisp->SetNormal( iVert, vNormal ); + CrossProduct( vTanS, vNormal, tanT ); + pDisp->SetTangentS(iVert, vTanS); + pDisp->SetTangentT(iVert, tanT); +} + +static void UpdateTangentSpace(CCoreDispInfo *pDisp, const CVertIndex &index, const Vector &vNormal, const Vector &vTanS) +{ + UpdateTangentSpace(pDisp, pDisp->VertIndexToInt(index), vNormal, vTanS); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// nListSize - +//----------------------------------------------------------------------------- +static void BlendSubNeighbors( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Loop through all of the displacements in the list. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + // Get the current displacement. + CCoreDispInfo *pDisp = ppListBase[iDisp]; + if ( !pDisp ) + continue; + + // Loop through all the edges of the displacement. + for ( int iEdge = 0; iEdge < 4; ++iEdge ) + { + // Find valid neighbors along the edge. + CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + if ( !pEdge ) + continue; + + // Check to see if we have sub-neighbors - defines a t-junction in this world. If not, + // then the normal blend edges function will catch it all. + if ( !pEdge->m_SubNeighbors[0].IsValid() || !pEdge->m_SubNeighbors[1].IsValid() ) + continue; + + // Get the mid-point of the current displacement. + CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge ); + int iMidPoint = pDisp->VertIndexToInt( viMidPoint ); + + const Vector &vecMidPoint = pDisp->GetVert( iMidPoint ); + + // Get the current sub-neighbors along the edge. + CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()]; + CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()]; + + // Get the current sub-neighbor corners. + int iCorners[2]; + iCorners[0] = FindNeighborCornerVert( pNeighbor1, vecMidPoint ); + iCorners[1] = FindNeighborCornerVert( pNeighbor2, vecMidPoint ); + if ( iCorners[0] != -1 && iCorners[1] != -1 ) + { + CVertIndex viCorners[2] = { pNeighbor1->GetCornerPointIndex( iCorners[0] ),pNeighbor2->GetCornerPointIndex( iCorners[1] ) }; + + // Accumulate the normals at the mid-point of the primary edge and corners of the sub-neighbors. + Vector vecAverage = pDisp->GetNormal( iMidPoint ); + vecAverage += pNeighbor1->GetNormal( viCorners[0] ); + vecAverage += pNeighbor2->GetNormal( viCorners[1] ); + + // Re-normalize. + VectorNormalize( vecAverage ); + Vector vAvgTanS = pDisp->GetTangentS(iMidPoint); + vAvgTanS += pNeighbor1->GetTangentS(viCorners[0]); + vAvgTanS += pNeighbor2->GetTangentS(viCorners[1]); + VectorNormalize(vAvgTanS); + //vecAverage.Init( 0.0f, 0.0f, 1.0f ); + + // Set the new normal value back. + UpdateTangentSpace( pDisp, iMidPoint, vecAverage, vAvgTanS ); + UpdateTangentSpace( pNeighbor1, viCorners[0], vecAverage, vAvgTanS ); + UpdateTangentSpace( pNeighbor2, viCorners[1], vecAverage, vAvgTanS ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pDisp - +// iNeighbors[512] - +// Output : int +//----------------------------------------------------------------------------- +static int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] ) +{ + int nNeighbors = 0; + + // Check corner neighbors. + for ( int iCorner=0; iCorner < 4; iCorner++ ) + { + const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner ); + + for ( int i=0; i < pCorner->m_nNeighbors; i++ ) + { + if ( nNeighbors < _ARRAYSIZE( iNeighbors ) ) + iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i]; + } + } + + for ( int iEdge=0; iEdge < 4; iEdge++ ) + { + const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + + for ( int i=0; i < 2; i++ ) + { + if ( pEdge->m_SubNeighbors[i].IsValid() ) + if ( nNeighbors < 512 ) + iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex(); + } + } + + return nNeighbors; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// listSize - +//----------------------------------------------------------------------------- +static void BlendCorners( CCoreDispInfo **ppListBase, int nListSize ) +{ + CUtlVector nbCornerVerts; + + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + CCoreDispInfo *pDisp = ppListBase[iDisp]; + + int iNeighbors[512]; + int nNeighbors = GetAllNeighbors( pDisp, iNeighbors ); + + // Make sure we have room for all the neighbors. + nbCornerVerts.RemoveAll(); + nbCornerVerts.EnsureCapacity( nNeighbors ); + nbCornerVerts.AddMultipleToTail( nNeighbors ); + + // For each corner. + for ( int iCorner=0; iCorner < 4; iCorner++ ) + { + // Has it been touched? + CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner ); + int iCornerVert = pDisp->VertIndexToInt( cornerVert ); + const Vector &vCornerVert = pDisp->GetVert( iCornerVert ); + + // For each displacement sharing this corner.. + Vector vAverage = pDisp->GetNormal( iCornerVert ); + Vector vAvgTanS; + pDisp->GetTangentS( iCornerVert, vAvgTanS ); + + for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ ) + { + int iNBListIndex = iNeighbors[iNeighbor]; + CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex]; + + // Find out which vert it is on the neighbor. + int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert ); + if ( iNBCorner == -1 ) + { + nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list. + } + else + { + CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner ); + int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert ); + nbCornerVerts[iNeighbor] = iNBVert; + vAverage += pNeighbor->GetNormal( iNBVert ); + vAvgTanS += pNeighbor->GetTangentS( iNBVert ); + } + } + + + // Blend all the neighbor normals with this one. + VectorNormalize( vAverage ); + VectorNormalize( vAvgTanS ); + UpdateTangentSpace(pDisp, iCornerVert, vAverage, vAvgTanS ); + + for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ ) + { + int iNBListIndex = iNeighbors[iNeighbor]; + if ( nbCornerVerts[iNeighbor] == -1 ) + continue; + + CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex]; + UpdateTangentSpace(pNeighbor, nbCornerVerts[iNeighbor], vAverage, vAvgTanS); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// listSize - +//----------------------------------------------------------------------------- +static void BlendEdges( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Loop through all the displacements in the list. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + // Get the current displacement. + CCoreDispInfo *pDisp = ppListBase[iDisp]; + if ( !pDisp ) + continue; + + // Loop through all of the edges on a displacement. + for ( int iEdge = 0; iEdge < 4; ++iEdge ) + { + // Get the current displacement edge. + CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + if ( !pEdge ) + continue; + + // Check for sub-edges. + for ( int iSubEdge = 0; iSubEdge < 2; ++iSubEdge ) + { + // Get the current sub-edge. + CDispSubNeighbor *pSubEdge = &pEdge->m_SubNeighbors[iSubEdge]; + if ( !pSubEdge->IsValid() ) + continue; + + // Get the current neighbor. + CCoreDispInfo *pNeighbor = ppListBase[pSubEdge->GetNeighborIndex()]; + if ( !pNeighbor ) + continue; + + // Get the edge dimension. + int iEdgeDim = g_EdgeDims[iEdge]; + + CDispSubEdgeIterator it; + it.Start( pDisp, iEdge, iSubEdge, true ); + + // Get setup on the first corner vert. + it.Next(); + CVertIndex viPrevPos = it.GetVertIndex(); + while ( it.Next() ) + { + // Blend the two. + if ( !it.IsLastVert() ) + { + Vector vecAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() ); + Vector vAvgTanS = pDisp->GetTangentS( it.GetVertIndex() ) + pNeighbor->GetTangentS( it.GetNBVertIndex() ); + VectorNormalize( vecAverage ); + VectorNormalize( vAvgTanS ); + UpdateTangentSpace(pDisp, it.GetVertIndex(), vecAverage, vAvgTanS ); + UpdateTangentSpace(pNeighbor, it.GetNBVertIndex(), vecAverage, vAvgTanS ); + } + + // Now blend the in-between verts (if this edge is high-res). + int iPrevPos = viPrevPos[!iEdgeDim]; + int iCurPos = it.GetVertIndex()[!iEdgeDim]; + for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ ) + { + float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 ); + Vector vecNormal; + VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vecNormal ); + VectorNormalize( vecNormal ); + Vector vAvgTanS; + VectorLerp( pDisp->GetTangentS( viPrevPos ), pDisp->GetTangentS( it.GetVertIndex() ), flPercent, vAvgTanS ); + VectorNormalize( vAvgTanS ); + + CVertIndex viTween; + viTween[iEdgeDim] = it.GetVertIndex()[iEdgeDim]; + viTween[!iEdgeDim] = iTween; + UpdateTangentSpace(pDisp, viTween, vecNormal, vAvgTanS); + } + + viPrevPos = it.GetVertIndex(); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **pListBase - +// listSize - +// NOTE: todo - this is almost the same code as found in vrad, should probably +// move it up into common code at some point if the feature +// continues to get used +//----------------------------------------------------------------------------- +void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Setup helper list for iteration. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + ppListBase[iDisp]->SetDispUtilsHelperInfo( ppListBase, nListSize ); + } + + // Blend normals along t-junctions, corners, and edges. + BlendSubNeighbors( ppListBase, nListSize ); + BlendCorners( ppListBase, nListSize ); + BlendEdges( ppListBase, nListSize ); +} diff --git a/public/disp_common.h b/public/disp_common.h new file mode 100644 index 0000000..174068c --- /dev/null +++ b/public/disp_common.h @@ -0,0 +1,263 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISP_COMMON_H +#define DISP_COMMON_H +#ifdef _WIN32 +#pragma once +#endif + +#include "disp_vertindex.h" +#include "bspfile.h" +#include "utlvector.h" + +class CPowerInfo; +class CCoreDispInfo; + +// ----------------------------------------------------------------------------- // +// Classes. +// ----------------------------------------------------------------------------- // + +// This class provides a set of utility functions for displacements that work in the tools and the engine. +abstract_class CDispUtilsHelper +{ +// Derived classes must implement these. +public: + virtual const CPowerInfo* GetPowerInfo() const = 0; + virtual CDispNeighbor* GetEdgeNeighbor( int index ) = 0; + virtual CDispCornerNeighbors* GetCornerNeighbors( int index ) = 0; + virtual CDispUtilsHelper* GetDispUtilsByIndex( int index ) = 0; + +// Helper functions. +public: + + int GetPower() const; + int GetSideLength() const; + const CVertIndex& GetCornerPointIndex( int iCorner ) const; + int VertIndexToInt( const CVertIndex &i ) const; + CVertIndex GetEdgeMidPoint( int iEdge ) const; +}; + + +// Use this to walk along two neighboring displacements and touch all the +// common vertices. +class CDispSubEdgeIterator +{ +public: + + CDispSubEdgeIterator(); + + // Normally, this will iterate all shared verts along the edge except the corners. + // If you want the corners to be touched too, then pass in bTouchCorners=true. + void Start( CDispUtilsHelper *pDisp, int iEdge, int iSub, bool bTouchCorners = false ); + bool Next(); + + const CVertIndex& GetVertIndex() const { return m_Index; } // Get the vert index for the displacement in pUtils. + const CVertIndex& GetNBVertIndex() const { return m_NBIndex; } // Get the neighbor's vert index. + CDispUtilsHelper* GetNeighbor() const { return m_pNeighbor; } + + // Returns true if you're on the last vert (ie: the next Next() call will return false).ssssss + bool IsLastVert() const; + + +private: + CDispUtilsHelper *m_pNeighbor; // The neighbor to the edge we were setup on. + + CVertIndex m_Index; + CVertIndex m_Inc; + + CVertIndex m_NBIndex; + CVertIndex m_NBInc; + + int m_End; + int m_FreeDim; +}; + + +// Use this to walk along the edge of a displacement, touching the points in common +// between the two neighbors. Note: this won't hit the corner points of any of the displacements. +// (As a result, it won't hit the midpoint of pDisps's edge if there are 2 neighbors). +class CDispEdgeIterator +{ +public: + CDispEdgeIterator( CDispUtilsHelper *pDisp, int iEdge ); + + // Seek to the next point on the edge. + bool Next(); + + const CVertIndex& GetVertIndex() const { return m_It.GetVertIndex(); } // Get the vert index for the displacement in pUtils. + const CVertIndex& GetNBVertIndex() const { return m_It.GetNBVertIndex(); } // Get the neighbor's vert index. + + // What is the current neighbor? + CDispUtilsHelper* GetCurrentNeighbor() const { return m_It.GetNeighbor(); } + + +private: + CDispUtilsHelper *m_pDisp; + int m_iEdge; + int m_iCurSub; + + CDispSubEdgeIterator m_It; +}; + + +// Use this to walk all the corners and edge verts in the displacement. +// It walks the edges in the order of the NEIGHBOREDGE_ defines. +// Iterate like this: +// CDispCircumferenceIterator iterator( pDisp->GetSideLength() ); +// while ( iterator.Next() ) +// ... +class CDispCircumferenceIterator +{ +public: + CDispCircumferenceIterator( int sideLength ); + + // Seek to the next point. Returns false when there are no more points. + bool Next(); + + const CVertIndex& GetVertIndex() const { return m_VertIndex; } + + +private: + int m_SideLengthM1; + int m_iCurEdge; + CVertIndex m_VertIndex; +}; + + +// These store info about how to scale and shift coordinates between neighbors +// of different relations (in g_ShiftInfos). +class CShiftInfo +{ +public: + int m_MidPointScale; + int m_PowerShiftAdd; + bool m_bValid; +}; + +class CDispBox +{ +public: + Vector m_Min, m_Max; +}; + +// ----------------------------------------------------------------------------- // +// Globals. +// ----------------------------------------------------------------------------- // + + +extern int g_EdgeDims[4]; // This tells which dimension (0 or 1) is locked on an edge for each NEIGHBOREDGE_ enum. +extern CShiftInfo g_ShiftInfos[3][3]; // See CShiftInfo. +extern int g_EdgeSideLenMul[4];// Multiply these by the side length to get the index of the edge. + + +// ----------------------------------------------------------------------------- // +// Helper functions. +// ----------------------------------------------------------------------------- // + +// Reference implementation to generate triangle indices for a displacement. +int DispCommon_GetNumTriIndices( int power ); +void DispCommon_GenerateTriIndices( int power, unsigned short *indices ); + +// Returns a NEIGHBOREDGE_ value for the edge that the index is on. +// Returns -1 if the index is not on a side. +// If the point is on a corner, the edges are tested in the order of the NEIGHBOREDGE_ defines. +int GetEdgeIndexFromPoint( CVertIndex const &index, int iPower ); + +// Returns a CORNER_ value for the corner the point is on, or -1 if it's not on a corner. +int GetEdgeIndexFromPoint( CVertIndex const &index, int iPower ); + +// This returns the neighbor's power, possibly +1 or -1. +// +// It will add one if the neighbor takes up half of your edge (ie: if it took up your +// whole edge, its resolution would be twice what it really is). +// +// It will subtract one if you take up half of its edge (ie: you only touch half of its verts). +// +// Returns -1 if the edge connection is invalid. +int GetNeighborEdgePower( CDispUtilsHelper *pDisp, int iEdge, int iSub ); + +// This function sets you up so you can walk along an edge that joins two neighbors. +// Add myInc to myIndex and nbInc to nbIndex until myIndex[iFreeDim] >= myEnd. +// +// Returns the neighbor displacement, or NULL if the specified sub neighbor isn't valid. +CDispUtilsHelper* SetupEdgeIncrements( + CDispUtilsHelper *pDisp, + int iEdge, + int iSub, + CVertIndex &myIndex, + CVertIndex &myInc, + CVertIndex &nbIndex, + CVertIndex &nbInc, + int &myEnd, + int &iFreeDim ); + +// Figure out which sub neighbor nodeIndex touches. +// Returns -1 if there is no valid sub neighbor at the specified index. +int GetSubNeighborIndex( + CDispUtilsHelper *pDisp, + int iEdge, + CVertIndex const &nodeIndex + ); + +// Given a vert index and the CSubNeighbor the vert lies on, this +// transforms the specified vert into the neighbor's space. +// +// Note: for corner verts, there may be multiple neighbors touching the same vert, so the +// result you get depends on the edge you specify in iEdge (ie: if you specify the same +// node index but a different edge, you may get a different neighbor). +// +// Note: This only returns a point if the point at nodeIndex actually touches a neighbor point. +// An example where this might be unexpected is if pDisp is power 4 and its neighbor on iEdge +// is power 3, and nodeIndex points at a vert in between two of its neighbor's verts. +// In that case, even though there is a neighbor displacement, nodeIndex doesn't touch +// any points on it, so NULL is returned. +CDispUtilsHelper* TransformIntoSubNeighbor( + CDispUtilsHelper *pDisp, + int iEdge, + int iSub, + CVertIndex const &nodeIndex, + CVertIndex &out + ); + +// Transform pDisp's node at nodeIndex into its neighboring connection. +// Returns the neighbor displacement and sets out to the index in the neighbor. +// +// Note: for corner verts, there may be multiple neighbors touching the same vert, so the +// result you get depends on the edge you specify in iEdge (ie: if you specify the same +// node index but a different edge, you may get a different neighbor). +// +// Note: This only returns a point if the point at nodeIndex actually touches a neighbor point. +// An example where this might surprise you is if pDisp is power 4 and its neighbor on iEdge +// is power 3, and nodeIndex points at a vert in between two of its neighbor's verts. +// In that case, even though there is a neighbor displacement, nodeIndex doesn't touch +// any points on it, so NULL is returned. +CDispUtilsHelper* TransformIntoNeighbor( + CDispUtilsHelper *pDisp, + int iEdge, + CVertIndex const &nodeIndex, + CVertIndex &out ); + +// Returns true if the specified point has one or more neighbors. +bool DoesPointHaveAnyNeighbors( + CDispUtilsHelper *pDisp, + const CVertIndex &index ); + + +void FindNeighboringDispSurfs( CCoreDispInfo **ppListBase, int nListSize ); +void SetupAllowedVerts( CCoreDispInfo **ppListBase, int nListSize ); +void GetDispBox( CCoreDispInfo *pDisp, CDispBox &box ); +void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize ); + +// ----------------------------------------------------------------------------- // +// Inlines. +// ----------------------------------------------------------------------------- // + +#include "disp_powerinfo.h" + + +#endif // DISP_COMMON_H diff --git a/public/disp_powerinfo.cpp b/public/disp_powerinfo.cpp new file mode 100644 index 0000000..0578d94 --- /dev/null +++ b/public/disp_powerinfo.cpp @@ -0,0 +1,580 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "disp_powerinfo.h" +#include "disp_common.h" +#include "commonmacros.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ------------------------------------------------------------------------ // +// Internal classes. +// ------------------------------------------------------------------------ // + +// These point at the vertices connecting to each of the [north,south,east,west] vertices. +class CVertCorners +{ +public: + short m_Corner1[2]; + short m_Corner2[2]; +}; + + +// ------------------------------------------------------------------------ // +// Globals. +// ------------------------------------------------------------------------ // + +// This points at vertices to the side of a node (north, south, east, west). +static short g_SideVertMul[4][2] = { {1,0}, {0,1}, {-1,0}, {0,-1} }; + +static CVertCorners g_SideVertCorners[4] = +{ + { {1,-1}, {1,1} }, + { {1,1}, {-1,1} }, + { {-1,1}, {-1,-1} }, + { {-1,-1}, {1,-1} } +}; + +// This is used in loops on child nodes. The indices point at the nodes: +// 0 = upper-right +// 1 = upper-left +// 2 = lower-left +// 3 = lower-right +static CVertIndex g_ChildNodeIndexMul[4] = +{ + CVertIndex(1,1), + CVertIndex(-1,1), + CVertIndex(-1,-1), + CVertIndex(1,-1) +}; + +// These are multipliers on vertMul (not nodeMul). +static CVertIndex g_ChildNodeDependencies[4][2] = +{ + { CVertIndex(1,0), CVertIndex(0,1) }, + { CVertIndex(0,1), CVertIndex(-1,0) }, + { CVertIndex(-1,0), CVertIndex(0,-1) }, + { CVertIndex(0,-1), CVertIndex(1,0) } +}; + +// 2x2 rotation matrices for each orientation. +static int g_OrientationRotations[4][2][2] = +{ + {{1, 0}, // CCW_0 + {0, 1}}, + + {{0, 1}, // CCW_90 + {-1,0}}, + + {{-1,0}, // CCW_180 + {0,-1}}, + + {{0, -1}, // CCW_270 + {1, 0}} +}; + + +// ------------------------------------------------------------------------ // +// Helper functions. +// ------------------------------------------------------------------------ // + +// Apply a 2D rotation to the specified CVertIndex around the specified centerpoint. +static CVertIndex Transform2D( + int const mat[2][2], + CVertIndex const &vert, + CVertIndex const ¢erPoint ) +{ + CVertIndex translated = vert - centerPoint; + + CVertIndex transformed( + translated.x*mat[0][0] + translated.y*mat[0][1], + translated.x*mat[1][0] + translated.y*mat[1][1] ); + + return transformed + centerPoint; +} + + +// Rotate a given CVertIndex with a specified orientation. +// Do this with a lookup table eventually! +static void GetEdgeVertIndex( int sideLength, int iEdge, int iVert, CVertIndex &out ) +{ + if( iEdge == NEIGHBOREDGE_RIGHT ) + { + out.x = sideLength - 1; + out.y = iVert; + } + else if( iEdge == NEIGHBOREDGE_TOP ) + { + out.x = iVert; + out.y = sideLength - 1; + } + else if( iEdge == NEIGHBOREDGE_LEFT ) + { + out.x = 0; + out.y = iVert; + } + else + { + out.x = iVert; + out.y = 0; + } +} + + +// Generate an index given a CVertIndex and the size of the displacement it resides in. +static int VertIndex( CVertIndex const &vert, int iMaxPower ) +{ + return vert.y * ((1 << iMaxPower) + 1) + vert.x; +} + + +static CVertIndex WrapVertIndex( CVertIndex const &in, int sideLength ) +{ + int out[2]; + + for( int i=0; i < 2; i++ ) + { + if( in[i] < 0 ) + out[i] = sideLength - 1 - (-in[i] % sideLength); + else if( in[i] >= sideLength ) + out[i] = in[i] % sideLength; + else + out[i] = in[i]; + } + + return CVertIndex( out[0], out[1] ); +} + + +static int GetFreeDependency( CVertDependency *pDep, int nElements ) +{ + for( int i=0; i < nElements; i++ ) + { + if( !pDep[i].IsValid() ) + return i; + } + + Assert( false ); + return 0; +} + + +static void AddDependency( + CVertInfo *dependencies, + int sideLength, + CVertIndex const &nodeIndex, + CVertIndex const &dependency, + int iMaxPower, + bool bCheckNeighborDependency, + bool bAddReverseDependency ) +{ + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + CVertInfo *pNode = &dependencies[iNodeIndex]; + + int iDep = GetFreeDependency( pNode->m_Dependencies, sizeof(pNode->m_Dependencies)/sizeof(pNode->m_Dependencies[0]) ); + pNode->m_Dependencies[iDep].m_iVert = dependency; + pNode->m_Dependencies[iDep].m_iNeighbor = -1; + + if( bAddReverseDependency ) + { + CVertInfo *pDep = &dependencies[VertIndex( dependency, iMaxPower )]; + iDep = GetFreeDependency( pDep->m_ReverseDependencies, CVertInfo::NUM_REVERSE_DEPENDENCIES ); + pDep->m_ReverseDependencies[iDep].m_iVert = nodeIndex; + pDep->m_ReverseDependencies[iDep].m_iNeighbor = -1; + } + + // Edge verts automatically add a dependency for the neighbor. + // Internal verts wind up in here twice anyway so it doesn't need to + if( bCheckNeighborDependency ) + { + int iConnection = GetEdgeIndexFromPoint( nodeIndex, iMaxPower ); + if( iConnection != -1 ) + { + Assert( !pNode->m_Dependencies[1].IsValid() ); + + CVertIndex delta( nodeIndex.x - dependency.x, nodeIndex.y - dependency.y ); + CVertIndex newIndex( nodeIndex.x + delta.x, nodeIndex.y + delta.y ); + + int fullSideLength = (1 << iMaxPower) + 1; + pNode->m_Dependencies[1].m_iVert = WrapVertIndex( CVertIndex( newIndex.x, newIndex.y ), fullSideLength ); + pNode->m_Dependencies[1].m_iNeighbor = iConnection; + } + } +} + + +// --------------------------------------------------------------------------------- // +// CTesselateWinding stuff. +// --------------------------------------------------------------------------------- // + +CTesselateVert::CTesselateVert( CVertIndex const &index, int iNode ) + : m_Index( index ) +{ + m_iNode = iNode; +} + + +CVertInfo::CVertInfo() +{ + int i; + for( i=0; i < sizeof(m_Dependencies)/sizeof(m_Dependencies[0]); i++ ) + { + m_Dependencies[i].m_iVert = CVertIndex( -1, -1 ); + m_Dependencies[i].m_iNeighbor = -1; + } + + for( i=0; i < sizeof(m_ReverseDependencies)/sizeof(m_ReverseDependencies[0]); i++ ) + { + m_ReverseDependencies[i].m_iVert = CVertIndex( -1, -1 ); + m_ReverseDependencies[i].m_iNeighbor = -1; + } + + m_iParent.x = m_iParent.y = -1; + m_iNodeLevel = -1; +} + + +CTesselateVert g_TesselateVerts[] = +{ + CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT), + CTesselateVert( CVertIndex(0,-1), -1), + CTesselateVert( CVertIndex(-1,-1), CHILDNODE_LOWER_LEFT), + CTesselateVert( CVertIndex(-1, 0), -1), + CTesselateVert( CVertIndex(-1, 1), CHILDNODE_UPPER_LEFT), + CTesselateVert( CVertIndex(0, 1), -1), + CTesselateVert( CVertIndex(1, 1), CHILDNODE_UPPER_RIGHT), + CTesselateVert( CVertIndex(1, 0), -1), + CTesselateVert( CVertIndex(1,-1), CHILDNODE_LOWER_RIGHT) +}; + +CTesselateWinding g_TWinding = +{ + g_TesselateVerts, + sizeof( g_TesselateVerts ) / sizeof( g_TesselateVerts[0] ) +}; + + + +// --------------------------------------------------------------------------------- // +// CPowerInfo stuff. +// --------------------------------------------------------------------------------- // + +// Precalculated info about each particular displacement size. +#define DECLARE_TABLES( size ) \ + static CVertInfo g_VertInfo_##size##x##size[ size*size ]; \ + static CFourVerts g_SideVerts_##size##x##size[ size*size ]; \ + static CFourVerts g_ChildVerts_##size##x##size[ size*size ]; \ + static CFourVerts g_SideVertCorners_##size##x##size[ size*size ]; \ + static CTwoUShorts g_ErrorEdges_##size##x##size[ size*size ]; \ + static CTriInfo g_TriInfos_##size##x##size[ (size-1)*(size-1)*2 ]; \ + static CPowerInfo g_PowerInfo_##size##x##size( \ + g_VertInfo_##size##x##size, \ + g_SideVerts_##size##x##size, \ + g_ChildVerts_##size##x##size, \ + g_SideVertCorners_##size##x##size,\ + g_ErrorEdges_##size##x##size, \ + g_TriInfos_##size##x##size \ + ) + +#define POWERINFO_ENTRY( size ) \ + (&g_PowerInfo_##size##x##size) + +DECLARE_TABLES( 5 ); +DECLARE_TABLES( 9 ); +DECLARE_TABLES( 17 ); + + +// Index by m_Power. +CPowerInfo *g_PowerInfos[NUM_POWERINFOS] = +{ + NULL, + NULL, + POWERINFO_ENTRY(5), + POWERINFO_ENTRY(9), + POWERINFO_ENTRY(17) +}; + + +CPowerInfo::CPowerInfo( + CVertInfo *pVertInfo, + CFourVerts *pSideVerts, + CFourVerts *pChildVerts, + CFourVerts *pSideVertCorners, + CTwoUShorts *pErrorEdges, + CTriInfo *pTriInfos ) +{ + m_pVertInfo = pVertInfo; + m_pSideVerts = pSideVerts; + m_pChildVerts = pChildVerts; + m_pSideVertCorners = pSideVertCorners; + m_pErrorEdges = pErrorEdges; + m_pTriInfos = pTriInfos; +} + +static void InitPowerInfoTriInfos_R( + CPowerInfo *pInfo, + CVertIndex const &nodeIndex, + CTriInfo* &pTriInfo, + int iMaxPower, + int iLevel ) +{ + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + + if( iLevel+1 < iMaxPower ) + { + // Recurse into children. + for( int iChild=0; iChild < 4; iChild++ ) + { + InitPowerInfoTriInfos_R( + pInfo, + pInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild], + pTriInfo, + iMaxPower, + iLevel+1 ); + } + } + else + { + unsigned short indices[3]; + + int vertInc = 1 << ((iMaxPower - iLevel) - 1); + + // We're at a leaf, generate the tris. + CTesselateWinding *pWinding = &g_TWinding; + + // Starting at the bottom-left, wind clockwise picking up vertices and + // generating triangles. + int iCurTriVert = 0; + for( int iVert=0; iVert < pWinding->m_nVerts; iVert++ ) + { + CVertIndex sideVert = BuildOffsetVertIndex( nodeIndex, pWinding->m_Verts[iVert].m_Index, vertInc ); + + if( iCurTriVert == 1 ) + { + // Add this vert and finish the tri. + pTriInfo->m_Indices[0] = indices[0]; + pTriInfo->m_Indices[1] = VertIndex( sideVert, iMaxPower ); + pTriInfo->m_Indices[2] = iNodeIndex; + ++pTriInfo; + } + + indices[0] = VertIndex( sideVert, iMaxPower ); + iCurTriVert = 1; + } + } +} + + +static void InitPowerInfo_R( + CPowerInfo *pPowerInfo, + int iMaxPower, + CVertIndex const &nodeIndex, + CVertIndex const &dependency1, + CVertIndex const &dependency2, + CVertIndex const &nodeEdge1, + CVertIndex const &nodeEdge2, + CVertIndex const &iParent, + int iLevel ) +{ + int sideLength = ((1 << iMaxPower) + 1); + int iNodeIndex = VertIndex( nodeIndex, iMaxPower ); + + pPowerInfo->m_pVertInfo[iNodeIndex].m_iParent = iParent; + pPowerInfo->m_pVertInfo[iNodeIndex].m_iNodeLevel = iLevel + 1; + + pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[0] = (unsigned short)(VertIndex( nodeEdge1, iMaxPower )); + pPowerInfo->m_pErrorEdges[iNodeIndex].m_Values[1] = (unsigned short)(VertIndex( nodeEdge2, iMaxPower )); + + // Add this node's dependencies. + AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency1, iMaxPower, false, true ); + AddDependency( pPowerInfo->m_pVertInfo, sideLength, nodeIndex, dependency2, iMaxPower, false, true ); + + // The 4 side vertices depend on this node. + int iPower = iMaxPower - iLevel; + int vertInc = 1 << (iPower - 1); + + for( int iSide=0; iSide < 4; iSide++ ) + { + // Store the side vert index. + CVertIndex sideVert( nodeIndex.x + g_SideVertMul[iSide][0]*vertInc, nodeIndex.y + g_SideVertMul[iSide][1]*vertInc ); + int iSideVert = VertIndex( sideVert, iMaxPower ); + + pPowerInfo->m_pSideVerts[iNodeIndex].m_Verts[iSide] = sideVert; + + // Store the side vert corners. + CVertIndex sideVertCorner0 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner1[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner1[1]*vertInc ); + CVertIndex sideVertCorner1 = CVertIndex( nodeIndex.x + g_SideVertCorners[iSide].m_Corner2[0]*vertInc, nodeIndex.y + g_SideVertCorners[iSide].m_Corner2[1]*vertInc ); + + pPowerInfo->m_pSideVertCorners[iNodeIndex].m_Verts[iSide] = sideVertCorner0; + + // Write the side vert corners into the error-edges list. + pPowerInfo->m_pErrorEdges[iSideVert].m_Values[0] = (unsigned short)VertIndex( sideVertCorner0, iMaxPower ); + pPowerInfo->m_pErrorEdges[iSideVert].m_Values[1] = (unsigned short)VertIndex( sideVertCorner1, iMaxPower ); + + AddDependency( + pPowerInfo->m_pVertInfo, + sideLength, + sideVert, + nodeIndex, + iMaxPower, + true, + true ); + } + + // Recurse into the children. + int nodeInc = vertInc >> 1; + if( nodeInc ) + { + for( int iChild=0; iChild < 4; iChild++ ) + { + CVertIndex childVert( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * nodeInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * nodeInc ); + + pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild] = childVert; + + InitPowerInfo_R( pPowerInfo, + iMaxPower, + childVert, + + CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][0].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][0].y*vertInc), + CVertIndex(nodeIndex.x + g_ChildNodeDependencies[iChild][1].x*vertInc, nodeIndex.y + g_ChildNodeDependencies[iChild][1].y*vertInc), + + nodeIndex, + CVertIndex( nodeIndex.x + g_ChildNodeIndexMul[iChild].x * vertInc, nodeIndex.y + g_ChildNodeIndexMul[iChild].y * vertInc ), + + nodeIndex, + iLevel + 1 ); + } + } +} + + +void InitPowerInfo( CPowerInfo *pInfo, int iMaxPower ) +{ + int sideLength = (1 << iMaxPower) + 1; + + // Precalculate the dependency graph. + CVertIndex nodeDependency1( sideLength-1, sideLength-1 ); + CVertIndex nodeDependency2( 0, 0 ); + + pInfo->m_RootNode = CVertIndex( sideLength/2, sideLength/2 ); + pInfo->m_SideLength = sideLength; + pInfo->m_SideLengthM1 = sideLength - 1; + pInfo->m_MidPoint = sideLength / 2; + pInfo->m_MaxVerts = sideLength * sideLength; + + // Setup the corner indices. + pInfo->m_CornerPointIndices[CORNER_LOWER_LEFT].Init( 0, 0 ); + pInfo->m_CornerPointIndices[CORNER_UPPER_LEFT].Init( 0, sideLength-1 ); + pInfo->m_CornerPointIndices[CORNER_UPPER_RIGHT].Init( sideLength-1, sideLength-1 ); + pInfo->m_CornerPointIndices[CORNER_LOWER_RIGHT].Init( sideLength-1, 0 ); + + InitPowerInfo_R( + pInfo, + iMaxPower, + pInfo->m_RootNode, + + nodeDependency1, // dependencies + nodeDependency2, + + CVertIndex(0,0), // error edge + CVertIndex(sideLength-1, sideLength-1), + + CVertIndex(-1,-1), // parent + 0 ); + + pInfo->m_Power = iMaxPower; + + CTriInfo *pTriInfo = pInfo->m_pTriInfos; + InitPowerInfoTriInfos_R( pInfo, pInfo->m_RootNode, pTriInfo, iMaxPower, 0 ); + + for( int iEdge=0; iEdge < 4; iEdge++ ) + { + // Figure out the start vert and increment. + CVertIndex nextVert; + GetEdgeVertIndex( sideLength, iEdge, 0, pInfo->m_EdgeStartVerts[iEdge] ); + GetEdgeVertIndex( sideLength, iEdge, 1, nextVert ); + pInfo->m_EdgeIncrements[iEdge] = nextVert - pInfo->m_EdgeStartVerts[iEdge]; + + // Now get the neighbor's start vert and increment. + CVertIndex nbStartVert, nbNextVert, nbDelta; + GetEdgeVertIndex( sideLength, (iEdge+2)&3, 0, nbStartVert ); + GetEdgeVertIndex( sideLength, (iEdge+2)&3, 1, nbNextVert ); + nbDelta = nbNextVert - nbStartVert; + + // Rotate it for each orientation. + for( int orient=0; orient < 4; orient++ ) + { + pInfo->m_NeighborStartVerts[iEdge][orient] = Transform2D( + g_OrientationRotations[orient], + nbStartVert, + CVertIndex( sideLength/2, sideLength/2 ) ); + + pInfo->m_NeighborIncrements[iEdge][orient] = Transform2D( + g_OrientationRotations[orient], + nbDelta, + CVertIndex(0,0) ); + } + } + + + // Init the node index increments. + int curPowerOf4 = 1; + int curTotal = 0; + for( int i=0; i < iMaxPower-1; i++ ) + { + curTotal += curPowerOf4; + + pInfo->m_NodeIndexIncrements[iMaxPower-i-2] = curTotal; + + curPowerOf4 *= 4; + } + + // Store off the total node count + pInfo->m_NodeCount = curTotal + curPowerOf4; + + pInfo->m_nTriInfos = Square( 1 << iMaxPower ) * 2; +} + +class CPowerInfoInitializer +{ +public: + CPowerInfoInitializer() + { + Assert( MAX_MAP_DISP_POWER+1 == NUM_POWERINFOS ); + + for( int i=0; i <= MAX_MAP_DISP_POWER; i++ ) + { + if( g_PowerInfos[i] ) + { + InitPowerInfo( g_PowerInfos[i], i ); + } + } + } +}; + +static CPowerInfoInitializer g_PowerInfoInitializer; + + +const CPowerInfo* GetPowerInfo( int iPower ) +{ + Assert( iPower >= 0 && iPower < ARRAYSIZE( g_PowerInfos ) ); + Assert( g_PowerInfos[iPower] ); + return g_PowerInfos[iPower]; +} + + +// ------------------------------------------------------------------------------------------------ // +// CPowerInfo member function initialization. +// ------------------------------------------------------------------------------------------------ // + +const CVertIndex& CPowerInfo::GetCornerPointIndex( int iCorner ) const +{ + Assert( iCorner >= 0 && iCorner < 4 ); + return m_CornerPointIndices[iCorner]; +} + diff --git a/public/disp_powerinfo.h b/public/disp_powerinfo.h new file mode 100644 index 0000000..456feea --- /dev/null +++ b/public/disp_powerinfo.h @@ -0,0 +1,215 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This module defines the CPowerInfo class, which contains a +// whole bunch of precalculated data for each displacement power. +// It holds data that indicates how to tesselate, how to access +// neighbor displacements, etc. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISP_POWERINFO_H +#define DISP_POWERINFO_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "disp_vertindex.h" +#include "bspfile.h" + + +#define NUM_POWERINFOS (MAX_MAP_DISP_POWER+1) + + +struct DispNodeInfo_t +{ + enum + { + // Indicates if any children at all have triangles + CHILDREN_HAVE_TRIANGLES = 0x1 + }; + + + // Indicates which tesselation indices are associated with a node + unsigned short m_FirstTesselationIndex; + unsigned char m_Count; + unsigned char m_Flags; + Vector m_mins; // AABB for node triangles + Vector m_maxs; +}; + + +// ------------------------------------------------------------------------ // +// CTesselateWindings are used to tell what order a node needs to visit +// vertices while tesselating. +// ------------------------------------------------------------------------ // +class CTesselateVert +{ +public: + CTesselateVert( CVertIndex const &index, int iNode ); + + CVertIndex m_Index; + short m_iNode; // Which node this vert is a part of (-1 on left, right, up, and down). +}; + + +class CTesselateWinding +{ +public: + CTesselateVert *m_Verts; + short m_nVerts; // (includes the last vert) +}; + + +class CVertDependency +{ +public: + + // Returns false if there is no dependency stored here. + bool IsValid() { return m_iVert.x != -1; } + + +public: + + // The vert index is in the same power as the source displacement. + // It is also wrapped, so for example, on the middle of the right edge + // of a 3x3, it will have a dependency on the 3x3's root node (1,1), and it + // will have another (1,1) entry that references a neighbor. + CVertIndex m_iVert; + + // This is -1 if the vert exists inside the source displacement. + // It is one of the NEIGHBOREDGE_ codes above if it reaches into a neighbor. + short m_iNeighbor; +}; + + +// Precalculated data about displacement vertices. +class CVertInfo +{ +public: + CVertInfo(); + + // These are the vertices that this vertex depends on (vertices that must be + // active for this vert to exist). + CVertDependency m_Dependencies[2]; + + // These are the vertices that have this vert in their m_Dependencies. + enum { NUM_REVERSE_DEPENDENCIES=4 }; + CVertDependency m_ReverseDependencies[NUM_REVERSE_DEPENDENCIES]; + + short m_iNodeLevel; // -1 if this is not a node. Otherwise, the recursion level + // of this node (root node = 1). + CVertIndex m_iParent; // x=-1 if this is a not a node or if it's the root node. +}; + + +class CTwoUShorts +{ +public: + unsigned short m_Values[2]; +}; + + +class CFourVerts +{ +public: + CVertIndex m_Verts[4]; +}; + + +// Used for referencing triangles in the fully-tesselated displacement by index. +class CTriInfo +{ +public: + unsigned short m_Indices[3]; +}; + + +// Precalculated data for displacements of a certain power. +class CPowerInfo +{ +public: + CPowerInfo( + CVertInfo *pVertInfo, + CFourVerts *pSideVerts, + CFourVerts *pChildVerts, + CFourVerts *pSideVertCorners, + CTwoUShorts *pErrorEdges, + CTriInfo *pTriInfos ); + + int GetPower() const { return m_Power; } + int GetSideLength() const { return m_SideLength; } + const CVertIndex& GetRootNode() const { return m_RootNode; } + int GetMidPoint() const { return m_MidPoint; } // Half the edge length. + + // Get at the tri list. + int GetNumTriInfos() const { return m_nTriInfos; } + const CTriInfo* GetTriInfo( int i ) const { return &m_pTriInfos[i]; } + + // Get the number of vertices in a displacement of this power. + int GetNumVerts() const { return m_MaxVerts; } + + // Return a corner point index. Indexed by the CORNER_ defines. + const CVertIndex& GetCornerPointIndex( int iCorner ) const; + + +public: + + CVertInfo *m_pVertInfo; + CFourVerts *m_pSideVerts; // The 4 side verts for each node. + CFourVerts *m_pChildVerts; // The 4 children for each node. + CFourVerts *m_pSideVertCorners; + CTwoUShorts *m_pErrorEdges; // These are the edges + // that are used to measure the screenspace + // error with respect to each vert. + + CTriInfo *m_pTriInfos; + int m_nTriInfos; + + int m_Power; + + CVertIndex m_RootNode; + int m_SideLength; + int m_SideLengthM1; // Side length minus 1. + int m_MidPoint; // Side length / 2. + int m_MaxVerts; // m_SideLength * m_SideLength + int m_NodeCount; // total # of nodes, including children + + // Precalculated increments if you're using a bit vector to represent nodes. + // Starting at level 0 of the tree, this stores the increment between the nodes at this + // level. Vectors holding node data are stored in preorder traversal, and these + // increments tell the number of elements between nodes at each level. + int m_NodeIndexIncrements[MAX_MAP_DISP_POWER]; + + CVertIndex m_EdgeStartVerts[4]; + CVertIndex m_EdgeIncrements[4]; + + CVertIndex m_NeighborStartVerts[4][4]; // [side][orientation] + CVertIndex m_NeighborIncrements[4][4]; // [side][orientation] + + +private: + friend void InitPowerInfo( CPowerInfo *pInfo, int iMaxPower ); + + CVertIndex m_CornerPointIndices[4]; +}; + + +// ----------------------------------------------------------------------------- // +// Globals. +// ----------------------------------------------------------------------------- // + +// Indexed by the TWINDING_ enums. +extern CTesselateWinding g_TWinding; + + +// ----------------------------------------------------------------------------- // +// Functions. +// ----------------------------------------------------------------------------- // + +// Valid indices are MIN_MAP_DISP_POWER through (and including) MAX_MAP_DISP_POWER. +const CPowerInfo* GetPowerInfo( int iPower ); + + +#endif // DISP_POWERINFO_H diff --git a/public/disp_tesselate.h b/public/disp_tesselate.h new file mode 100644 index 0000000..b365189 --- /dev/null +++ b/public/disp_tesselate.h @@ -0,0 +1,206 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISP_TESSELATE_H +#define DISP_TESSELATE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "disp_powerinfo.h" + + +inline int InternalVertIndex( const CPowerInfo *pInfo, const CVertIndex &vert ) +{ + return vert.y * pInfo->m_SideLength + vert.x; +} + + +template< class TesselateHelper > +inline void InternalEndTriangle( + TesselateHelper *pHelper, + CVertIndex const &nodeIndex, + int &iCurTriVert ) +{ + // End our current triangle here. + Assert( iCurTriVert == 2 ); + + // Finish the triangle. + pHelper->m_TempIndices[2] = (unsigned short)InternalVertIndex( pHelper->m_pPowerInfo, nodeIndex ); + + pHelper->EndTriangle(); + + // Add on the last vertex to join to the next triangle. + pHelper->m_TempIndices[0] = pHelper->m_TempIndices[1]; + iCurTriVert = 1; +} + + +//----------------------------------------------------------------------------- +// Tesselates a single node, doesn't deal with hierarchy +//----------------------------------------------------------------------------- +template< class TesselateHelper > +inline void TesselateDisplacementNode( + TesselateHelper *pHelper, + CVertIndex const &nodeIndex, + int iLevel, + int *pActiveChildren ) +{ + int iPower = pHelper->m_pPowerInfo->m_Power - iLevel; + int vertInc = 1 << (iPower - 1); + + CTesselateWinding *pWinding = &g_TWinding; + + // Starting at the bottom-left, wind clockwise picking up vertices and + // generating triangles. + int iCurTriVert = 0; + for( int iVert=0; iVert < pWinding->m_nVerts; iVert++ ) + { + CVertIndex sideVert = BuildOffsetVertIndex( nodeIndex, pWinding->m_Verts[iVert].m_Index, vertInc ); + + int iVertNode = pWinding->m_Verts[iVert].m_iNode; + bool bNode = (iVertNode != -1) && pActiveChildren[iVertNode]; + if( bNode ) + { + if( iCurTriVert == 2 ) + InternalEndTriangle( pHelper, nodeIndex, iCurTriVert ); + + iCurTriVert = 0; + } + else + { + int iVertBit = InternalVertIndex( pHelper->m_pPowerInfo, sideVert ); + if( pHelper->m_pActiveVerts[iVertBit>>5] & (1 << (iVertBit & 31)) ) + { + // Ok, add a vert here. + pHelper->m_TempIndices[iCurTriVert] = (unsigned short)InternalVertIndex( pHelper->m_pPowerInfo, sideVert ); + iCurTriVert++; + if( iCurTriVert == 2 ) + InternalEndTriangle( pHelper, nodeIndex, iCurTriVert ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Tesselates in a *breadth first* fashion +//----------------------------------------------------------------------------- +template< class T > +inline void TesselateDisplacement_R( + T *pHelper, + const CVertIndex &nodeIndex, + int iNodeBitIndex, + int iLevel + ) +{ + // Here's the node info for our current node + Assert( iNodeBitIndex < pHelper->m_pPowerInfo->m_NodeCount ); + DispNodeInfo_t& nodeInfo = pHelper->GetNodeInfo( iNodeBitIndex ); + + // Store off the current number of indices + int oldIndexCount = pHelper->m_nIndices; + + // Go through each quadrant. If there is an active child node, recurse down. + int bActiveChildren[4]; + if( iLevel >= pHelper->m_pPowerInfo->m_Power - 1 ) + { + // This node has no children. + bActiveChildren[0] = bActiveChildren[1] = bActiveChildren[2] = bActiveChildren[3] = false; + } + else + { + int iNodeIndex = InternalVertIndex( pHelper->m_pPowerInfo, nodeIndex ); + + int iChildNodeBit = iNodeBitIndex + 1; + for( int iChild=0; iChild < 4; iChild++ ) + { + CVertIndex const &childNode = pHelper->m_pPowerInfo->m_pChildVerts[iNodeIndex].m_Verts[iChild]; + + // Make sure we really can tesselate here (a smaller neighbor displacement could + // have inactivated certain edge verts. + int iVertBit = InternalVertIndex( pHelper->m_pPowerInfo, childNode ); + bActiveChildren[iChild] = ( pHelper->m_pActiveVerts[iVertBit>>5] & (1 << (iVertBit & 31)) ); + + if( bActiveChildren[iChild] ) + { + TesselateDisplacement_R( pHelper, childNode, iChildNodeBit, iLevel+1 ); + } + else + { + // Make sure the triangle counts are cleared on this one because it may visit this + // node in GenerateDecalFragments_R if nodeInfo's CHILDREN_HAVE_TRIANGLES flag is set. + DispNodeInfo_t &childInfo = pHelper->GetNodeInfo( iChildNodeBit ); + childInfo.m_Count = 0; + childInfo.m_Flags = 0; + } + + iChildNodeBit += pHelper->m_pPowerInfo->m_NodeIndexIncrements[iLevel]; + } + } + + // Set the child field + if ( pHelper->m_nIndices != oldIndexCount ) + { + nodeInfo.m_Flags = DispNodeInfo_t::CHILDREN_HAVE_TRIANGLES; + oldIndexCount = pHelper->m_nIndices; + } + else + { + nodeInfo.m_Flags = 0; + } + + // Now tesselate the node itself... + TesselateDisplacementNode( pHelper, nodeIndex, iLevel, bActiveChildren ); + + // Now that we've tesselated, figure out how many indices we've added at this node + nodeInfo.m_Count = pHelper->m_nIndices - oldIndexCount; + nodeInfo.m_FirstTesselationIndex = oldIndexCount; + Assert( nodeInfo.m_Count % 3 == 0 ); +} + + +class CBaseTesselateHelper +{ +public: + + // Functions your derived class must implement: + // void EndTriangle(); // (the 3 indices are in m_TempIndices). + // DispNodeInfo_t& GetNodeInfo( int iNodeBit ); + + + // Set these before calling TesselateDisplacement. + uint32 *m_pActiveVerts; // These bits control the tesselation. + const CPowerInfo *m_pPowerInfo; // Lots of precalculated data about a displacement this size. + + + // Used internally by TesselateDisplacement. + int m_nIndices; // After calling TesselateDisplacement, this is set to the # of indices generated. + unsigned short m_TempIndices[6]; +}; + + + +// This interface is shared betwixt VBSP and the engine. VBSP uses it to build the +// physics mesh and the engine uses it to render. +// +// To use this function, derive a class from CBaseTesselateHelper that supports the TesselateHelper functions. +template< class TesselateHelper > +inline void TesselateDisplacement( TesselateHelper *pHelper ) +{ + pHelper->m_nIndices = 0; + + TesselateDisplacement_R( + pHelper, + pHelper->m_pPowerInfo->m_RootNode, + 0, // node bit indexing CDispDecal::m_NodeIntersects + 0 ); +} + + +#endif // DISP_TESSELATE_H diff --git a/public/disp_vertindex.h b/public/disp_vertindex.h new file mode 100644 index 0000000..5fb66a9 --- /dev/null +++ b/public/disp_vertindex.h @@ -0,0 +1,150 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISP_VERTINDEX_H +#define DISP_VERTINDEX_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/dbg.h" + + +// ------------------------------------------------------------------------ // +// Helper class used for indexing vertices in the 2D grid. +// ------------------------------------------------------------------------ // + +class CVertIndex +{ +public: + CVertIndex(); + CVertIndex( short ix, short iy ); + + void Init( short ix, short iy ); + + short& operator[]( short i ); + short const& operator[]( short i ) const; + void operator+=( CVertIndex const &other ); + void operator-=( CVertIndex const &other ); + CVertIndex operator+( CVertIndex const &other ) const; + CVertIndex operator-( CVertIndex const &other ) const; + void operator<<=( int shift ); + void operator>>=( int shift ); + bool operator==( CVertIndex const &other ) const; + bool operator!=( CVertIndex const &other ) const; + + +public: + + short x, y; +}; + + +// ------------------------------------------------------------------ // +// Helper functions. +// ------------------------------------------------------------------ // + +inline CVertIndex BuildOffsetVertIndex( + CVertIndex const &nodeIndex, + CVertIndex const &offset, + int mul ) +{ + return CVertIndex( nodeIndex.x + offset.x * mul, nodeIndex.y + offset.y * mul ); +} + + +// ------------------------------------------------------------------ // +// CVertIndex inlines. +// ------------------------------------------------------------------ // + +inline CVertIndex::CVertIndex() +{ +} + + +inline CVertIndex::CVertIndex( short ix, short iy ) +{ + x = ix; + y = iy; +} + + +inline void CVertIndex::Init( short ix, short iy ) +{ + x = ix; + y = iy; +} + + +inline short& CVertIndex::operator[]( short i ) +{ + Assert( i >= 0 && i <= 1 ); + return ((short*)this)[i]; +} + + +inline short const& CVertIndex::operator[]( short i ) const +{ + Assert( i >= 0 && i <= 1 ); + return ((short*)this)[i]; +} + + +inline void CVertIndex::operator+=( CVertIndex const &other ) +{ + x += other.x; + y += other.y; +} + + +inline void CVertIndex::operator-=( CVertIndex const &other ) +{ + x -= other.x; + y -= other.y; +} + + +inline CVertIndex CVertIndex::operator+( CVertIndex const &other ) const +{ + return CVertIndex( x + other.x, y + other.y ); +} + + +inline CVertIndex CVertIndex::operator-( CVertIndex const &other ) const +{ + return CVertIndex( x - other.x, y - other.y ); +} + + +inline void CVertIndex::operator<<=( int shift ) +{ + x <<= shift; + y <<= shift; +} + + +inline void CVertIndex::operator>>=( int shift ) +{ + x >>= shift; + y >>= shift; +} + + +inline bool CVertIndex::operator==( CVertIndex const &other ) const +{ + return x==other.x && y==other.y; +} + + +inline bool CVertIndex::operator!=( CVertIndex const &other ) const +{ + return x!=other.x || y!=other.y; +} + + +#endif // DISP_VERTINDEX_H diff --git a/public/dispcoll.cpp b/public/dispcoll.cpp new file mode 100644 index 0000000..7ef3f7a --- /dev/null +++ b/public/dispcoll.cpp @@ -0,0 +1,1994 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "BuildDisp.h" +#include "DispColl.h" +#include "tier0/dbg.h" + +//============================================================================= + +const float CDispCollTree::COLLISION_EPSILON = 0.01f; +const float CDispCollTree::ONE_MINUS_COLLISION_EPSILON = 1.0f - COLLISION_EPSILON; + +//============================================================================= +// +// Displacement Collision Triangle Functions +// + + +//----------------------------------------------------------------------------- +// Purpose: initialize the displacement triangles +//----------------------------------------------------------------------------- +void CDispCollTri::Init( void ) +{ + for( int i = 0; i < 3; i++ ) + { + m_Points[i].x = 0.0f; m_Points[i].y = 0.0f; m_Points[i].z = 0.0f; + m_PointNormals[i].x = 0.0f; m_PointNormals[i].y = 0.0f; m_PointNormals[i].z = 0.0f; + } + + m_Normal.x = 0.0f; m_Normal.y = 0.0f; m_Normal.z = 0.0f; + m_Distance = 0.0f; + + m_ProjAxes[0] = -1; + m_ProjAxes[1] = -1; + + m_bIntersect = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetPoint( int index, Vector const &vert ) +{ + Assert( index >= 0 ); + Assert( index < 3 ); + + m_Points[index].x = vert[0]; + m_Points[index].y = vert[1]; + m_Points[index].z = vert[2]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetPointNormal( int index, Vector const &normal ) +{ + Assert( index >= 0 ); + Assert( index < 3 ); + + m_PointNormals[index].x = normal[0]; + m_PointNormals[index].y = normal[1]; + m_PointNormals[index].z = normal[2]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTri::CalcPlane( void ) +{ + // + // calculate the plane normal and distance + // + Vector segment1, segment2, cross; + + segment1 = m_Points[1] - m_Points[0]; + segment2 = m_Points[2] - m_Points[0]; + cross = segment1.Cross( segment2 ); + m_Normal = cross; + VectorNormalize(m_Normal); + + m_Distance = m_Normal.Dot( m_Points[0] ); + + // + // calculate the projection axes + // + if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[1] ) ) + { + if( FloatMakePositive( m_Normal[0] ) > FloatMakePositive( m_Normal[2] ) ) + { + m_ProjAxes[0] = 1; + m_ProjAxes[1] = 2; + } + else + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 1; + } + } + else + { + if( FloatMakePositive( m_Normal[1] ) > FloatMakePositive( m_Normal[2] ) ) + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 2; + } + else + { + m_ProjAxes[0] = 0; + m_ProjAxes[1] = 1; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTri::SetIntersect( bool bIntersect ) +{ + m_bIntersect = bIntersect; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTri::IsIntersect( void ) +{ + return m_bIntersect; +} + + +//============================================================================= +// +// Displacement Collision Node Functions +// + + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDispCollNode::CDispCollNode() +{ + m_Bounds[0].x = m_Bounds[0].y = m_Bounds[0].z = 99999.9f; + m_Bounds[1].x = m_Bounds[1].y = m_Bounds[1].z = -99999.9f; + + m_Tris[0].Init(); + m_Tris[1].Init(); + + m_bIsLeaf = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollNode::IsLeaf( void ) +{ + return m_bIsLeaf; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollNode::SetBounds( Vector const &bMin, Vector const &bMax ) +{ + m_Bounds[0] = bMin; + m_Bounds[1] = bMax; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollNode::GetBounds( Vector &bMin, Vector &bMax ) +{ + bMin = m_Bounds[0]; + bMax = m_Bounds[1]; +} + + +//============================================================================= +// +// Displacement Collision Tree Functions +// + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDispCollTree::CDispCollTree() +{ + m_Power = 0; + + m_NodeCount = 0; + m_pNodes = NULL; + + InitAABBData(); +} + + +//----------------------------------------------------------------------------- +// Purpose: deconstructor +//----------------------------------------------------------------------------- +CDispCollTree::~CDispCollTree() +{ + FreeNodes(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::InitAABBData( void ) +{ + m_AABBNormals[0].x = -1.0f; m_AABBNormals[0].y = 0.0f; m_AABBNormals[0].z = 0.0f; + m_AABBNormals[1].x = 1.0f; m_AABBNormals[1].y = 0.0f; m_AABBNormals[1].z = 0.0f; + + m_AABBNormals[2].x = 0.0f; m_AABBNormals[2].y = -1.0f; m_AABBNormals[2].z = 0.0f; + m_AABBNormals[3].x = 0.0f; m_AABBNormals[3].y = 1.0f; m_AABBNormals[3].z = 0.0f; + + m_AABBNormals[4].x = 0.0f; m_AABBNormals[4].y = 0.0f; m_AABBNormals[4].z = -1.0f; + m_AABBNormals[5].x = 0.0f; m_AABBNormals[5].y = 0.0f; m_AABBNormals[5].z = 1.0f; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CalcBounds( CDispCollNode *pNode, int nodeIndex ) +{ + Vector bounds[2]; + bounds[0].Init( 99999.9f, 99999.9f, 99999.9f ); + bounds[1].Init( -99999.9f, -99999.9f, -99999.9f ); + + // + // handle leaves differently -- bounding volume defined by triangles + // + if( pNode->IsLeaf() ) + { + for( int i = 0; i < 2; i++ ) + { + for( int j = 0; j < 3; j++ ) + { + // + // minimum + // + if( bounds[0].x > pNode->m_Tris[i].m_Points[j].x ) { bounds[0].x = pNode->m_Tris[i].m_Points[j].x; } + if( bounds[0].y > pNode->m_Tris[i].m_Points[j].y ) { bounds[0].y = pNode->m_Tris[i].m_Points[j].y; } + if( bounds[0].z > pNode->m_Tris[i].m_Points[j].z ) { bounds[0].z = pNode->m_Tris[i].m_Points[j].z; } + + // + // maximum + // + if( bounds[1].x < pNode->m_Tris[i].m_Points[j].x ) { bounds[1].x = pNode->m_Tris[i].m_Points[j].x; } + if( bounds[1].y < pNode->m_Tris[i].m_Points[j].y ) { bounds[1].y = pNode->m_Tris[i].m_Points[j].y; } + if( bounds[1].z < pNode->m_Tris[i].m_Points[j].z ) { bounds[1].z = pNode->m_Tris[i].m_Points[j].z; } + } + } + } + // + // bounding volume defined by maxima and minima of children volumes + // + else + { + for( int i = 0; i < 4; i++ ) + { + int childIndex = GetChildNode( nodeIndex, i ); + CDispCollNode *pChildNode = &m_pNodes[childIndex]; + + Vector childBounds[2]; + pChildNode->GetBounds( childBounds[0], childBounds[1] ); + + // + // minimum + // + if( bounds[0].x > childBounds[0].x ) { bounds[0].x = childBounds[0].x; } + if( bounds[0].y > childBounds[0].y ) { bounds[0].y = childBounds[0].y; } + if( bounds[0].z > childBounds[0].z ) { bounds[0].z = childBounds[0].z; } + + // + // maximum + // + if( bounds[1].x < childBounds[1].x ) { bounds[1].x = childBounds[1].x; } + if( bounds[1].y < childBounds[1].y ) { bounds[1].y = childBounds[1].y; } + if( bounds[1].z < childBounds[1].z ) { bounds[1].z = childBounds[1].z; } + } + } + + pNode->SetBounds( bounds[0], bounds[1] ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ) +{ + int nodeLevel = GetNodeLevel( nodeIndex ); + + // + // terminating condition -- set node info (leaf or otherwise) + // + if( nodeLevel == termLevel ) + { + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + CalcBounds( pNode, nodeIndex ); + + return; + } + + // + // recurse into children + // + for( int i = 0; i < 4; i++ ) + { + CreateNodes_r( pDisp, GetChildNode( nodeIndex, i ), termLevel ); + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreateNodes( CCoreDispInfo *pDisp ) +{ + // + // create all nodes in tree + // + int power = pDisp->GetPower() + 1; + for( int level = power; level > 0; level-- ) + { + CreateNodes_r( pDisp, 0 /* rootIndex */, level ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CDispCollTree::GetNodeIndexFromComponents( int x, int y ) +{ + int index = 0; + + // Interleave bits from the x and y values to create the index: + + for( int shift = 0; x != 0; shift += 2, x >>= 1 ) + { + index |= ( x & 1 ) << shift; + } + + for( shift = 1; y != 0; shift += 2, y >>= 1 ) + { + index |= ( y & 1 ) << shift; + } + + return index; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::InitLeaves( CCoreDispInfo *pDisp ) +{ + // + // get power and width and displacement surface + // + int power = pDisp->GetPower(); + int width = pDisp->GetWidth(); + + // + // get leaf indices + // + int startIndex = CalcNodeCount( power - 1 ); + int endIndex = CalcNodeCount( power ); + + for( int index = startIndex; index < endIndex; index++ ) + { + // + // create triangles at leaves + // + int x = ( index - startIndex ) % ( width - 1 ); + int y = ( index - startIndex ) / ( width - 1 ); + + int nodeIndex = GetNodeIndexFromComponents( x, y ); + nodeIndex += startIndex; + + Vector vert; + Vector normal; + + // + // tri 1 + // + pDisp->GetVert( x + ( y * width ), vert ); + pDisp->GetNormal( x + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 0, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 0, normal ); + + pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 1, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 1, normal ); + + pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[0].SetPoint( 2, vert ); + m_pNodes[nodeIndex].m_Tris[0].SetPointNormal( 2, normal ); + + m_pNodes[nodeIndex].m_Tris[0].CalcPlane(); + + // + // tri 2 + // + pDisp->GetVert( ( x + 1 ) + ( y * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( y * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 0, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 0, normal ); + + pDisp->GetVert( x + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( x + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 1, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 1, normal ); + + pDisp->GetVert( ( x + 1 ) + ( ( y + 1 ) * width ), vert ); + pDisp->GetNormal( ( x + 1 ) + ( ( y + 1 ) * width ), normal ); + m_pNodes[nodeIndex].m_Tris[1].SetPoint( 2, vert ); + m_pNodes[nodeIndex].m_Tris[1].SetPointNormal( 2, normal ); + + m_pNodes[nodeIndex].m_Tris[1].CalcPlane(); + + // set node as leaf + m_pNodes[nodeIndex].m_bIsLeaf = true; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: allocate and initialize the displacement collision tree +// Input: power - size of the displacement surface +// Output: bool - success? (true/false) +//----------------------------------------------------------------------------- +bool CDispCollTree::Create( CCoreDispInfo *pDisp ) +{ + // + // calculate the number of nodes needed given the size of the displacement + // + m_Power = pDisp->GetPower(); + m_NodeCount = CalcNodeCount( m_Power ); + + // + // allocate tree space + // + if( !AllocNodes( m_NodeCount ) ) + return false; + + // initialize leaves + InitLeaves( pDisp ); + + // create tree nodes + CreateNodes( pDisp ); + + // tree successfully created! + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: allocate memory for the displacement collision tree +// Input: nodeCount - number of nodes to allocate +// Output: bool - success? (true/false) +//----------------------------------------------------------------------------- +bool CDispCollTree::AllocNodes( int nodeCount ) +{ + // sanity check + Assert( nodeCount != 0 ); + + m_pNodes = new CDispCollNode[nodeCount]; + if( !m_pNodes ) + return false; + + // tree successfully allocated! + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: release allocated memory for displacement collision tree +//----------------------------------------------------------------------------- +void CDispCollTree::FreeNodes( void ) +{ + if( m_pNodes ) + { + delete [] m_pNodes; + m_pNodes = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: calculate the number of tree nodes given the size of the +// displacement surface +// Input: power - size of the displacement surface +// Output: int - the number of tree nodes +//----------------------------------------------------------------------------- +inline int CDispCollTree::CalcNodeCount( int power ) +{ + // power range [2...4] + Assert( power > 0 ); + Assert( power < 5 ); + + return ( ( 1 << ( ( power + 1 ) << 1 ) ) / 3 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the parent node index given the current node +// Input: nodeIndex - current node index +// Output: int - the index of the parent node +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetParentNode( int nodeIndex ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // ( nodeIndex - 1 ) / 4 + return ( ( nodeIndex - 1 ) >> 2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: get the child node index given the current node index and direction +// of the child (1 of 4) +// Input: nodeIndex - current node index +// direction - direction of the child ( [0...3] - SW, SE, NW, NE ) +// Output: int - the index of the child node +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetChildNode( int nodeIndex, int direction ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // ( nodeIndex * 4 ) + ( direction + 1 ) + return ( ( nodeIndex << 2 ) + ( direction + 1 ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetNodeLevel( int nodeIndex ) +{ + // node range [0...m_NodeCount) + Assert( nodeIndex >= 0 ); + Assert( nodeIndex < m_NodeCount ); + + // level = 2^n + 1 + if( nodeIndex == 0 ) { return 1; } + if( nodeIndex < 5 ) { return 2; } + if( nodeIndex < 21 ) { return 3; } + if( nodeIndex < 85 ) { return 4; } + if( nodeIndex < 341 ) { return 5; } + + return -1; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, + CDispCollTri const *pTri, float *fraction ) +{ + const float DET_EPSILON = 0.001f; + const float DIST_EPSILON = 0.001f; + + // + // calculate the edges + // + Vector edge1 = pTri->m_Points[1] - pTri->m_Points[0]; + Vector edge2 = pTri->m_Points[2] - pTri->m_Points[0]; + +// Vector faceNormal = edge1.Cross( edge2 ); +// Vector normNormal = faceNormal.Normalize(); + + // + // calculate the triangle's determinant + // + Vector pVec = rayDir.Cross( edge2 ); + float det = pVec.Dot( edge1 ); + + // if determinant is zero -- ray lies in plane + if( ( det > -DET_EPSILON ) && ( det < DET_EPSILON ) ) + return false; + + // + // utility calculations - inverse determinant and distance from v0 to ray start + // + double invDet = 1.0f / det; + Vector tVec = rayStart - pTri->m_Points[0]; + + // + // calculate the U parameter and test bounds + // + double u = pVec.Dot( tVec ) * invDet; + if( ( u < 0.0f ) || ( u > 1.0f ) ) + return false; + + Vector qVec = tVec.Cross( edge1 ); + + // + // calculate the V parameter and test bounds + // + double v = qVec.Dot( rayDir ) * invDet; + if( ( v < 0.0f ) || ( ( u + v ) > 1.0f ) ) + return false; + + // calculate where ray intersects triangle + *fraction = qVec.Dot( edge2 ) * invDet; + *fraction /= rayLength; + + if( ( *fraction < DIST_EPSILON ) || ( *fraction > ( 1.0f - DIST_EPSILON ) ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ) +{ + // save starting fraction -- to test for collision + float startFraction = pData->m_Fraction; + + // + // calculate the ray + // + Vector seg = pData->m_EndPos - pData->m_StartPos; + Vector rayDir = seg; + float rayLength = VectorNormalize( rayDir ); + + // + // test ray against all triangles in list + // + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + float fraction = 1.0f; + bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, pTemp->m_ppTriList[i], &fraction ); + if( !bResult ) + continue; + + if( pData->m_bOcclude ) + { + return true; + } + + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = pTemp->m_ppTriList[i]->m_Normal; + pData->m_Distance = pTemp->m_ppTriList[i]->m_Distance; + } + } + + // collision! + if( pData->m_Fraction < startFraction ) + return true; + + // no collision! + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ) +{ + const float MY_DIST_EPSILON = 0.01f; + + for( int i = 0; i < 6; i++ ) + { + float dist1 = m_AABBNormals[i].Dot( rayStart ) - pTemp->m_AABBDistances[i]; + float dist2 = m_AABBNormals[i].Dot( rayEnd ) - pTemp->m_AABBDistances[i]; + + // + // entry intersection point - move ray start up to intersection + // + if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 < -MY_DIST_EPSILON ) ) + { + float fraction = ( dist1 / ( dist1 - dist2 ) ); + + Vector segment, increment; + segment = ( rayEnd - rayStart ) * fraction; + increment = segment; + VectorNormalize(increment); + segment += increment; + rayStart += segment; + } + else if( ( dist1 > MY_DIST_EPSILON ) && ( dist2 > MY_DIST_EPSILON ) ) + { + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ) +{ + // + // note -- these never change! + // +// m_AABBNormals[0].x = -1; +// m_AABBNormals[1].x = 1; + +// m_AABBNormals[2].y = -1; +// m_AABBNormals[3].y = 1; + +// m_AABBNormals[4].z = -1; +// m_AABBNormals[5].z = 1; + + pTemp->m_AABBDistances[0] = -bbMin.x; + pTemp->m_AABBDistances[1] = bbMax.x; + + pTemp->m_AABBDistances[2] = -bbMin.y; + pTemp->m_AABBDistances[3] = bbMax.y; + + pTemp->m_AABBDistances[4] = -bbMin.z; + pTemp->m_AABBDistances[5] = bbMax.z; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ) +{ + // get the current node + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + + // + // get node bounding box and create collision planes + // + Vector bounds[2]; + pNode->GetBounds( bounds[0], bounds[1] ); + CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); + + bool bIntersect = RayAABBTest( pTemp, rayStart, rayEnd ); + if( bIntersect ) + { + // done -- add triangles to triangle list + if( pNode->IsLeaf() ) + { + // Assert for now -- flush cache later!!!!! + Assert( pTemp->m_TriListCount >= 0 ); + Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); + + pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; + pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; + pTemp->m_TriListCount += 2; + } + // continue recursion + else + { + for( int i = 0; i < 4; i++ ) + { + RayNodeTest_r( pTemp, GetChildNode( nodeIndex, i ), rayStart, rayEnd ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTestAllTris( CDispCollData *pData, int power ) +{ + // + // get leaf indices + // + int startIndex = CalcNodeCount( power - 1 ); + int endIndex = CalcNodeCount( power ); + + // save incoming fraction + float startFraction = pData->m_Fraction; + float fraction = pData->m_Fraction; + + Vector ray = pData->m_EndPos - pData->m_StartPos; + Vector rayDir = ray; + float rayLength = VectorNormalize(rayDir); + + // + // test ray against all triangles in list + // + for( int index = startIndex; index < endIndex; index++ ) + { + for( int j = 0; j < 2; j++ ) + { + bool bResult = RayTriTest( pData->m_StartPos, rayDir, rayLength, &m_pNodes[index].m_Tris[j], &fraction ); + if( !bResult ) + continue; + + if( pData->m_bOcclude ) + { + return true; + } + + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = m_pNodes[index].m_Tris[j].m_Normal; + pData->m_Distance = m_pNodes[index].m_Tris[j].m_Distance; + } + } + } + + // collision! + if( pData->m_Fraction < startFraction ) + return true; + + // no collision! + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::RayTest( CDispCollData *pData ) +{ + // reset the triangle list count + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // trace against nodes (copy start, end because they change) + RayNodeTest_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos ); + + // + // trace against tris (if need be) + // + if( tmp.m_TriListCount != 0 ) + { + bool result = RayTriListTest( &tmp, pData ); + return result; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri, Vector &plNormal, float *plDist, + float *fraction ) +{ + + // + // PUT A COPY HERE OF START AND END -- SINCE I CHANGE THEM!!!!!! + // + + + + + + int dir, ptIndex; + float closeValue; + float distStart, distEnd; + float t; + Vector rayPt; + + // get ray direction + Vector rayDir = rayEnd - rayStart; + + // initialize fraction + *fraction = 1.0f; + + // + // test for collision with axial planes (x, y, z) + // + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + closeValue = -99999.9f; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] > closeValue ) + { + closeValue = pTri->m_Points[ptIndex][dir]; + } + } + + closeValue += extents[dir]; + + distStart = rayStart[dir] - closeValue; + distEnd = rayEnd[dir] - closeValue; + } + else + { + closeValue = 99999.9f; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] < closeValue ) + { + closeValue = pTri->m_Points[ptIndex][dir]; + } + } + + closeValue -= extents[dir]; + + distStart = -( rayStart[dir] - closeValue ); + distEnd = -( rayEnd[dir] - closeValue ); + } + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal.Init(); + plNormal[dir] = 1.0f; + *plDist = closeValue; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + } + + // + // check for an early out + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + { + if( *fraction == 1.0f ) + return false; + + return true; + } + + // + // handle 9 edge tests + // + Vector normal; + Vector edge; + float dist; + + // find the closest box point + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + boxPt[dir] = extents[dir]; + } + else + { + boxPt[dir] = -extents[dir]; + } + } + + // + // edge 0 + // + edge = pTri->m_Points[1] - pTri->m_Points[0]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // edge 1 + // + edge = pTri->m_Points[2] - pTri->m_Points[1]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // edge 2 + // + edge = pTri->m_Points[0] - pTri->m_Points[2]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.y * rayStart.y ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.y * rayEnd.y ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.z * rayStart.z ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.z * rayEnd.z ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + + // extents adjusted dist + dist = ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ); + + // find distances from plane (start, end) + distStart = ( normal.x * rayStart.x ) + ( normal.y * rayStart.y ) - dist; + distEnd = ( normal.x * rayEnd.x ) + ( normal.y * rayEnd.y ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // test face plane + // + dist = ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ); + + distStart = pTri->m_Normal.Dot( rayStart ) - dist; + distEnd = pTri->m_Normal.Dot( rayEnd ) - dist; + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + t = ( distStart - COLLISION_EPSILON ) / ( distStart - distEnd ); + if( t > *fraction ) + { + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayStart ); + *fraction = t; + plNormal = normal; + *plDist = dist; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + t = ( distStart + COLLISION_EPSILON ) / ( distStart - distEnd ); + VectorScale( rayDir, t, rayPt ); + VectorAdd( rayStart, rayPt, rayEnd ); + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + if( *fraction == 1.0f ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ) +{ + bool bResult = false; + + Vector normal; + float fraction, dist; + + // + // sweep ABB against all triangles in list + // + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( pTemp->m_ppTriList[i]->IsIntersect() ) + { + bResult = SweptAABBTriIntersect( pData->m_StartPos, pData->m_EndPos, pData->m_Extents, + pTemp->m_ppTriList[i], normal, &dist, &fraction ); + if( bResult ) + { + if( fraction < pData->m_Fraction ) + { + pData->m_Fraction = fraction; + pData->m_Normal = normal; + pData->m_Distance = dist; + } + } + } + } + + return bResult; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::IntersectAABBTriTest( Vector &rayStart, Vector &extents, + CDispCollTri const *pTri ) +{ + int dir, ptIndex; + float dist; + + // + // test axail planes (x, y, z) + // + + for( dir = 0; dir < 3; dir++ ) + { + // + // negative axial plane, component = dir + // + dist = rayStart[dir] - extents[dir]; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] > dist ) + break; + } + + if( ptIndex == 3 ) + return false; + + // + // positive axial plane, component = dir + // + dist = rayStart[dir] + extents[dir]; + for( ptIndex = 0; ptIndex < 3; ptIndex++ ) + { + if( pTri->m_Points[ptIndex][dir] < dist ) + break; + } + + if( ptIndex == 3 ) + return false; + } + + // + // add a test here to see if triangle face normal is close to axial -- done if so!!! + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + return true; + + // find the closest point on the box (use negated tri face noraml) + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( pTri->m_Normal[dir] < 0.0f ) + { + boxPt[dir] = extents[dir]; + } + else + { + boxPt[dir] = -extents[dir]; + } + } + + // + // triangle plane test + // + // do the opposite because the ray has been negated + if( ( ( pTri->m_Normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // + // test edge planes - 9 of them + // + Vector normal; + Vector edge; + + // + // edge 0 + // + edge = pTri->m_Points[1] - pTri->m_Points[0]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 1 + // + edge = pTri->m_Points[2] - pTri->m_Points[1]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 2 + // + edge = pTri->m_Points[0] - pTri->m_Points[2]; + + // cross x + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross y + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.z * ( boxPt.z - pTri->m_Points[0].z ) ) ) > 0.0f ) + return false; + + // cross z + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - pTri->m_Points[0].x ) ) + ( normal.y * ( boxPt.y - pTri->m_Points[0].y ) ) ) > 0.0f ) + return false; + + return true; +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri ) +{ + // get ray direction + Vector rayDir = rayEnd - rayStart; + + // + // quick and dirty test -- test to see if the object is traveling away from triangle surface??? + // + if( pTri->m_Normal.Dot( rayDir ) > 0.0f ) + return false; + + // + // calc the swept triangle face (negate the ray -- opposite direction of box travel) + // + rayDir.Negate(); + + Vector points[3]; + points[0] = pTri->m_Points[0] + rayDir; + points[1] = pTri->m_Points[1] + rayDir; + points[2] = pTri->m_Points[2] + rayDir; + + // + // handle 4 faces tests (3 axial planes and triangle face) + // + int dir; + float dist; + + // + // axial planes tests (x, y, z) + // + for( dir = 0; dir < 3; dir++ ) + { + bool bOutside = true; + + if( rayDir[dir] < 0.0f ) + { + dist = rayStart[dir] - extents[dir]; + for( int ptIndex = 0; ptIndex < 3; ptIndex ) + { + if( points[ptIndex][dir] > dist ) + { + bOutside = false; + break; + } + } + } + else + { + dist = rayStart[dir] + extents[dir]; + for( int ptIndex = 0; ptIndex < 3; ptIndex ) + { + if( pTri->m_Points[ptIndex][dir] < dist ) + { + bOutside = false; + break; + } + } + } + + if( bOutside ) + return false; + } + + // + // add a test here to see if triangle face normal is close to axial -- done if so!!! + // + if( ( pTri->m_Normal[0] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[1] > ONE_MINUS_COLLISION_EPSILON ) || + ( pTri->m_Normal[2] > ONE_MINUS_COLLISION_EPSILON ) ) + return true; + + // + // handle 9 edge tests - always use the newly swept face for this + // + Vector normal; + Vector edge; + + // find the closest box point - (is written opposite to normal due to negating ray) + Vector boxPt( 0.0f, 0.0f, 0.0f ); + for( dir = 0; dir < 3; dir++ ) + { + if( rayDir[dir] < 0.0f ) + { + boxPt[dir] = rayStart[dir] - extents[dir]; + } + else + { + boxPt[dir] = rayStart[dir] + extents[dir]; + } + } + + // + // edge 0 + // + edge = points[1] - points[0]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 1 + // + edge = points[2] - points[1]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // edge 2 + // + edge = points[0] - points[2]; + + // cross x-edge + normal.x = 0.0f; + normal.y = -edge.z; + normal.z = edge.y; + if( ( ( normal.y * ( boxPt.y - points[0].y ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross, y-edge + normal.x = edge.z; + normal.y = 0.0f; + normal.z = edge.y; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + // cross z-edge + normal.x = -edge.y; + normal.y = edge.x; + normal.z = 0.0f; + if( ( ( normal.x * ( boxPt.x - points[0].x ) ) + ( normal.y * ( boxPt.y - points[0].y ) ) ) > 0.0f ) + return false; + + // + // triangle plane test + // + // do the opposite because the ray has been negated + if( ( ( pTri->m_Normal.x * ( boxPt.x - points[0].x ) ) + + ( pTri->m_Normal.y * ( boxPt.y - points[0].y ) ) + + ( pTri->m_Normal.z * ( boxPt.z - points[0].z ) ) ) > 0.0f ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ) +{ + // + // intersect AABB with all triangles in list + // + if( bIntersect ) + { + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( IntersectAABBTriTest( rayStart, extents, pTemp->m_ppTriList[i] ) ) + return true; + } + + return false; + } + // + // sweep AABB against all triangles in list + // + else + { + bool bResult = false; + + for( int i = 0; i < pTemp->m_TriListCount; i++ ) + { + if( SweptAABBTriTest( rayStart, rayEnd, extents, pTemp->m_ppTriList[i] ) ) + { + pTemp->m_ppTriList[i]->SetIntersect( true ); + bResult = true; + } + } + + return bResult; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ) +{ + float dist; + + for( int dir = 0; dir < 3; dir++ ) + { + // negative direction + dist = -( pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + if( dist > COLLISION_EPSILON ) + return false; + + // positive direction + dist = pos[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + if( dist > COLLISION_EPSILON ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ) +{ + int dir; + float distStart, distEnd; + float fraction; + float deltas[3]; + float scalers[3]; + + // + // enter and exit fractions + // + float enterFraction = 0.0f; + float exitFraction = 0.0f; + + // + // de-normalize the paramter space so that we don't have to divide + // to find the fractional amount later (clamped for precision) + // + deltas[0] = rayEnd.x - rayStart.x; + deltas[1] = rayEnd.y - rayStart.y; + deltas[2] = rayEnd.z - rayStart.z; + if( ( deltas[0] < COLLISION_EPSILON ) && ( deltas[0] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + if( ( deltas[1] < COLLISION_EPSILON ) && ( deltas[1] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + if( ( deltas[2] < COLLISION_EPSILON ) && ( deltas[2] > -COLLISION_EPSILON ) ) { deltas[0] = 1.0f; } + scalers[0] = deltas[1] * deltas[2]; + scalers[1] = deltas[0] * deltas[2]; + scalers[2] = deltas[0] * deltas[1]; + + for( dir = 0; dir < 3; dir++ ) + { + // + // negative direction + // + distStart = -( rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + distEnd = -( rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)] - extents[dir] ) ); + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction > enterFraction ) + { + enterFraction = fraction; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction < exitFraction ) + { + exitFraction = fraction; + } + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + + // + // positive direction + // + distStart = rayStart[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + distEnd = rayEnd[dir] - ( pTemp->m_AABBDistances[(dir>>1)+1] + extents[dir] ); + + if( ( distStart > COLLISION_EPSILON ) && ( distEnd < -COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction > enterFraction ) + { + enterFraction = fraction; + } + } + else if( ( distStart < -COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + fraction = distStart * scalers[dir]; + if( fraction < exitFraction ) + { + exitFraction = fraction; + } + } + else if( ( distStart > COLLISION_EPSILON ) && ( distEnd > COLLISION_EPSILON ) ) + { + return false; + } + } + + if( exitFraction < enterFraction ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, + bool bIntersect ) +{ + // + // get the current nodes bounds and create collision test planes + // (saved in the in class cache m_AABBNormals, m_AABBDistances) + // + Vector bounds[2]; + CDispCollNode *pNode = &m_pNodes[nodeIndex]; + pNode->GetBounds( bounds[0], bounds[1] ); + CreatePlanesFromBounds( pTemp, bounds[0], bounds[1] ); + + // + // interesect/sweep test + // + bool bResult; + if( bIntersect ) + { + bResult = IntersectAABBAABBTest( pTemp, rayStart, extents ); + } + else + { + bResult = SweptAABBAABBTest( pTemp, rayStart, rayEnd, extents ); + } + + if( bResult ) + { + // if leaf node -- add triangles to interstection test list + if( pNode->IsLeaf() ) + { + // Assert for now -- flush cache later!!!!! + Assert( pTemp->m_TriListCount >= 0 ); + Assert( pTemp->m_TriListCount < TRILIST_CACHE_SIZE ); + + pTemp->m_ppTriList[pTemp->m_TriListCount] = &pNode->m_Tris[0]; + pTemp->m_ppTriList[pTemp->m_TriListCount+1] = &pNode->m_Tris[1]; + pTemp->m_TriListCount += 2; + } + // continue recursion + else + { + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 0 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 1 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 2 ), rayStart, rayEnd, extents, bIntersect ); + BuildTriList_r( pTemp, GetChildNode( nodeIndex, 3 ), rayStart, rayEnd, extents, bIntersect ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBSweep( CDispCollData *pData ) +{ + // reset the triangle lists counts + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // sweep the AABB against the tree + BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ); + + // find collision triangles + if( CullTriList( &tmp, pData->m_StartPos, pData->m_EndPos, pData->m_Extents, false ) ) + { + // find closest intersection + return AABBTriIntersect( &tmp, pData ); + } + + return false; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBIntersect( CDispCollData *pData ) +{ + // reset the triangle lists counts + CDispCollTreeTempData tmp; + tmp.m_TriListCount = 0; + + // sweep the AABB against the tree + BuildTriList_r( &tmp, 0, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); + + // find collision triangles + return CullTriList( &tmp, pData->m_StartPos, pData->m_StartPos, pData->m_Extents, true ); +} diff --git a/public/dispcoll.h b/public/dispcoll.h new file mode 100644 index 0000000..1816db4 --- /dev/null +++ b/public/dispcoll.h @@ -0,0 +1,249 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISPCOLL_H +#define DISPCOLL_H +#pragma once + +#include "mathlib/vector.h" + +class CCoreDispInfo; + +//============================================================================= +// +// Displacement Collision Triangle Data +// +class CDispCollTri +{ +public: + + void Init( void ); + inline void SetPoint( int index, Vector const& vert ); + inline void SetPointNormal( int index, Vector const& normal ); + void CalcPlane( void ); + + inline void SetIntersect( bool bIntersect ); + inline bool IsIntersect( void ); + + Vector m_Points[3]; // polygon points + Vector m_PointNormals[3]; // polygon point normals + Vector m_Normal; // plane normal + float m_Distance; // plane distance + short m_ProjAxes[2]; // projection axes (2 minor axes) + bool m_bIntersect; // intersected triangle??? +}; + +//============================================================================= +// +// Displacement Collision Node Data +// +class CDispCollNode +{ +public: + + CDispCollNode(); + inline bool IsLeaf( void ); + inline void SetBounds( Vector const &bMin, Vector const &bMax ); + inline void GetBounds( Vector &bMin, Vector &bMax ); + + Vector m_Bounds[2]; // node minimum and maximum + + bool m_bIsLeaf; // is the node a leaf? ( may have to make this an int for alignment!) + CDispCollTri m_Tris[2]; // two triangles contained in leaf node +}; + + +//============================================================================= +// +// Displacement Collision Data +// +class CDispCollData +{ +public: + + Vector m_StartPos; + Vector m_EndPos; + Vector m_Extents; + float m_Fraction; + int m_Contents; + Vector m_Normal; + float m_Distance; + bool m_bOcclude; +}; + + + +// HACKHACK: JAY: Moved this out of CDispCollTree to be thread safe in vrad +enum { TRILIST_CACHE_SIZE = 128 }; + +class CDispCollTreeTempData +{ +public: + // + // temps + // + int m_TriListCount; + CDispCollTri *m_ppTriList[TRILIST_CACHE_SIZE]; + + // collision tree node cache + float m_AABBDistances[6]; +}; + + +//============================================================================= +// +// Displacement Collision Tree +// +class CDispCollTree +{ +public: + + static const float COLLISION_EPSILON; + static const float ONE_MINUS_COLLISION_EPSILON; + + //========================================================================= + // + // Creation/Destruction + // + CDispCollTree(); + ~CDispCollTree(); + + virtual bool Create( CCoreDispInfo *pDisp ); + + //========================================================================= + // + // Collision Functions + // + bool RayTest( CDispCollData *pData ); + bool RayTestAllTris( CDispCollData *pData, int power ); + + bool AABBIntersect( CDispCollData *pData ); + bool AABBSweep( CDispCollData *pData ); + + //========================================================================= + // + // Attrib Functions + // + inline void SetPower( int power ); + inline int GetPower( void ); + + inline void SetCheckCount( int count ); + inline int GetCheckCount( void ); + + inline void GetBounds( Vector& boundMin, Vector& boundMax ); + +protected: + + int m_Power; + + int m_NodeCount; + CDispCollNode *m_pNodes; + + int m_CheckCount; + + // collision tree node cache + Vector m_AABBNormals[6]; + //========================================================================= + // + // Creation/Destruction + // + void InitAABBData( void ); + void InitLeaves( CCoreDispInfo *pDisp ); + void CreateNodes( CCoreDispInfo *pDisp ); + void CreateNodes_r( CCoreDispInfo *pDisp, int nodeIndex, int termLevel ); + void CalcBounds( CDispCollNode *pNode, int nodeIndex ); + + //========================================================================= + // + // Collision Functions + // + void CreatePlanesFromBounds( CDispCollTreeTempData *pTemp, Vector const &bbMin, Vector const &bbMax ); + +// void RayNodeTest_r( int nodeIndex, Vector &rayStart, Vector &rayEnd ); + void RayNodeTest_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector rayStart, Vector rayEnd ); + bool RayAABBTest( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd ); + bool RayTriListTest( CDispCollTreeTempData *pTemp, CDispCollData *pData ); + bool RayTriTest( Vector const &rayStart, Vector const &rayDir, float const rayLength, CDispCollTri const *pTri, float *fraction ); + + void BuildTriList_r( CDispCollTreeTempData *pTemp, int nodeIndex, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); + bool IntersectAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &pos, const Vector &extents ); + bool SweptAABBAABBTest( CDispCollTreeTempData *pTemp, const Vector &rayStart, const Vector &rayEnd, const Vector &extents ); + + bool CullTriList( CDispCollTreeTempData *pTemp, Vector &rayStart, Vector &rayEnd, Vector &extents, bool bIntersect ); + bool SweptAABBTriTest( Vector &rayStart, Vector &rayEnd, Vector &extents, CDispCollTri const *pTri ); + bool AABBTriIntersect( CDispCollTreeTempData *pTemp, CDispCollData *pData ); + bool IntersectAABBTriTest( Vector &rayStart, Vector &extents, CDispCollTri const *pTri ); + bool SweptAABBTriIntersect( Vector &rayStart, Vector &rayEnd, Vector &extents, + CDispCollTri const *pTri, Vector &plNormal, float *plDist, + float *fraction ); + + //========================================================================= + // + // Memory Functions + // + bool AllocNodes( int nodeCount ); + void FreeNodes( void ); + + //========================================================================= + // + // Utility Functions + // + inline int CalcNodeCount( int power ); + inline int GetParentNode( int nodeIndex ); + inline int GetChildNode( int nodeIndex, int direction ); + inline int GetNodeLevel( int nodeIndex ); + int GetNodeIndexFromComponents( int x, int y ); +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::SetPower( int power ) +{ + m_Power = power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetPower( void ) +{ + return m_Power; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::SetCheckCount( int count ) +{ + m_CheckCount = count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline int CDispCollTree::GetCheckCount( void ) +{ + return m_CheckCount; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void CDispCollTree::GetBounds( Vector& boundMin, Vector& boundMax ) +{ + boundMin[0] = m_pNodes[0].m_Bounds[0].x; + boundMin[1] = m_pNodes[0].m_Bounds[0].y; + boundMin[2] = m_pNodes[0].m_Bounds[0].z; + + boundMax[0] = m_pNodes[0].m_Bounds[1].x; + boundMax[1] = m_pNodes[0].m_Bounds[1].y; + boundMax[2] = m_pNodes[0].m_Bounds[1].z; +} + + +#endif // DISPCOLL_H \ No newline at end of file diff --git a/public/dispcoll_common.cpp b/public/dispcoll_common.cpp new file mode 100644 index 0000000..85d385a --- /dev/null +++ b/public/dispcoll_common.cpp @@ -0,0 +1,1540 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cmodel.h" +#include "dispcoll_common.h" +#include "collisionutils.h" +#include "tier1/strtools.h" +#include "tier0/vprof.h" +#include "tier1/fmtstr.h" +#include "tier1/utlhash.h" +#include "tier1/generichash.h" +#include "tier0/fasttimer.h" +#include "vphysics/virtualmesh.h" +#include "tier1/datamanager.h" +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//============================================================================= +// Cache + +#ifdef ENGINE_DLL +CDataManager g_DispCollTriCache( 2048*1024 ); +#endif + + +struct DispCollPlaneIndex_t +{ + Vector vecPlane; + int index; +}; + +class CPlaneIndexHashFuncs +{ +public: + CPlaneIndexHashFuncs( int ) {} + + // Compare + bool operator()( const DispCollPlaneIndex_t &lhs, const DispCollPlaneIndex_t &rhs ) const + { + return ( lhs.vecPlane == rhs.vecPlane || lhs.vecPlane == -rhs.vecPlane ); + } + + // Hash + unsigned int operator()( const DispCollPlaneIndex_t &item ) const + { + return HashItem( item.vecPlane ) ^ HashItem( -item.vecPlane ); + } +}; + +CUtlHash g_DispCollPlaneIndexHash( 512 ); + + +//============================================================================= +// Displacement Collision Triangle + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CDispCollTri::CDispCollTri() +{ + Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTri::Init( void ) +{ + m_vecNormal.Init(); + m_flDist = 0.0f; + m_TriData[0].m_IndexDummy = m_TriData[1].m_IndexDummy = m_TriData[2].m_IndexDummy = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTri::CalcPlane( CDispVector &m_aVerts ) +{ + Vector vecEdges[2]; + vecEdges[0] = m_aVerts[GetVert( 1 )] - m_aVerts[GetVert( 0 )]; + vecEdges[1] = m_aVerts[GetVert( 2 )] - m_aVerts[GetVert( 0 )]; + + m_vecNormal = vecEdges[1].Cross( vecEdges[0] ); + VectorNormalize( m_vecNormal ); + m_flDist = m_vecNormal.Dot( m_aVerts[GetVert( 0 )] ); + + // Calculate the signbits for the plane - fast test. + m_ucSignBits = 0; + m_ucPlaneType = PLANE_ANYZ; + for ( int iAxis = 0; iAxis < 3 ; ++iAxis ) + { + if ( m_vecNormal[iAxis] < 0.0f ) + { + m_ucSignBits |= 1 << iAxis; + } + + if ( m_vecNormal[iAxis] == 1.0f ) + { + m_ucPlaneType = iAxis; + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void FindMin( float v1, float v2, float v3, int &iMin ) +{ + float flMin = v1; + iMin = 0; + if( v2 < flMin ) { flMin = v2; iMin = 1; } + if( v3 < flMin ) { flMin = v3; iMin = 2; } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +inline void FindMax( float v1, float v2, float v3, int &iMax ) +{ + float flMax = v1; + iMax = 0; + if( v2 > flMax ) { flMax = v2; iMax = 1; } + if( v3 > flMax ) { flMax = v3; iMax = 2; } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTri::FindMinMax( CDispVector &m_aVerts ) +{ + int iMin, iMax; + FindMin( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMin ); + FindMax( m_aVerts[GetVert(0)].x, m_aVerts[GetVert(1)].x, m_aVerts[GetVert(2)].x, iMax ); + SetMin( 0, iMin ); + SetMax( 0, iMax ); + + FindMin( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMin ); + FindMax( m_aVerts[GetVert(0)].y, m_aVerts[GetVert(1)].y, m_aVerts[GetVert(2)].y, iMax ); + SetMin( 1, iMin ); + SetMax( 1, iMax ); + + FindMin( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMin ); + FindMax( m_aVerts[GetVert(0)].z, m_aVerts[GetVert(1)].z, m_aVerts[GetVert(2)].z, iMax ); + SetMin( 2, iMin ); + SetMax( 2, iMax ); +} + + +// SIMD Routines for intersecting with the quad tree +FORCEINLINE int IntersectRayWithFourBoxes( const FourVectors &rayStart, const FourVectors &invDelta, const FourVectors &rayExtents, const FourVectors &boxMins, const FourVectors &boxMaxs ) +{ + // SIMD Test ray against all four boxes at once + // each node stores the bboxes of its four children + FourVectors hitMins = boxMins; + hitMins -= rayStart; + FourVectors hitMaxs = boxMaxs; + hitMaxs -= rayStart; + + // adjust for swept box by enlarging the child bounds to shrink the sweep down to a point + hitMins -= rayExtents; + hitMaxs += rayExtents; + + // compute the parametric distance along the ray of intersection in each dimension + hitMins *= invDelta; + hitMaxs *= invDelta; + + // Find the exit parametric intersection distance in each dimesion, for each box + FourVectors exitT = maximum(hitMins,hitMaxs); + // Find the entry parametric intersection distance in each dimesion, for each box + FourVectors entryT = minimum(hitMins,hitMaxs); + + // now find the max overall entry distance across all dimensions for each box + fltx4 minTemp = MaxSIMD(entryT.x, entryT.y); + fltx4 boxEntryT = MaxSIMD(minTemp, entryT.z); + + // now find the min overall exit distance across all dimensions for each box + fltx4 maxTemp = MinSIMD(exitT.x, exitT.y); + fltx4 boxExitT = MinSIMD(maxTemp, exitT.z); + + boxEntryT = MaxSIMD(boxEntryT,Four_Zeros); + boxExitT = MinSIMD(boxExitT,Four_Ones); + + // if entry<=exit for the box, we've got a hit + fltx4 active = CmpLeSIMD(boxEntryT,boxExitT); // mask of which boxes are active + + // hit at least one box? + return TestSignSIMD(active); +} + +// This does 4 simultaneous box intersections +// NOTE: This can be used as a 1 vs 4 test by replicating a single box into the one side +FORCEINLINE int IntersectFourBoxPairs( const FourVectors &mins0, const FourVectors &maxs0, const FourVectors &mins1, const FourVectors &maxs1 ) +{ + // find the max mins and min maxs in each dimension + FourVectors intersectMins = maximum(mins0,mins1); + FourVectors intersectMaxs = minimum(maxs0,maxs1); + + // if intersectMins <= intersectMaxs then the boxes overlap in this dimension + fltx4 overlapX = CmpLeSIMD(intersectMins.x,intersectMaxs.x); + fltx4 overlapY = CmpLeSIMD(intersectMins.y,intersectMaxs.y); + fltx4 overlapZ = CmpLeSIMD(intersectMins.z,intersectMaxs.z); + + // if the boxes overlap in all three dimensions, they intersect + fltx4 tmp = AndSIMD( overlapX, overlapY ); + fltx4 active = AndSIMD( tmp, overlapZ ); + + // hit at least one box? + return TestSignSIMD(active); +} + +// This does 4 simultaneous box vs. sphere intersections +// NOTE: This can be used as a 1 vs 4 test by replicating a single sphere/box into one side +FORCEINLINE int IntersectFourBoxSpherePairs( const FourVectors ¢er, const fltx4 &radiusSq, const FourVectors &mins, const FourVectors &maxs ) +{ + // for each dimension of each box, compute the clamped distance from the mins side to the center (must be >= 0) + FourVectors minDist = mins; + minDist -= center; + FourVectors dist; + dist.x = MaxSIMD(Four_Zeros, minDist.x); + dist.y = MaxSIMD(Four_Zeros, minDist.y); + dist.z = MaxSIMD(Four_Zeros, minDist.z); + + // now compute the distance from the maxs side to the center + FourVectors maxDist = center; + maxDist -= maxs; + // NOTE: Don't need to clamp here because we clamp against the minDist which must be >= 0, so the two clamps + // get folded together + FourVectors totalDist; + totalDist.x = MaxSIMD(dist.x, maxDist.x); + totalDist.y = MaxSIMD(dist.y, maxDist.y); + totalDist.z = MaxSIMD(dist.z, maxDist.z); + // get the total squred distance between each box & sphere center by summing the squares of each + // component/dimension + fltx4 distSq = totalDist * totalDist; + + // if squared distance between each sphere center & box is less than the radiusSquared for that sphere + // we have an intersection + fltx4 active = CmpLeSIMD(distSq,radiusSq); + + // at least one intersection? + return TestSignSIMD(active); +} + + +int FORCEINLINE CDispCollTree::BuildRayLeafList( int iNode, rayleaflist_t &list ) +{ + list.nodeList[0] = iNode; + int listIndex = 0; + list.maxIndex = 0; + while ( listIndex <= list.maxIndex ) + { + iNode = list.nodeList[listIndex]; + // the rest are all leaves + if ( IsLeafNode(iNode) ) + return listIndex; + listIndex++; + const CDispCollNode &node = m_nodes[iNode]; + int mask = IntersectRayWithFourBoxes( list.rayStart, list.invDelta, list.rayExtents, node.m_mins, node.m_maxs ); + if ( mask ) + { + int child = Nodes_GetChild( iNode, 0 ); + if ( mask & 1 ) + { + ++list.maxIndex; + list.nodeList[list.maxIndex] = child; + } + if ( mask & 2 ) + { + ++list.maxIndex; + list.nodeList[list.maxIndex] = child+1; + } + if ( mask & 4 ) + { + ++list.maxIndex; + list.nodeList[list.maxIndex] = child+2; + } + if ( mask & 8 ) + { + ++list.maxIndex; + list.nodeList[list.maxIndex] = child+3; + } + Assert(list.maxIndex < MAX_AABB_LIST); + } + } + + return listIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: Create the AABB tree. +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTree_Create( CCoreDispInfo *pDisp ) +{ + // Copy the flags. + m_nFlags = pDisp->GetSurface()->GetFlags(); + + // Copy necessary displacement data. + AABBTree_CopyDispData( pDisp ); + + // Setup/create the leaf nodes first so the recusion can use this data to stop. + AABBTree_CreateLeafs(); + + // Create the bounding box of the displacement surface + the base face. + AABBTree_CalcBounds(); + + // Successful. + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::AABBTree_CopyDispData( CCoreDispInfo *pDisp ) +{ + // Displacement size. + m_nPower = pDisp->GetPower(); + + // Displacement base surface data. + CCoreDispSurface *pSurf = pDisp->GetSurface(); + m_nContents = pSurf->GetContents(); + pSurf->GetNormal( m_vecStabDir ); + for ( int iPoint = 0; iPoint < 4; iPoint++ ) + { + pSurf->GetPoint( iPoint, m_vecSurfPoints[iPoint] ); + } + + // Allocate collision tree data. + { + MEM_ALLOC_CREDIT(); + m_aVerts.SetSize( GetSize() ); + } + + { + MEM_ALLOC_CREDIT(); + m_aTris.SetSize( GetTriSize() ); + } + + { + MEM_ALLOC_CREDIT(); + int numLeaves = (GetWidth()-1) * (GetHeight()-1); + m_leaves.SetCount(numLeaves); + int numNodes = Nodes_CalcCount( m_nPower ); + numNodes -= numLeaves; + m_nodes.SetCount(numNodes); + } + + + // Setup size. + m_nSize = sizeof( this ); + m_nSize += sizeof( Vector ) * GetSize(); + m_nSize += sizeof( CDispCollTri ) * GetTriSize(); +#if OLD_DISP_AABB + m_nSize += sizeof( CDispCollAABBNode ) * Nodes_CalcCount( m_nPower ); +#endif + m_nSize += sizeof(m_nodes[0]) * m_nodes.Count(); + m_nSize += sizeof(m_leaves[0]) * m_leaves.Count(); + m_nSize += sizeof( CDispCollTri* ) * DISPCOLL_TREETRI_SIZE; + + // Copy vertex data. + for ( int iVert = 0; iVert < m_aVerts.Count(); iVert++ ) + { + pDisp->GetVert( iVert, m_aVerts[iVert] ); + } + + // Copy and setup triangle data. + unsigned short iVerts[3]; + for ( int iTri = 0; iTri < m_aTris.Count(); ++iTri ) + { + pDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] ); + m_aTris[iTri].SetVert( 0, iVerts[0] ); + m_aTris[iTri].SetVert( 1, iVerts[1] ); + m_aTris[iTri].SetVert( 2, iVerts[2] ); + m_aTris[iTri].m_uiFlags = pDisp->GetTriTagValue( iTri ); + + // Calculate the surface props and set flags. + float flTotalAlpha = 0.0f; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + flTotalAlpha += pDisp->GetAlpha( m_aTris[iTri].GetVert( iVert ) ); + } + + if ( flTotalAlpha > DISP_ALPHA_PROP_DELTA ) + { + m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP2; + } + else + { + m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFPROP1; + } + + // Add the displacement surface flag. + m_aTris[iTri].m_uiFlags |= DISPSURF_FLAG_SURFACE; + + // Calculate the plane normal and the min max. + m_aTris[iTri].CalcPlane( m_aVerts ); + m_aTris[iTri].FindMinMax( m_aVerts ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::AABBTree_CreateLeafs( void ) +{ + int numLeaves = (GetWidth()-1) * (GetHeight()-1); + m_leaves.SetCount(numLeaves); + int numNodes = Nodes_CalcCount( m_nPower ); + numNodes -= numLeaves; + m_nodes.SetCount(numNodes); + + // Get the width and height of the displacement. + int nWidth = GetWidth() - 1; + int nHeight = GetHeight() - 1; + + for ( int iHgt = 0; iHgt < nHeight; ++iHgt ) + { + for ( int iWid = 0; iWid < nWidth; ++iWid ) + { + int iLeaf = Nodes_GetIndexFromComponents( iWid, iHgt ); + int iIndex = iHgt * nWidth + iWid; + int iTri = iIndex * 2; + + m_leaves[iLeaf].m_tris[0] = iTri; + m_leaves[iLeaf].m_tris[1] = iTri + 1; + } + } +} + +void CDispCollTree::AABBTree_GenerateBoxes_r( int nodeIndex, Vector *pMins, Vector *pMaxs ) +{ + // leaf + ClearBounds( *pMins, *pMaxs ); + if ( nodeIndex >= m_nodes.Count() ) + { + int iLeaf = nodeIndex - m_nodes.Count(); + + for ( int iTri = 0; iTri < 2; ++iTri ) + { + int triIndex = m_leaves[iLeaf].m_tris[iTri]; + const CDispCollTri &tri = m_aTris[triIndex]; + AddPointToBounds( m_aVerts[tri.GetVert( 0 )], *pMins, *pMaxs ); + AddPointToBounds( m_aVerts[tri.GetVert( 1 )], *pMins, *pMaxs ); + AddPointToBounds( m_aVerts[tri.GetVert( 2 )], *pMins, *pMaxs ); + } + } + else // node + { + Vector childMins[4], childMaxs[4]; + for ( int i = 0; i < 4; i++ ) + { + int child = Nodes_GetChild( nodeIndex, i ); + AABBTree_GenerateBoxes_r( child, &childMins[i], &childMaxs[i] ); + AddPointToBounds( childMins[i], *pMins, *pMaxs ); + AddPointToBounds( childMaxs[i], *pMins, *pMaxs ); + } + m_nodes[nodeIndex].m_mins.LoadAndSwizzle( childMins[0], childMins[1], childMins[2], childMins[3] ); + m_nodes[nodeIndex].m_maxs.LoadAndSwizzle( childMaxs[0], childMaxs[1], childMaxs[2], childMaxs[3] ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::AABBTree_CalcBounds( void ) +{ + // Check data. + if ( ( m_aVerts.Count() == 0 ) || ( m_nodes.Count() == 0 ) ) + return; + + AABBTree_GenerateBoxes_r( 0, &m_mins, &m_maxs ); + +#if INCLUDE_SURFACE_IN_BOUNDS + // Add surface points to bounds. + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + VectorMin( m_vecSurfPoints[iPoint], m_mins, m_mins ); + VectorMax( m_vecSurfPoints[iPoint], m_maxs, m_maxs ); + } +#endif + + // Bloat a little. + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + m_mins[iAxis] -= 1.0f; + m_maxs[iAxis] += 1.0f; + } +} + +static CThreadFastMutex s_CacheMutex; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTree::LockCache() +{ +#ifdef ENGINE_DLL + if ( !g_DispCollTriCache.LockResource( m_hCache ) ) + { + AUTO_LOCK_FM( s_CacheMutex ); + + // Cache may have just been created, so check once more + if ( !g_DispCollTriCache.LockResource( m_hCache ) ) + { + Cache(); + m_hCache = g_DispCollTriCache.CreateResource( this ); + g_DispCollTriCache.LockResource( m_hCache ); + //Msg( "Adding 0x%x to cache (actual %d) [%d, %d --> %.2f] %d total, %d unique\n", this, GetCacheMemorySize(), GetTriSize(), m_aEdgePlanes.Count(), (float)m_aEdgePlanes.Count()/(float)GetTriSize(), totals, uniques ); + } + } +#else + Cache(); +#endif +} + +inline void CDispCollTree::UnlockCache() +{ +#ifdef ENGINE_DLL + g_DispCollTriCache.UnlockResource( m_hCache ); +#endif +} +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::Cache( void ) +{ + if ( m_aTrisCache.Count() == GetTriSize() ) + { + return; + } + + VPROF( "CDispCollTree::Cache" ); + + // Alloc. +// int nSize = sizeof( CDispCollTriCache ) * GetTriSize(); + int nTriCount = GetTriSize(); + { + MEM_ALLOC_CREDIT(); + m_aTrisCache.SetSize( nTriCount ); + } + + for ( int iTri = 0; iTri < nTriCount; ++iTri ) + { + Cache_Create( &m_aTris[iTri], iTri ); + } + + g_DispCollPlaneIndexHash.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, RayDispOutput_t &output ) +{ + if ( IsBoxIntersectingRay( m_mins, m_maxs, ray.m_Start, ray.m_Delta, DISPCOLL_DIST_EPSILON ) ) + { + return AABBTree_Ray( ray, ray.InvDelta(), output ); + } + return false; +} + +bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, RayDispOutput_t &output ) +{ + VPROF( "DispRayTest" ); + + // Check for ray test. + if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) ) + return false; + + // Check for opacity. + if ( !( m_nContents & MASK_OPAQUE ) ) + return false; + + // Pre-calc the inverse delta for perf. + CDispCollTri *pImpactTri = NULL; + + AABBTree_TreeTrisRayBarycentricTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, output, &pImpactTri ); + + if ( pImpactTri ) + { + // Collision. + output.ndxVerts[0] = pImpactTri->GetVert( 0 ); + output.ndxVerts[1] = pImpactTri->GetVert( 2 ); + output.ndxVerts[2] = pImpactTri->GetVert( 1 ); + + Assert( (output.u <= 1.0f ) && ( output.v <= 1.0f ) ); + Assert( (output.u >= 0.0f ) && ( output.v >= 0.0f ) ); + + return true; + } + + // No collision. + return false; +} + +void CDispCollTree::AABBTree_TreeTrisRayBarycentricTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, RayDispOutput_t &output, CDispCollTri **pImpactTri ) +{ + rayleaflist_t list; + // NOTE: This part is loop invariant - should be hoisted up as far as possible + list.invDelta.DuplicateVector(vecInvDelta); + list.rayStart.DuplicateVector(ray.m_Start); + Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); + list.rayExtents.DuplicateVector(ext); + int listIndex = BuildRayLeafList( iNode, list ); + + float flU, flV, flT; + for ( ; listIndex <= list.maxIndex; listIndex++ ) + { + int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); + CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; + CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; + + if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], flU, flV, &flT ) ) + { + // Make sure it's inside the range + if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) ) + { + if( ( flT > 0.0f ) && ( flT < output.dist ) ) + { + (*pImpactTri) = pTri0; + output.u = flU; + output.v = flV; + output.dist = flT; + } + } + } + if ( ComputeIntersectionBarycentricCoordinates( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], flU, flV, &flT ) ) + { + // Make sure it's inside the range + if ( ( flU >= 0.0f ) && ( flV >= 0.0f ) && ( ( flU + flV ) <= 1.0f ) ) + { + if( ( flT > 0.0f ) && ( flT < output.dist ) ) + { + (*pImpactTri) = pTri1; + output.u = flU; + output.v = flV; + output.dist = flT; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTree_Ray( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace, bool bSide ) +{ + VPROF("AABBTree_Ray"); + +// VPROF_BUDGET( "DispRayTraces", VPROF_BUDGETGROUP_DISP_RAYTRACES ); + + // Check for ray test. + if ( CheckFlags( CCoreDispInfo::SURF_NORAY_COLL ) ) + return false; + + // Check for opacity. + if ( !( m_nContents & MASK_OPAQUE ) ) + return false; + + // Pre-calc the inverse delta for perf. + CDispCollTri *pImpactTri = NULL; + + AABBTree_TreeTrisRayTest( ray, vecInvDelta, DISPCOLL_ROOTNODE_INDEX, pTrace, bSide, &pImpactTri ); + + if ( pImpactTri ) + { + // Collision. + VectorCopy( pImpactTri->m_vecNormal, pTrace->plane.normal ); + pTrace->plane.dist = pImpactTri->m_flDist; + pTrace->dispFlags = pImpactTri->m_uiFlags; + return true; + } + + // No collision. + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::AABBTree_TreeTrisRayTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, CBaseTrace *pTrace, bool bSide, CDispCollTri **pImpactTri ) +{ + rayleaflist_t list; + // NOTE: This part is loop invariant - should be hoisted up as far as possible + list.invDelta.DuplicateVector(vecInvDelta); + list.rayStart.DuplicateVector(ray.m_Start); + Vector ext = ray.m_Extents + Vector(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); + list.rayExtents.DuplicateVector(ext); + int listIndex = BuildRayLeafList( iNode, list ); + + for ( ;listIndex <= list.maxIndex; listIndex++ ) + { + int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); + CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; + CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; + float flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri0->GetVert( 0 )], m_aVerts[pTri0->GetVert( 2 )], m_aVerts[pTri0->GetVert( 1 )], bSide ); + if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) ) + { + pTrace->fraction = flFrac; + (*pImpactTri) = pTri0; + } + + flFrac = IntersectRayWithTriangle( ray, m_aVerts[pTri1->GetVert( 0 )], m_aVerts[pTri1->GetVert( 2 )], m_aVerts[pTri1->GetVert( 1 )], bSide ); + if( ( flFrac >= 0.0f ) && ( flFrac < pTrace->fraction ) ) + { + pTrace->fraction = flFrac; + (*pImpactTri) = pTri1; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CDispCollTree::AABBTree_GetTrisInSphere( const Vector ¢er, float radius, unsigned short *pIndexOut, int indexMax ) +{ + return AABBTree_BuildTreeTrisInSphere_r( center, radius, DISPCOLL_ROOTNODE_INDEX, pIndexOut, indexMax ); +} + +int CDispCollTree::AABBTree_BuildTreeTrisInSphere_r( const Vector ¢er, float radius, int iNode, unsigned short *pIndexOut, unsigned short indexMax ) +{ + int nodeList[MAX_AABB_LIST]; + nodeList[0] = iNode; + int nTriCount = 0; + int listIndex = 0; + int maxIndex = 0; + // NOTE: This part is loop invariant - should be hoisted up as far as possible + FourVectors sphereCenters; + sphereCenters.DuplicateVector(center); + float radiusSq = radius * radius; + fltx4 sphereRadSq = ReplicateX4(radiusSq); + while ( listIndex <= maxIndex ) + { + iNode = nodeList[listIndex]; + listIndex++; + // the rest are all leaves + if ( IsLeafNode(iNode) ) + { + VPROF("Tris"); + for ( --listIndex; listIndex <= maxIndex; listIndex++ ) + { + if ( (nTriCount+2) <= indexMax ) + { + int leafIndex = nodeList[listIndex] - m_nodes.Count(); + pIndexOut[nTriCount] = m_leaves[leafIndex].m_tris[0]; + pIndexOut[nTriCount+1] = m_leaves[leafIndex].m_tris[1]; + nTriCount += 2; + } + } + break; + } + else + { + const CDispCollNode &node = m_nodes[iNode]; + int mask = IntersectFourBoxSpherePairs( sphereCenters, sphereRadSq, node.m_mins, node.m_maxs ); + if ( mask ) + { + int child = Nodes_GetChild( iNode, 0 ); + if ( mask & 1 ) + { + ++maxIndex; + nodeList[maxIndex] = child; + } + if ( mask & 2 ) + { + ++maxIndex; + nodeList[maxIndex] = child+1; + } + if ( mask & 4 ) + { + ++maxIndex; + nodeList[maxIndex] = child+2; + } + if ( mask & 8 ) + { + ++maxIndex; + nodeList[maxIndex] = child+3; + } + Assert(maxIndex < MAX_AABB_LIST); + } + } + } + return nTriCount; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTree_IntersectAABB( const Vector &absMins, const Vector &absMaxs ) +{ + // Check for hull test. + if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) ) + return false; + + cplane_t plane; + Vector center = 0.5f *(absMins + absMaxs); + Vector extents = absMaxs - center; + + int nodeList[MAX_AABB_LIST]; + nodeList[0] = 0; + int listIndex = 0; + int maxIndex = 0; + + // NOTE: This part is loop invariant - should be hoisted up as far as possible + FourVectors mins0; + mins0.DuplicateVector(absMins); + FourVectors maxs0; + maxs0.DuplicateVector(absMaxs); + FourVectors rayExtents; + while ( listIndex <= maxIndex ) + { + int iNode = nodeList[listIndex]; + listIndex++; + // the rest are all leaves + if ( IsLeafNode(iNode) ) + { + VPROF("Tris"); + for ( --listIndex; listIndex <= maxIndex; listIndex++ ) + { + int leafIndex = nodeList[listIndex] - m_nodes.Count(); + CDispCollTri *pTri0 = &m_aTris[m_leaves[leafIndex].m_tris[0]]; + CDispCollTri *pTri1 = &m_aTris[m_leaves[leafIndex].m_tris[1]]; + + VectorCopy( pTri0->m_vecNormal, plane.normal ); + plane.dist = pTri0->m_flDist; + plane.signbits = pTri0->m_ucSignBits; + plane.type = pTri0->m_ucPlaneType; + + if ( IsBoxIntersectingTriangle( center, extents, + m_aVerts[pTri0->GetVert( 0 )], + m_aVerts[pTri0->GetVert( 2 )], + m_aVerts[pTri0->GetVert( 1 )], + plane, 0.0f ) ) + return true; + VectorCopy( pTri1->m_vecNormal, plane.normal ); + plane.dist = pTri1->m_flDist; + plane.signbits = pTri1->m_ucSignBits; + plane.type = pTri1->m_ucPlaneType; + + if ( IsBoxIntersectingTriangle( center, extents, + m_aVerts[pTri1->GetVert( 0 )], + m_aVerts[pTri1->GetVert( 2 )], + m_aVerts[pTri1->GetVert( 1 )], + plane, 0.0f ) ) + return true; + } + break; + } + else + { + const CDispCollNode &node = m_nodes[iNode]; + int mask = IntersectFourBoxPairs( mins0, maxs0, node.m_mins, node.m_maxs ); + if ( mask ) + { + int child = Nodes_GetChild( iNode, 0 ); + if ( mask & 1 ) + { + ++maxIndex; + nodeList[maxIndex] = child; + } + if ( mask & 2 ) + { + ++maxIndex; + nodeList[maxIndex] = child+1; + } + if ( mask & 4 ) + { + ++maxIndex; + nodeList[maxIndex] = child+2; + } + if ( mask & 8 ) + { + ++maxIndex; + nodeList[maxIndex] = child+3; + } + Assert(maxIndex < MAX_AABB_LIST); + } + } + } + + // no collision + return false; +} + +static const Vector g_Vec3DispCollEpsilons(DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON,DISPCOLL_DIST_EPSILON); +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::AABBTree_SweepAABB( const Ray_t &ray, const Vector &vecInvDelta, CBaseTrace *pTrace ) +{ + VPROF( "Disp_AABBTree_SweepAABB" ); + // VPROF_BUDGET( "DispHullTraces", VPROF_BUDGETGROUP_DISP_HULLTRACES ); + // Check for hull test. + if ( CheckFlags( CCoreDispInfo::SURF_NOHULL_COLL ) ) + return false; + + // Test ray against the triangles in the list. + Vector rayDir; + VectorCopy( ray.m_Delta, rayDir ); + VectorNormalize( rayDir ); + // Save fraction. + float flFrac = pTrace->fraction; + + rayleaflist_t list; + // NOTE: This part is loop invariant - should be hoisted up as far as possible + list.invDelta.DuplicateVector(vecInvDelta); + list.rayStart.DuplicateVector(ray.m_Start); + Vector ext = ray.m_Extents + g_Vec3DispCollEpsilons; + list.rayExtents.DuplicateVector(ext); + int listIndex = BuildRayLeafList( 0, list ); + + if ( listIndex <= list.maxIndex ) + { + VPROF( "DispHullTest_Tris" ); + LockCache(); + for ( ; listIndex <= list.maxIndex; listIndex++ ) + { + int leafIndex = list.nodeList[listIndex] - m_nodes.Count(); + int iTri0 = m_leaves[leafIndex].m_tris[0]; + int iTri1 = m_leaves[leafIndex].m_tris[1]; + CDispCollTri *pTri0 = &m_aTris[iTri0]; + CDispCollTri *pTri1 = &m_aTris[iTri1]; + + SweepAABBTriIntersect( ray, rayDir, iTri0, pTri0, pTrace ); + SweepAABBTriIntersect( ray, rayDir, iTri1, pTri1, pTrace ); + } + UnlockCache(); + } + + // Collision. + if ( pTrace->fraction < flFrac ) + return true; + + // No collision. + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CDispCollTree::ResolveRayPlaneIntersect( float flStart, float flEnd, const Vector &vecNormal, float flDist, CDispCollHelper *pHelper ) +{ + if( ( flStart > 0.0f ) && ( flEnd > 0.0f ) ) + return false; + + if( ( flStart < 0.0f ) && ( flEnd < 0.0f ) ) + return true; + + float flDenom = flStart - flEnd; + bool bDenomIsZero = ( flDenom == 0.0f ); + if( ( flStart >= 0.0f ) && ( flEnd <= 0.0f ) ) + { + // Find t - the parametric distance along the trace line. + float t = ( !bDenomIsZero ) ? ( flStart - DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f; + if( t > pHelper->m_flStartFrac ) + { + pHelper->m_flStartFrac = t; + VectorCopy( vecNormal, pHelper->m_vecImpactNormal ); + pHelper->m_flImpactDist = flDist; + } + } + else + { + // Find t - the parametric distance along the trace line. + float t = ( !bDenomIsZero ) ? ( flStart + DISPCOLL_DIST_EPSILON ) / flDenom : 0.0f; + if( t < pHelper->m_flEndFrac ) + { + pHelper->m_flEndFrac = t; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTree::FacePlane( const Ray_t &ray, const Vector &rayDir, CDispCollTri *pTri, CDispCollHelper *pHelper ) +{ + // Calculate the closest point on box to plane (get extents in that direction). + Vector vecExtent; + CalcClosestExtents( pTri->m_vecNormal, ray.m_Extents, vecExtent ); + + float flExpandDist = pTri->m_flDist - pTri->m_vecNormal.Dot( vecExtent ); + + float flStart = pTri->m_vecNormal.Dot( ray.m_Start ) - flExpandDist; + float flEnd = pTri->m_vecNormal.Dot( ( ray.m_Start + ray.m_Delta ) ) - flExpandDist; + + return ResolveRayPlaneIntersect( flStart, flEnd, pTri->m_vecNormal, pTri->m_flDist, pHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool FORCEINLINE CDispCollTree::AxisPlanesXYZ( const Ray_t &ray, CDispCollTri *pTri, CDispCollHelper *pHelper ) +{ + static const TableVector g_ImpactNormalVecs[2][3] = + { + { + { -1, 0, 0 }, + { 0, -1, 0 }, + { 0, 0, -1 }, + }, + + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 }, + } + }; + + Vector vecImpactNormal; + float flDist, flExpDist, flStart, flEnd; + + int iAxis; + for ( iAxis = 2; iAxis >= 0; --iAxis ) + { + const float rayStart = ray.m_Start[iAxis]; + const float rayExtent = ray.m_Extents[iAxis]; + const float rayDelta = ray.m_Delta[iAxis]; + + // Min + flDist = m_aVerts[pTri->GetVert(pTri->GetMin(iAxis))][iAxis]; + flExpDist = flDist - rayExtent; + flStart = flExpDist - rayStart; + flEnd = flStart - rayDelta; + + if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[0][iAxis], flDist, pHelper ) ) + return false; + + // Max + flDist = m_aVerts[pTri->GetVert(pTri->GetMax(iAxis))][iAxis]; + flExpDist = flDist + rayExtent; + flStart = rayStart - flExpDist; + flEnd = flStart + rayDelta; + + if ( !ResolveRayPlaneIntersect( flStart, flEnd, g_ImpactNormalVecs[1][iAxis], flDist, pHelper ) ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Testing! +//----------------------------------------------------------------------------- +void CDispCollTree::Cache_Create( CDispCollTri *pTri, int iTri ) +{ + MEM_ALLOC_CREDIT(); + Vector *pVerts[3]; + pVerts[0] = &m_aVerts[pTri->GetVert( 0 )]; + pVerts[1] = &m_aVerts[pTri->GetVert( 1 )]; + pVerts[2] = &m_aVerts[pTri->GetVert( 2 )]; + + CDispCollTriCache *pCache = &m_aTrisCache[iTri]; + + Vector vecEdge; + + // Edge 1 + VectorSubtract( *pVerts[1], *pVerts[0], vecEdge ); + Cache_EdgeCrossAxisX( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossX[0] ); + Cache_EdgeCrossAxisY( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossY[0] ); + Cache_EdgeCrossAxisZ( vecEdge, *pVerts[0], *pVerts[2], pTri, pCache->m_iCrossZ[0] ); + + // Edge 2 + VectorSubtract( *pVerts[2], *pVerts[1], vecEdge ); + Cache_EdgeCrossAxisX( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossX[1] ); + Cache_EdgeCrossAxisY( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossY[1] ); + Cache_EdgeCrossAxisZ( vecEdge, *pVerts[1], *pVerts[0], pTri, pCache->m_iCrossZ[1] ); + + // Edge 3 + VectorSubtract( *pVerts[0], *pVerts[2], vecEdge ); + Cache_EdgeCrossAxisX( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossX[2] ); + Cache_EdgeCrossAxisY( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossY[2] ); + Cache_EdgeCrossAxisZ( vecEdge, *pVerts[2], *pVerts[1], pTri, pCache->m_iCrossZ[2] ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CDispCollTree::AddPlane( const Vector &vecNormal ) +{ + UtlHashHandle_t handle; + DispCollPlaneIndex_t planeIndex; + bool bDidInsert; + + planeIndex.vecPlane = vecNormal; + planeIndex.index = m_aEdgePlanes.Count(); + + handle = g_DispCollPlaneIndexHash.Insert( planeIndex, &bDidInsert ); + + if ( !bDidInsert ) + { + DispCollPlaneIndex_t &existingEntry = g_DispCollPlaneIndexHash[handle]; + if ( existingEntry.vecPlane == vecNormal ) + { + return existingEntry.index; + } + else + { + return ( existingEntry.index | 0x8000 ); + } + } + + return m_aEdgePlanes.AddToTail( vecNormal ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// NOTE: The plane distance get stored in the normal x position since it isn't +// used. +//----------------------------------------------------------------------------- +bool CDispCollTree::Cache_EdgeCrossAxisX( const Vector &vecEdge, const Vector &vecOnEdge, + const Vector &vecOffEdge, CDispCollTri *pTri, + unsigned short &iPlane ) +{ + // Calculate the normal - edge x axisX = ( 0.0, edgeZ, -edgeY ) + Vector vecNormal( 0.0f, vecEdge.z, -vecEdge.y ); + VectorNormalize( vecNormal ); + + // Check for zero length normals. + if( ( vecNormal.y == 0.0f ) || ( vecNormal.z == 0.0f ) ) + { + iPlane = DISPCOLL_NORMAL_UNDEF; + return false; + } + +// if ( pTri->m_vecNormal.Dot( vecNormal ) ) +// { +// iPlane = DISPCOLL_NORMAL_UNDEF; +// return false; +// } + + // Finish the plane definition - get distance. + float flDist = ( vecNormal.y * vecOnEdge.y ) + ( vecNormal.z * vecOnEdge.z ); + + // Special case the point off edge in plane + float flOffDist = ( vecNormal.y * vecOffEdge.y ) + ( vecNormal.z * vecOffEdge.z ); + if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) + { + // Adjust plane facing - triangle should be behind the plane. + vecNormal.x = -flDist; + vecNormal.y = -vecNormal.y; + vecNormal.z = -vecNormal.z; + } + else + { + vecNormal.x = flDist; + } + + // Add edge plane to edge plane list. + iPlane = static_cast( AddPlane( vecNormal ) ); + + // Created the cached edge. + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// NOTE: The plane distance get stored in the normal y position since it isn't +// used. +//----------------------------------------------------------------------------- +bool CDispCollTree::Cache_EdgeCrossAxisY( const Vector &vecEdge, const Vector &vecOnEdge, + const Vector &vecOffEdge, CDispCollTri *pTri, + unsigned short &iPlane ) +{ + // Calculate the normal - edge x axisY = ( -edgeZ, 0.0, edgeX ) + Vector vecNormal( -vecEdge.z, 0.0f, vecEdge.x ); + VectorNormalize( vecNormal ); + + // Check for zero length normals + if( ( vecNormal.x == 0.0f ) || ( vecNormal.z == 0.0f ) ) + { + iPlane = DISPCOLL_NORMAL_UNDEF; + return false; + } + +// if ( pTri->m_vecNormal.Dot( vecNormal ) ) +// { +// iPlane = DISPCOLL_NORMAL_UNDEF; +// return false; +// } + + // Finish the plane definition - get distance. + float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.z * vecOnEdge.z ); + + // Special case the point off edge in plane + float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.z * vecOffEdge.z ); + if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) + { + // Adjust plane facing if necessay - triangle should be behind the plane. + vecNormal.x = -vecNormal.x; + vecNormal.y = -flDist; + vecNormal.z = -vecNormal.z; + } + else + { + vecNormal.y = flDist; + } + + // Add edge plane to edge plane list. + iPlane = static_cast( AddPlane( vecNormal ) ); + + // Created the cached edge. + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::Cache_EdgeCrossAxisZ( const Vector &vecEdge, const Vector &vecOnEdge, + const Vector &vecOffEdge, CDispCollTri *pTri, + unsigned short &iPlane ) +{ + // Calculate the normal - edge x axisY = ( edgeY, -edgeX, 0.0 ) + Vector vecNormal( vecEdge.y, -vecEdge.x, 0.0f ); + VectorNormalize( vecNormal ); + + // Check for zero length normals + if( ( vecNormal.x == 0.0f ) || ( vecNormal.y == 0.0f ) ) + { + iPlane = DISPCOLL_NORMAL_UNDEF; + return false; + } + +// if ( pTri->m_vecNormal.Dot( vecNormal ) ) +// { +// iPlane = DISPCOLL_NORMAL_UNDEF; +// return false; +// } + + // Finish the plane definition - get distance. + float flDist = ( vecNormal.x * vecOnEdge.x ) + ( vecNormal.y * vecOnEdge.y ); + + // Special case the point off edge in plane + float flOffDist = ( vecNormal.x * vecOffEdge.x ) + ( vecNormal.y * vecOffEdge.y ); + if ( !( FloatMakePositive( flOffDist - flDist ) < DISPCOLL_DIST_EPSILON ) && ( flOffDist > flDist ) ) + { + // Adjust plane facing if necessay - triangle should be behind the plane. + vecNormal.x = -vecNormal.x; + vecNormal.y = -vecNormal.y; + vecNormal.z = -flDist; + } + else + { + vecNormal.z = flDist; + } + + // Add edge plane to edge plane list. + iPlane = static_cast( AddPlane( vecNormal ) ); + + // Created the cached edge. + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template +bool CDispCollTree::EdgeCrossAxis( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) +{ + if ( iPlane == DISPCOLL_NORMAL_UNDEF ) + return true; + + // Get the edge plane. + Vector vecNormal; + if ( ( iPlane & 0x8000 ) != 0 ) + { + VectorCopy( m_aEdgePlanes[(iPlane&0x7fff)], vecNormal ); + vecNormal.Negate(); + } + else + { + VectorCopy( m_aEdgePlanes[iPlane], vecNormal ); + } + + const int OTHER_AXIS1 = ( AXIS + 1 ) % 3; + const int OTHER_AXIS2 = ( AXIS + 2 ) % 3; + + // Get the pland distance are "fix" the normal. + float flDist = vecNormal[AXIS]; + vecNormal[AXIS] = 0.0f; + + // Calculate the closest point on box to plane (get extents in that direction). + Vector vecExtent; + //vecExtent[AXIS] = 0.0f; + vecExtent[OTHER_AXIS1] = ( vecNormal[OTHER_AXIS1] < 0.0f ) ? ray.m_Extents[OTHER_AXIS1] : -ray.m_Extents[OTHER_AXIS1]; + vecExtent[OTHER_AXIS2] = ( vecNormal[OTHER_AXIS2] < 0.0f ) ? ray.m_Extents[OTHER_AXIS2] : -ray.m_Extents[OTHER_AXIS2]; + + // Expand the plane by the extents of the box to reduce the swept box/triangle + // test to a ray/extruded triangle test (one of the triangles extruded planes + // was just calculated above). + Vector vecEnd; + vecEnd[AXIS] = 0; + vecEnd[OTHER_AXIS1] = ray.m_Start[OTHER_AXIS1] + ray.m_Delta[OTHER_AXIS1]; + vecEnd[OTHER_AXIS2] = ray.m_Start[OTHER_AXIS2] + ray.m_Delta[OTHER_AXIS2]; + + float flExpandDist = flDist - ( ( vecNormal[OTHER_AXIS1] * vecExtent[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecExtent[OTHER_AXIS2] ) ); + float flStart = ( vecNormal[OTHER_AXIS1] * ray.m_Start[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * ray.m_Start[OTHER_AXIS2] ) - flExpandDist; + float flEnd = ( vecNormal[OTHER_AXIS1] * vecEnd[OTHER_AXIS1] ) + ( vecNormal[OTHER_AXIS2] * vecEnd[OTHER_AXIS2] ) - flExpandDist; + + return ResolveRayPlaneIntersect( flStart, flEnd, vecNormal, flDist, pHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTree::EdgeCrossAxisX( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) +{ + return EdgeCrossAxis<0>( ray, iPlane, pHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTree::EdgeCrossAxisY( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) +{ + return EdgeCrossAxis<1>( ray, iPlane, pHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline bool CDispCollTree::EdgeCrossAxisZ( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ) +{ + return EdgeCrossAxis<2>( ray, iPlane, pHelper ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CDispCollTree::SweepAABBTriIntersect( const Ray_t &ray, const Vector &rayDir, int iTri, CDispCollTri *pTri, CBaseTrace *pTrace ) +{ + // Init test data. + CDispCollHelper helper; + helper.m_flEndFrac = 1.0f; + helper.m_flStartFrac = DISPCOLL_INVALID_FRAC; + + // Make sure objects are traveling toward one another. + float flDistAlongNormal = pTri->m_vecNormal.Dot( ray.m_Delta ); + if( flDistAlongNormal > DISPCOLL_DIST_EPSILON ) + return; + + // Test against the axis planes. + if ( !AxisPlanesXYZ( ray, pTri, &helper ) ) + { + return; + } + + // + // There are 9 edge tests - edges 1, 2, 3 cross with the box edges (symmetry) 1, 2, 3. However, the box + // is axis-aligned resulting in axially directional edges -- thus each test is edges 1, 2, and 3 vs. + // axial planes x, y, and z + // + // There are potentially 9 more tests with edges, the edge's edges and the direction of motion! + // NOTE: I don't think these tests are necessary for a manifold surface. + // + + CDispCollTriCache *pCache = &m_aTrisCache[iTri]; + + // Edges 1-3, interleaved - axis tests are 2d tests + if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[0], &helper ) ) { return; } + if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[1], &helper ) ) { return; } + if ( !EdgeCrossAxisX( ray, pCache->m_iCrossX[2], &helper ) ) { return; } + + if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[0], &helper ) ) { return; } + if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[1], &helper ) ) { return; } + if ( !EdgeCrossAxisY( ray, pCache->m_iCrossY[2], &helper ) ) { return; } + + if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[0], &helper ) ) { return; } + if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[1], &helper ) ) { return; } + if ( !EdgeCrossAxisZ( ray, pCache->m_iCrossZ[2], &helper ) ) { return; } + + // Test against the triangle face plane. + if ( !FacePlane( ray, rayDir, pTri, &helper ) ) + return; + + if ( ( helper.m_flStartFrac < helper.m_flEndFrac ) || ( FloatMakePositive( helper.m_flStartFrac - helper.m_flEndFrac ) < 0.001f ) ) + { + if ( ( helper.m_flStartFrac != DISPCOLL_INVALID_FRAC ) && ( helper.m_flStartFrac < pTrace->fraction ) ) + { + // Clamp -- shouldn't really ever be here!??? + if ( helper.m_flStartFrac < 0.0f ) + { + helper.m_flStartFrac = 0.0f; + } + + pTrace->fraction = helper.m_flStartFrac; + VectorCopy( helper.m_vecImpactNormal, pTrace->plane.normal ); + pTrace->plane.dist = helper.m_flImpactDist; + pTrace->dispFlags = pTri->m_uiFlags; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CDispCollTree::CDispCollTree() +{ + m_nPower = 0; + m_nFlags = 0; + + for ( int iPoint = 0; iPoint < 4; ++iPoint ) + { + m_vecSurfPoints[iPoint].Init(); + } + m_nContents = -1; + m_nSurfaceProps[0] = 0; + m_nSurfaceProps[1] = 0; + + m_vecStabDir.Init(); + m_mins.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + m_maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + + m_iCounter = 0; + + m_aVerts.Purge(); + m_aTris.Purge(); + m_aEdgePlanes.Purge(); +#ifdef ENGINE_DLL + m_hCache = INVALID_MEMHANDLE; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: deconstructor +//----------------------------------------------------------------------------- +CDispCollTree::~CDispCollTree() +{ +#ifdef ENGINE_DLL + if ( m_hCache != INVALID_MEMHANDLE ) + g_DispCollTriCache.DestroyResource( m_hCache ); +#endif + m_aVerts.Purge(); + m_aTris.Purge(); + m_aEdgePlanes.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::Create( CCoreDispInfo *pDisp ) +{ + // Create the AABB Tree. + return AABBTree_Create( pDisp ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CDispCollTree::PointInBounds( const Vector &vecBoxCenter, const Vector &vecBoxMin, + const Vector &vecBoxMax, bool bPoint ) +{ + // Point test inside bounds. + if( bPoint ) + { + return IsPointInBox( vecBoxCenter, m_mins, m_maxs ); + } + + // Box test inside bounds + Vector vecExtents; + VectorSubtract( vecBoxMax, vecBoxMin, vecExtents ); + vecExtents *= 0.5f; + + Vector vecExpandBounds[2]; + vecExpandBounds[0] = m_mins - vecExtents; + vecExpandBounds[1] = m_maxs + vecExtents; + + return IsPointInBox( vecBoxCenter, vecExpandBounds[0], vecExpandBounds[1] ); +} + +void CDispCollTree::GetVirtualMeshList( virtualmeshlist_t *pList ) +{ + int i; + int triangleCount = GetTriSize(); + pList->indexCount = triangleCount * 3; + pList->triangleCount = triangleCount; + pList->vertexCount = m_aVerts.Count(); + pList->pVerts = m_aVerts.Base(); + pList->pHull = NULL; + pList->surfacePropsIndex = GetSurfaceProps(0); + int index = 0; + for ( i = 0 ; i < triangleCount; i++ ) + { + pList->indices[index+0] = m_aTris[i].GetVert(0); + pList->indices[index+1] = m_aTris[i].GetVert(1); + pList->indices[index+2] = m_aTris[i].GetVert(2); + index += 3; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#ifdef ENGINE_DLL +static int g_nTrees; +#endif +CDispCollTree *DispCollTrees_Alloc( int count ) +{ + CDispCollTree *pTrees = NULL; +#ifdef ENGINE_DLL + pTrees = (CDispCollTree *)Hunk_Alloc( count * sizeof(CDispCollTree), false ); + g_nTrees = count; + for ( int i = 0; i < g_nTrees; i++ ) + { + Construct( pTrees + i ); + } +#else + pTrees = new CDispCollTree[count]; +#endif + if( !pTrees ) + return NULL; + + for ( int i = 0; i < count; i++ ) + { + pTrees[i].m_iCounter = i; + } + return pTrees; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void DispCollTrees_Free( CDispCollTree *pTrees ) +{ +#ifdef ENGINE_DLL + for ( int i = 0; i < g_nTrees; i++ ) + { + Destruct( pTrees + i ); + } + g_nTrees = 0; +#else + if( pTrees ) + { + delete [] pTrees; + pTrees = NULL; + } +#endif +} diff --git a/public/dispcoll_common.h b/public/dispcoll_common.h new file mode 100644 index 0000000..a2f6de6 --- /dev/null +++ b/public/dispcoll_common.h @@ -0,0 +1,456 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DISPCOLL_COMMON_H +#define DISPCOLL_COMMON_H +#pragma once + +#include "trace.h" +#include "builddisp.h" +#include "bitvec.h" +#ifdef ENGINE_DLL +#include "../engine/zone.h" +#endif + +#ifdef ENGINE_DLL +template +class CDispVector : public CUtlVector > +{ +}; +#else +template +class CDispVector : public CUtlVector > +{ +}; +#endif + +FORWARD_DECLARE_HANDLE( memhandle_t ); + +#define DISPCOLL_TREETRI_SIZE MAX_DISPTRIS +#define DISPCOLL_DIST_EPSILON 0.03125f +#define DISPCOLL_ROOTNODE_INDEX 0 +#define DISPCOLL_INVALID_TRI -1 +#define DISPCOLL_INVALID_FRAC -99999.9f +#define DISPCOLL_NORMAL_UNDEF 0xffff + +extern double g_flDispCollSweepTimer; +extern double g_flDispCollIntersectTimer; +extern double g_flDispCollInCallTimer; + +struct RayDispOutput_t +{ + short ndxVerts[4]; // 3 verts and a pad + float u, v; // the u, v paramters (edgeU = v1 - v0, edgeV = v2 - v0) + float dist; // intersection distance +}; + +// Assumptions: +// Max patch is 17x17, therefore 9 bits needed to represent a triangle index +// + +//============================================================================= +// Displacement Collision Triangle +class CDispCollTri +{ + + struct index_t + { + union + { + struct + { + unsigned short uiVert:9; + unsigned short uiMin:2; + unsigned short uiMax:2; + } m_Index; + + unsigned short m_IndexDummy; + }; + }; + + index_t m_TriData[3]; + +public: + unsigned short m_ucSignBits:3; // Plane test. + unsigned short m_ucPlaneType:3; // Axial test? + unsigned short m_uiFlags:5; // Uses 5-bits - maybe look into merging it with something? + + Vector m_vecNormal; // Triangle normal (plane normal). + float m_flDist; // Triangle plane dist. + + // Creation. + CDispCollTri(); + void Init( void ); + void CalcPlane( CDispVector &m_aVerts ); + void FindMinMax( CDispVector &m_aVerts ); + + // Triangle data. + inline void SetVert( int iPos, int iVert ) { Assert( ( iPos >= 0 ) && ( iPos < 3 ) ); Assert( ( iVert >= 0 ) && ( iVert < ( 1 << 9 ) ) ); m_TriData[iPos].m_Index.uiVert = iVert; } + inline int GetVert( int iPos ) const { Assert( ( iPos >= 0 ) && ( iPos < 3 ) ); return m_TriData[iPos].m_Index.uiVert; } + inline void SetMin( int iAxis, int iMin ) { Assert( ( iAxis >= 0 ) && ( iAxis < 3 ) ); Assert( ( iMin >= 0 ) && ( iMin < 3 ) ); m_TriData[iAxis].m_Index.uiMin = iMin; } + inline int GetMin( int iAxis ) const { Assert( ( iAxis >= 0 ) && ( iAxis < 3 ) ); return m_TriData[iAxis].m_Index.uiMin; } + inline void SetMax( int iAxis, int iMax ) { Assert( ( iAxis >= 0 ) && ( iAxis < 3 ) ); Assert( ( iMax >= 0 ) && ( iMax < 3 ) ); m_TriData[iAxis].m_Index.uiMax = iMax; } + inline int GetMax( int iAxis ) const { Assert( ( iAxis >= 0 ) && ( iAxis < 3 ) ); return m_TriData[iAxis].m_Index.uiMax; } +}; + +//============================================================================= +// Helper +class CDispCollHelper +{ +public: + + float m_flStartFrac; + float m_flEndFrac; + Vector m_vecImpactNormal; + float m_flImpactDist; +}; + +//============================================================================= +// Cache +#pragma pack(1) +class CDispCollTriCache +{ +public: + unsigned short m_iCrossX[3]; + unsigned short m_iCrossY[3]; + unsigned short m_iCrossZ[3]; +}; +#pragma pack() +#include "mathlib/ssemath.h" + +class CDispCollNode +{ +public: + FourVectors m_mins; + FourVectors m_maxs; +}; + +class CDispCollLeaf +{ +public: + short m_tris[2]; +}; + +// a power 4 displacement can have 341 nodes, pad out to 344 for 16-byte alignment +const int MAX_DISP_AABB_NODES = 341; +const int MAX_AABB_LIST = 344; + +struct rayleaflist_t +{ + FourVectors rayStart; + FourVectors rayExtents; + FourVectors invDelta; + int nodeList[MAX_AABB_LIST]; + int maxIndex; +}; + +//============================================================================= +// +// Displacement Collision Tree Data +// +class CDispCollTree +{ +public: + + // Creation/Destruction. + CDispCollTree(); + ~CDispCollTree(); + virtual bool Create( CCoreDispInfo *pDisp ); + + // Raycasts. + // NOTE: These assume you've precalculated invDelta as well as culled to the bounds of this disp + bool AABBTree_Ray( const Ray_t &ray, const Vector &invDelta, CBaseTrace *pTrace, bool bSide = true ); + bool AABBTree_Ray( const Ray_t &ray, const Vector &invDelta, RayDispOutput_t &output ); + // NOTE: Lower perf helper function, should not be used in the game runtime + bool AABBTree_Ray( const Ray_t &ray, RayDispOutput_t &output ); + + // Hull Sweeps. + // NOTE: These assume you've precalculated invDelta as well as culled to the bounds of this disp + bool AABBTree_SweepAABB( const Ray_t &ray, const Vector &invDelta, CBaseTrace *pTrace ); + + // Hull Intersection. + bool AABBTree_IntersectAABB( const Vector &absMins, const Vector &absMaxs ); + + // Point/Box vs. Bounds. + bool PointInBounds( Vector const &vecBoxCenter, Vector const &vecBoxMin, Vector const &vecBoxMax, bool bPoint ); + + // Utility. + inline void SetPower( int power ) { m_nPower = power; } + inline int GetPower( void ) { return m_nPower; } + + inline int GetFlags( void ) { return m_nFlags; } + inline void SetFlags( int nFlags ) { m_nFlags = nFlags; } + inline bool CheckFlags( int nFlags ) { return ( ( nFlags & GetFlags() ) != 0 ) ? true : false; } + + inline int GetWidth( void ) { return ( ( 1 << m_nPower ) + 1 ); } + inline int GetHeight( void ) { return ( ( 1 << m_nPower ) + 1 ); } + inline int GetSize( void ) { return ( ( 1 << m_nPower ) + 1 ) * ( ( 1 << m_nPower ) + 1 ); } + inline int GetTriSize( void ) { return ( ( 1 << m_nPower ) * ( 1 << m_nPower ) * 2 ); } + +// inline void SetTriFlags( short iTri, unsigned short nFlags ) { m_aTris[iTri].m_uiFlags = nFlags; } + + inline void GetStabDirection( Vector &vecDir ) { vecDir = m_vecStabDir; } + + inline void GetBounds( Vector &vecBoxMin, Vector &vecBoxMax ) { vecBoxMin = m_mins; vecBoxMax = m_maxs; } + inline int GetContents( void ) { return m_nContents; } + inline void SetSurfaceProps( int iProp, short nSurfProp ) { Assert( ( iProp >= 0 ) && ( iProp < 2 ) ); m_nSurfaceProps[iProp] = nSurfProp; } + inline short GetSurfaceProps( int iProp ) { return m_nSurfaceProps[iProp]; } + + inline unsigned int GetMemorySize( void ) { return m_nSize; } + inline unsigned int GetCacheMemorySize( void ) { return ( m_aTrisCache.Count() * sizeof(CDispCollTriCache) + m_aEdgePlanes.Count() * sizeof(Vector) ); } + + inline bool IsCached( void ) { return m_aTrisCache.Count() == m_aTris.Count(); } + + void GetVirtualMeshList( struct virtualmeshlist_t *pList ); + int AABBTree_GetTrisInSphere( const Vector ¢er, float radius, unsigned short *pIndexOut, int indexMax ); + +public: + + inline int Nodes_GetChild( int iNode, int nDirection ); + inline int Nodes_CalcCount( int nPower ); + inline int Nodes_GetParent( int iNode ); + inline int Nodes_GetLevel( int iNode ); + inline int Nodes_GetIndexFromComponents( int x, int y ); + + void LockCache(); + void UnlockCache(); + void Cache( void ); + void Uncache() { m_aTrisCache.Purge(); m_aEdgePlanes.Purge(); } + +#ifdef ENGINE_DLL + // Data manager methods + static size_t EstimatedSize( CDispCollTree *pTree ) + { + return pTree->GetCacheMemorySize(); + } + + static CDispCollTree *CreateResource( CDispCollTree *pTree ) + { + // Created ahead of time + return pTree; + } + + bool GetData() + { + return IsCached(); + } + + size_t Size() + { + return GetCacheMemorySize(); + } + + void DestroyResource() + { + Uncache(); + m_hCache = NULL; + } +#endif + +protected: + + bool AABBTree_Create( CCoreDispInfo *pDisp ); + void AABBTree_CopyDispData( CCoreDispInfo *pDisp ); + void AABBTree_CreateLeafs( void ); + void AABBTree_GenerateBoxes_r( int nodeIndex, Vector *pMins, Vector *pMaxs ); + void AABBTree_CalcBounds( void ); + + int AABBTree_BuildTreeTrisInSphere_r( const Vector ¢er, float radius, int iNode, unsigned short *pIndexOut, unsigned short indexMax ); + + void AABBTree_TreeTrisRayTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, CBaseTrace *pTrace, bool bSide, CDispCollTri **pImpactTri ); + void AABBTree_TreeTrisRayBarycentricTest( const Ray_t &ray, const Vector &vecInvDelta, int iNode, RayDispOutput_t &output, CDispCollTri **pImpactTri ); + + int FORCEINLINE BuildRayLeafList( int iNode, rayleaflist_t &list ); + + struct AABBTree_TreeTrisSweepTest_Args_t + { + AABBTree_TreeTrisSweepTest_Args_t( const Ray_t &ray, const Vector &vecInvDelta, const Vector &rayDir, CBaseTrace *pTrace ) + : ray( ray ), vecInvDelta( vecInvDelta ), rayDir( rayDir ), pTrace( pTrace ) {} + const Ray_t &ray; + const Vector &vecInvDelta; + const Vector &rayDir; + CBaseTrace *pTrace; + }; + +protected: + + void SweepAABBTriIntersect( const Ray_t &ray, const Vector &rayDir, int iTri, CDispCollTri *pTri, CBaseTrace *pTrace ); + + void Cache_Create( CDispCollTri *pTri, int iTri ); // Testing! + bool Cache_EdgeCrossAxisX( const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane ); + bool Cache_EdgeCrossAxisY( const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane ); + bool Cache_EdgeCrossAxisZ( const Vector &vecEdge, const Vector &vecOnEdge, const Vector &vecOffEdge, CDispCollTri *pTri, unsigned short &iPlane ); + + inline bool FacePlane( const Ray_t &ray, const Vector &rayDir, CDispCollTri *pTri, CDispCollHelper *pHelper ); + bool FORCEINLINE AxisPlanesXYZ( const Ray_t &ray, CDispCollTri *pTri, CDispCollHelper *pHelper ); + inline bool EdgeCrossAxisX( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ); + inline bool EdgeCrossAxisY( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ); + inline bool EdgeCrossAxisZ( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ); + + bool ResolveRayPlaneIntersect( float flStart, float flEnd, const Vector &vecNormal, float flDist, CDispCollHelper *pHelper ); + template bool FORCEINLINE TestOneAxisPlaneMin( const Ray_t &ray, CDispCollTri *pTri ); + template bool FORCEINLINE TestOneAxisPlaneMax( const Ray_t &ray, CDispCollTri *pTri ); + template bool EdgeCrossAxis( const Ray_t &ray, unsigned short iPlane, CDispCollHelper *pHelper ); + + // Utility + inline void CalcClosestBoxPoint( const Vector &vecPlaneNormal, const Vector &vecBoxStart, const Vector &vecBoxExtents, Vector &vecBoxPoint ); + inline void CalcClosestExtents( const Vector &vecPlaneNormal, const Vector &vecBoxExtents, Vector &vecBoxPoint ); + int AddPlane( const Vector &vecNormal ); + bool FORCEINLINE IsLeafNode(int iNode); +public: + Vector m_mins; // Bounding box of the displacement surface and base face + int m_iCounter; + Vector m_maxs; // Bounding box of the displacement surface and base face +protected: + int m_nContents; // The displacement surface "contents" (solid, etc...) + +#ifdef ENGINE_DLL + memhandle_t m_hCache; +#endif + + int m_nPower; // Size of the displacement ( 2^power + 1 ) + int m_nFlags; + + Vector m_vecSurfPoints[4]; // Base surface points. + // Collision data. + Vector m_vecStabDir; // Direction to stab for this displacement surface (is the base face normal) + short m_nSurfaceProps[2]; // Surface properties (save off from texdata for impact responses) + +protected: + CDispVector m_aVerts; // Displacement verts. + CDispVector m_aTris; // Displacement triangles. + CDispVector m_nodes; // Nodes. + CDispVector m_leaves; // Leaves. + // Cache + CUtlVector m_aTrisCache; + CUtlVector m_aEdgePlanes; + + CDispCollHelper m_Helper; + + unsigned int m_nSize; + +}; + +FORCEINLINE bool CDispCollTree::IsLeafNode(int iNode) +{ + return iNode >= m_nodes.Count() ? true : false; +} + +//----------------------------------------------------------------------------- +// Purpose: get the child node index given the current node index and direction +// of the child (1 of 4) +// Input: iNode - current node index +// nDirection - direction of the child ( [0...3] - SW, SE, NW, NE ) +// Output: int - the index of the child node +//----------------------------------------------------------------------------- +inline int CDispCollTree::Nodes_GetChild( int iNode, int nDirection ) +{ + // node range [0...m_NodeCount) + Assert( iNode >= 0 ); + Assert( iNode < m_nodes.Count() ); + + // ( node index * 4 ) + ( direction + 1 ) + return ( ( iNode << 2 ) + ( nDirection + 1 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline int CDispCollTree::Nodes_CalcCount( int nPower ) +{ + Assert( nPower >= 1 ); + Assert( nPower <= 4 ); + + return ( ( 1 << ( ( nPower + 1 ) << 1 ) ) / 3 ); +} + +//----------------------------------------------------------------------------- +// Purpose: get the parent node index given the current node +// Input: iNode - current node index +// Output: int - the index of the parent node +//----------------------------------------------------------------------------- +inline int CDispCollTree::Nodes_GetParent( int iNode ) +{ + // node range [0...m_NodeCount) + Assert( iNode >= 0 ); + Assert( iNode < m_nodes.Count() ); + + // ( node index - 1 ) / 4 + return ( ( iNode - 1 ) >> 2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// TODO: should make this a function - not a hardcoded set of statements!!! +//----------------------------------------------------------------------------- +inline int CDispCollTree::Nodes_GetLevel( int iNode ) +{ + // node range [0...m_NodeCount) + Assert( iNode >= 0 ); + Assert( iNode < m_nodes.Count() ); + + // level = 2^n + 1 + if ( iNode == 0 ) { return 1; } + if ( iNode < 5 ) { return 2; } + if ( iNode < 21 ) { return 3; } + if ( iNode < 85 ) { return 4; } + if ( iNode < 341 ) { return 5; } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline int CDispCollTree::Nodes_GetIndexFromComponents( int x, int y ) +{ + int nIndex = 0; + + // Interleave bits from the x and y values to create the index + int iShift; + for( iShift = 0; x != 0; iShift += 2, x >>= 1 ) + { + nIndex |= ( x & 1 ) << iShift; + } + + for( iShift = 1; y != 0; iShift += 2, y >>= 1 ) + { + nIndex |= ( y & 1 ) << iShift; + } + + return nIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTree::CalcClosestBoxPoint( const Vector &vecPlaneNormal, const Vector &vecBoxStart, + const Vector &vecBoxExtents, Vector &vecBoxPoint ) +{ + vecBoxPoint = vecBoxStart; + ( vecPlaneNormal[0] < 0.0f ) ? vecBoxPoint[0] += vecBoxExtents[0] : vecBoxPoint[0] -= vecBoxExtents[0]; + ( vecPlaneNormal[1] < 0.0f ) ? vecBoxPoint[1] += vecBoxExtents[1] : vecBoxPoint[1] -= vecBoxExtents[1]; + ( vecPlaneNormal[2] < 0.0f ) ? vecBoxPoint[2] += vecBoxExtents[2] : vecBoxPoint[2] -= vecBoxExtents[2]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline void CDispCollTree::CalcClosestExtents( const Vector &vecPlaneNormal, const Vector &vecBoxExtents, + Vector &vecBoxPoint ) +{ + ( vecPlaneNormal[0] < 0.0f ) ? vecBoxPoint[0] = vecBoxExtents[0] : vecBoxPoint[0] = -vecBoxExtents[0]; + ( vecPlaneNormal[1] < 0.0f ) ? vecBoxPoint[1] = vecBoxExtents[1] : vecBoxPoint[1] = -vecBoxExtents[1]; + ( vecPlaneNormal[2] < 0.0f ) ? vecBoxPoint[2] = vecBoxExtents[2] : vecBoxPoint[2] = -vecBoxExtents[2]; +} + +//============================================================================= +// Global Helper Functions +CDispCollTree *DispCollTrees_Alloc( int count ); +void DispCollTrees_Free( CDispCollTree *pTrees ); + +#endif // DISPCOLL_COMMON_H diff --git a/public/dlight.h b/public/dlight.h new file mode 100644 index 0000000..64e6f8b --- /dev/null +++ b/public/dlight.h @@ -0,0 +1,94 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined ( DLIGHTH ) +#define DLIGHTH +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" + +class IClientRenderable; + +//----------------------------------------------------------------------------- +// Dynamic light structure +//----------------------------------------------------------------------------- + +enum +{ + DLIGHT_NO_WORLD_ILLUMINATION = 0x1, + DLIGHT_NO_MODEL_ILLUMINATION = 0x2, + + // NOTE: These two features are used to dynamically tweak the alpha on displacements + // which is a special effect for selecting which texture to use. If + // we ever change how alpha is stored for displacements, we'll have to kill this feature + DLIGHT_ADD_DISPLACEMENT_ALPHA = 0x4, + DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA = 0x8, + DLIGHT_DISPLACEMENT_MASK = (DLIGHT_ADD_DISPLACEMENT_ALPHA | DLIGHT_SUBTRACT_DISPLACEMENT_ALPHA), +}; + +// This is the lighting value that is used to determine when something can be +// culle from lighting because it is close enough to black to be virtually black. +//#define MIN_LIGHTING_VALUE (1.0f/256.0f) + +// This is the broken value of MIN_LIGHTING_VALUE that we have to take into consideration +// to make sure that the lighting for dlights look the same as they did in HL2. +// We'll use the real MIN_LIGHTING_VALUE above to calculate larger radii for dynamic +// light sources. +//#define HL2_BROKEN_MIN_LIGHTING_VALUE (20.0f/256.0f) + +struct dlight_t +{ + int flags; + Vector origin; + float radius; + ColorRGBExp32 color; // Light color with exponent + float die; // stop lighting after this time + float decay; // drop this each second + float minlight; // don't add when contributing less + int key; + int style; // lightstyle + + // For spotlights. Use m_OuterAngle == 0 for point lights + Vector m_Direction; // center of the light cone + float m_InnerAngle; + float m_OuterAngle; + + // If this ptr is set, the dlight will only affect this particular client renderable + const IClientRenderable* m_pExclusiveLightReceiver; + + dlight_t() + : m_pExclusiveLightReceiver( NULL ) + { + } + + // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float GetRadius() const + { +// return FastSqrt( radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ) ); + return radius; + } + + // see comments above about HL2_BROKEN_MIN_LIGHTING_VALUE and MIN_LIGHTING_VALUE + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float GetRadiusSquared() const + { +// return radius * radius * ( HL2_BROKEN_MIN_LIGHTING_VALUE / MIN_LIGHTING_VALUE ); + return radius * radius; + } + + // THIS SHOULD ONLY GET CALLED FROM THE ENGINE + float IsRadiusGreaterThanZero() const + { + // don't bother calculating the new radius if you just want to know if it is greater than zero. + return radius > 0.0f; + } +}; + +#endif diff --git a/public/dmserializers/idmserializers.h b/public/dmserializers/idmserializers.h new file mode 100644 index 0000000..301f486 --- /dev/null +++ b/public/dmserializers/idmserializers.h @@ -0,0 +1,47 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// Main header file for the serializers DLL +// +//============================================================================= + +#ifndef IDMSERIALIZERS_H +#define IDMSERIALIZERS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" + + +//----------------------------------------------------------------------------- +// Interface +//----------------------------------------------------------------------------- +class IDmSerializers : public IAppSystem +{ +}; + + +//----------------------------------------------------------------------------- +// Used only by applications to hook in DmSerializers +//----------------------------------------------------------------------------- +#define DMSERIALIZERS_INTERFACE_VERSION "VDmSerializers001" + + +//----------------------------------------------------------------------------- +// Singleton +//----------------------------------------------------------------------------- +extern IDmSerializers *g_pDmSerializers; + + +#endif // DMSERIALIZERS_H + + diff --git a/public/dmxloader/dmxattribute.h b/public/dmxloader/dmxattribute.h new file mode 100644 index 0000000..3d5f7f6 --- /dev/null +++ b/public/dmxloader/dmxattribute.h @@ -0,0 +1,252 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMXATTRIBUTE_H +#define DMXATTRIBUTE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "datamodel/dmattributetypes.h" +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/mempool.h" +#include "dmxloader/dmxloader.h" + + +//----------------------------------------------------------------------------- +// Forward declarations: +//----------------------------------------------------------------------------- +class CDmxElement; + + + + + +#define DECLARE_DMX_ATTRIBUTE_TYPE_INTERNAL( _className, _storageType, _attributeType, _attributeName, _defaultSetStatement ) \ + template< > class CDmAttributeInfo< _className > \ + { \ + private: \ + enum { ATTRIBUTE_TYPE = _attributeType }; \ + typedef _storageType StorageType_t; \ + static DmAttributeType_t AttributeType() { return _attributeType; } \ + static const char *AttributeTypeName() { return _attributeName; } \ + static void SetDefaultValue( _className& value ) { _defaultSetStatement } \ + friend class CDmxAttribute; \ + friend class CDmxElement; \ + }; \ + +#define DECLARE_DMX_ATTRIBUTE_ARRAY_TYPE_INTERNAL( _className, _storageType, _attributeType, _attributeName ) \ + template< > class CDmAttributeInfo< CUtlVector<_className> > \ + { \ + private: \ + enum { ATTRIBUTE_TYPE = _attributeType }; \ + typedef _storageType StorageType_t; \ + static DmAttributeType_t AttributeType() { return _attributeType; } \ + static const char *AttributeTypeName() { return _attributeName; } \ + static void SetDefaultValue( CUtlVector< _className >& value ) { value.RemoveAll(); } \ + friend class CDmxAttribute; \ + friend class CDmxElement; \ + }; \ + +#define DECLARE_DMX_ATTRIBUTE_TYPE( _className, _attributeType, _attributeName, _defaultSetStatement ) \ + DECLARE_DMX_ATTRIBUTE_TYPE_INTERNAL( _className, _className, _attributeType, _attributeName, _defaultSetStatement ) + +#define DECLARE_DMX_ATTRIBUTE_ARRAY_TYPE( _className, _attributeType, _attributeName )\ + DECLARE_DMX_ATTRIBUTE_ARRAY_TYPE_INTERNAL( _className, CUtlVector< _className >, _attributeType, _attributeName ) + + + +//----------------------------------------------------------------------------- +// Attribute info, modified for use in mod code +//----------------------------------------------------------------------------- +DECLARE_DMX_ATTRIBUTE_TYPE( CDmxElement*, AT_ELEMENT, "element", value = 0; ) +DECLARE_DMX_ATTRIBUTE_ARRAY_TYPE( CDmxElement*, AT_ELEMENT_ARRAY, "element_array" ) + +DECLARE_DMX_ATTRIBUTE_TYPE( CUtlString, AT_STRING, "string", value.Set( NULL ); ) +DECLARE_DMX_ATTRIBUTE_ARRAY_TYPE( CUtlString, AT_STRING_ARRAY, "string_array" ) + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CDmxAttribute +{ + DECLARE_DMX_ALLOCATOR( ); + +public: + // Returns attribute name and type + DmAttributeType_t GetType() const; + const char *GetTypeString() const; + template< class T > bool IsA() const; + + // Returns the name. NOTE: The utlsymbol + // can be turned into a string by using g_pDataModel->String(); + const char *GetName() const; + CUtlSymbolLarge GetNameSymbol() const; + void SetName( const char *pName ); + + // Gets values + template< class T > const T& GetValue( ) const; + template< class T > const CUtlVector< T >& GetArray( ) const; + const char *GetValueString() const; + + // Sets values (+ type) + template< class T > void SetValue( const T& value ); + void SetValue( const char *pString ); + void SetValue( char *pString ); + void SetValue( const void *pBuffer, size_t nLen ); + void SetValue( const CDmxAttribute *pAttribute ); + + // Method to set values in an array (just directly operate on the array) + // NOTE: This will create a new array of the appropriate type if + // the type doesn't match the current type + template< class T > CUtlVector< T >& GetArrayForEdit(); + + // Sets the attribute to its default value based on its type + void SetToDefaultValue(); + + // Convert to and from string + void SetValueFromString( const char *pValue ); + const char *GetValueAsString( char *pBuffer, size_t nBufLen ) const; + + // Gets the size of an array, returns 0 if it's not an array type + int GetArrayCount() const; + + // Read from file + bool Unserialize( DmAttributeType_t type, CUtlBuffer &buf ); + bool UnserializeElement( DmAttributeType_t type, CUtlBuffer &buf ); + bool Serialize( CUtlBuffer &buf ) const; + bool SerializeElement( int nIndex, CUtlBuffer &buf ) const; + bool SerializesOnMultipleLines() const; + + // Returns the size of the variables storing the various attribute types + static int AttributeDataSize( DmAttributeType_t type ); + // Gets the basic type for a given array attribute type (e.g. AT_INT_ARRAY -> AT_INT) + static DmAttributeType_t ArrayAttributeBasicType( DmAttributeType_t type ); + +private: + CDmxAttribute( const char *pAttributeName ); + CDmxAttribute( CUtlSymbolLarge attributeName ); + ~CDmxAttribute(); + + // Allocate, free memory for data + void AllocateDataMemory( DmAttributeType_t type ); + void AllocateDataMemory_AndConstruct( DmAttributeType_t type ); + void FreeDataMemory( ); + + + // Untyped methods for getting/setting used by unpack + void SetValue( DmAttributeType_t type, const void *pSrc, int nLen ); + // NOTE: [Get|Set]ArrayValue don't currently support AT_STRING_ARRAY, AT_STRING_VOID or AT_ELEMENT_ARRAY + void SetArrayValue( DmAttributeType_t type, const void *pSrc, int nDataTypeSize, int nArrayLength, int nSrcStride ); + void GetArrayValue( DmAttributeType_t type, void *pDest, int nDataTypeSize, int nArrayLength, const char *pDefaultString = NULL ) const; + void SetArrayCount( int nArrayCount ); + const void *GetArrayBase( void ) const; + + // Helper templated methods called from untyped methods (VT is vector datatype, T is basic datatype, VT will be the same as T if the attribute is non-array) + template < class VT, class T > void ConstructDataMemory( void ); + template < class VT, class T > void DestructDataMemory( void ); + template < class VT, class T > void SetArrayCount( int nArrayCount ); + template < class VT, class T > void GetArrayCount( int &nArrayCount ) const; + template < class VT, class T > void GetArrayBase( const void * &pBasePtr ) const; + template < class VT, class T > void SerializesOnMultipleLines( bool &bResult ) const; + template < class VT, class T > void SerializeType( bool &bSuccess, CUtlBuffer &buf ) const; + template < class VT, class T > void SerializeTypedElement( bool &bSuccess, int nIndex, CUtlBuffer &buf ) const; + template < class VT, class T > void UnserializeType( bool &bSuccess, CUtlBuffer &buf ); + template < class VT, class T > void UnserializeTypedElement( bool &bSuccess, CUtlBuffer &buf ); + template < class VT, class T > void SetDefaultValue( void ); + + + DmAttributeType_t m_Type; + CUtlSymbolLarge m_Name; + void *m_pData; + + static CUtlSymbolTableLargeMT s_AttributeNameSymbols; + + friend class CDmxElement; + +public: + + static const char *s_pAttributeTypeName[AT_TYPE_COUNT]; + +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline DmAttributeType_t CDmxAttribute::GetType() const +{ + return m_Type; +} + +template< class T > inline bool CDmxAttribute::IsA() const +{ + return GetType() == CDmAttributeInfo< T >::ATTRIBUTE_TYPE; +} + +inline CUtlSymbolLarge CDmxAttribute::GetNameSymbol() const +{ + return m_Name; +} + + +//----------------------------------------------------------------------------- +// Sets a value in the attribute +//----------------------------------------------------------------------------- +template< class T > void CDmxAttribute::SetValue( const T& value ) +{ + AllocateDataMemory( CDmAttributeInfo::AttributeType() ); + CopyConstruct( (T*)m_pData, value ); +} + + +//----------------------------------------------------------------------------- +// Returns data in the attribute +//----------------------------------------------------------------------------- +inline const char *CDmxAttribute::GetValueString() const +{ + if ( m_Type == AT_STRING ) + return *(CUtlString*)m_pData; + return ""; +} + +template< class T > +inline const T& CDmxAttribute::GetValue( ) const +{ + if ( CDmAttributeInfo::AttributeType() == m_Type ) + return *(T*)m_pData; + + static T defaultValue; + CDmAttributeInfo::SetDefaultValue( defaultValue ); + return defaultValue; +} + +template< class T > +inline const CUtlVector< T >& CDmxAttribute::GetArray( ) const +{ + if ( CDmAttributeInfo< CUtlVector< T > >::AttributeType() == m_Type ) + return *( CUtlVector< T > *)m_pData; + + static CUtlVector defaultArray; + return defaultArray; +} + +template< class T > +inline CUtlVector< T >& CDmxAttribute::GetArrayForEdit( ) +{ + if ( CDmAttributeInfo< CUtlVector< T > >::AttributeType() == m_Type ) + return *( CUtlVector< T > *)m_pData; + + AllocateDataMemory( CDmAttributeInfo< CUtlVector< T > >::AttributeType() ); + Construct( (CUtlVector*)m_pData ); + return *(CUtlVector< T > *)m_pData; +} + +#endif // DMXATTRIBUTE_H diff --git a/public/dmxloader/dmxelement.h b/public/dmxloader/dmxelement.h new file mode 100644 index 0000000..d77cfae --- /dev/null +++ b/public/dmxloader/dmxelement.h @@ -0,0 +1,518 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMXELEMENT_H +#define DMXELEMENT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "datamodel/dmattributetypes.h" +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier1/mempool.h" +#include "tier1/UtlSortVector.h" +#include "dmxloader/dmxattribute.h" + + +//----------------------------------------------------------------------------- +// Sort functor class for attributes +//----------------------------------------------------------------------------- +class CDmxAttributeLess +{ +public: + bool Less( const CDmxAttribute * pAttribute1, const CDmxAttribute *pAttribute2, void *pContext ) + { + return (pAttribute1?pAttribute1->GetNameSymbol():CUtlSymbolLarge(UTL_INVAL_SYMBOL_LARGE)) < (pAttribute2?pAttribute2->GetNameSymbol():CUtlSymbolLarge(UTL_INVAL_SYMBOL_LARGE)); + } +}; + + +enum BitfieldType_t +{ + BITFIELD_TYPE_NONE, + BITFIELD_TYPE_BOOL, + BITFIELD_TYPE_CHAR, + BITFIELD_TYPE_UNSIGNED_CHAR, + BITFIELD_TYPE_BYTE = BITFIELD_TYPE_UNSIGNED_CHAR, + BITFIELD_TYPE_SHORT, + BITFIELD_TYPE_UNSIGNED_SHORT, + BITFIELD_TYPE_INT, + BITFIELD_TYPE_UNSIGNED_INT, +}; + +//----------------------------------------------------------------------------- +// Used to unpack elements into a structure. Does not recurse +// Also does not work with arrays. +//----------------------------------------------------------------------------- +struct DmxElementUnpackStructure_t +{ + const char *m_pAttributeName; + const char *m_pDefaultString; + DmAttributeType_t m_AttributeType; + int m_nOffset; + int m_nSize; // If size is -1 the AT_STRING datatype is considered to be a UtlString, rather than a char array. + int m_nBitOffset; // Default value for this should be -1. A non-negative value indicates that the attribute is a bitfield and + // m_nSize should be interpreted as number of bits + BitfieldType_t m_BitfieldType; // the data type of your variable in the bitfield. + const void *m_pUserData; // If you want to associate some app-specific data with each field + + // Embedded structure / Baseclass ptr + const char *m_pTypeName; + const DmxElementUnpackStructure_t *m_pSub; + + int m_nArrayLength; // For fixed-size arrays, this is a positive value (default is 0). For arrays, m_nSize is the size of an array element and m_pDefaultString is the default element value. +}; + +#define NO_BIT_OFFSET -1 +#define UTL_STRING_SIZE -1 +#define NO_USER_DATA NULL +#define NO_EMBEDDED_TYPENAME NULL +#define NO_EMBEDDED_STRUCT_PTR NULL +#define NOT_A_BITFIELD BITFIELD_TYPE_NONE +#define NOT_AN_ARRAY 0 + +#define DECLARE_DMXELEMENT_UNPACK() \ + template friend DmxElementUnpackStructure_t *DmxElementUnpackInit(T *); + +#define BEGIN_DMXELEMENT_UNPACK( _structName ) \ + template DmxElementUnpackStructure_t *DmxElementUnpackInit(T *); \ + template <> DmxElementUnpackStructure_t *DmxElementUnpackInit<_structName>( _structName * ); \ + namespace _structName##_UnpackInit \ + { \ + static DmxElementUnpackStructure_t *s_pUnpack = DmxElementUnpackInit( (_structName *)NULL ); \ + } \ + \ + template <> DmxElementUnpackStructure_t *DmxElementUnpackInit<_structName>( _structName * ) \ + { \ + typedef _structName DestStructType_t; \ + static DmxElementUnpackStructure_t unpack[] = \ + { \ + +#define DMXELEMENT_UNPACK_FLTX4( _attributeName, _defaultString, _varName ) \ + { _attributeName, _defaultString, CDmAttributeInfo::AttributeType(), offsetof( DestStructType_t, _varName ), sizeof( fltx4 ), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, + +#define DMXELEMENT_UNPACK_EMBEDDED( _typeName, _attributeName, _varName, _embeddedUnpackStructure ) \ + { _attributeName, "", AT_TYPE_COUNT, offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, _typeName, _embeddedUnpackStructure, NOT_AN_ARRAY }, + +#define DMXELEMENT_UNPACK_BASECLASS( _structName, _baseClass ) \ + { "Baseclass unpack", "", AT_TYPE_COUNT, (int)static_cast< _baseClass * >( (_structName*)0), 0, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, #_baseClass, DmxElementUnpackInit<_baseClass>( (_baseClass *)0 ), NOT_AN_ARRAY }, + +#define DMXELEMENT_UNPACK_BASECLASS_NAMESPACE( _namespace, _structName, _baseClass ) \ + { "Baseclass unpack", "", AT_TYPE_COUNT, (int)static_cast< _baseClass * >( (_structName*)0), 0, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, #_baseClass, DmxElementUnpackInit##_namespace<_baseClass>( (_baseClass *)0 ), NOT_AN_ARRAY }, + +#define VGUI_UNPACK_BASEPANEL() \ + DMXELEMENT_UNPACK_BASECLASS_NAMESPACE( vgui, DestStructType_t, DestStructType_t::BaseClass ) \ + +#define DMXELEMENT_UNPACK_FIELD( _attributeName, _defaultString, _type, _varName ) \ + { _attributeName, _defaultString, CDmAttributeInfo<_type>::AttributeType(), offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +// Use for preallocated char array +#define DMXELEMENT_UNPACK_FIELD_STRING( _attributeName, _defaultString, _varName ) \ + { _attributeName, _defaultString, AT_STRING, offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +// Use for UtlString datatype +#define DMXELEMENT_UNPACK_FIELD_UTLSTRING( _attributeName, _defaultString, _varName ) \ + { _attributeName, _defaultString, AT_STRING, offsetof( DestStructType_t, _varName ), UTL_STRING_SIZE, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +#define DMXELEMENT_UNPACK_SHORT( _attributeName, _defaultString, _varName ) \ + { _attributeName, _defaultString, AT_INT, offsetof( DestStructType_t, _varName ), sizeof( short ), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +#define DMXELEMENT_UNPACK_CHAR( _attributeName, _defaultString, _varName ) \ + { _attributeName, _defaultString, AT_INT, offsetof( DestStructType_t, _varName ), sizeof( char ), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +#define DMXELEMENT_UNPACK_BITFIELD( _attributeName, _defaultString, _bitfieldType, _varName ) \ + { _attributeName, _defaultString, CDmAttributeInfo::AttributeType(), DestStructType_t::Get##_varName##ByteOffset(), DestStructType_t::Get##_varName##BitCount(), DestStructType_t::Get##_varName##BitOffset(), _bitfieldType, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, + +// NOTE: DMXELEMENT_UNPACK_FIELD_ARRAY is for fixed-size arrays, not pointers or CUtlVectors (TODO: doesn't work for strings or bitfields yet!) +#define DMXELEMENT_UNPACK_FIELD_ARRAY( _attributeName, _defaultString, _type, _varName ) \ + { _attributeName, _defaultString, CDmAttributeInfo>::AttributeType(), offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName[0]), NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, ARRAYSIZE( ((DestStructType_t *)0)->_varName ) }, + + +#define DMXELEMENT_UNPACK_FIELD_USERDATA( _attributeName, _defaultString, _type, _varName, _userData ) \ + { _attributeName, _defaultString, CDmAttributeInfo<_type>::AttributeType(), offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName), NO_BIT_OFFSET, NOT_A_BITFIELD, _userData, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +#define DMXELEMENT_UNPACK_FIELD_STRING_USERDATA( _attributeName, _defaultString, _varName, _userData ) \ + { _attributeName, _defaultString, AT_STRING, offsetof( DestStructType_t, _varName ), sizeof( ((DestStructType_t *)0)->_varName), NO_BIT_OFFSET, NOT_A_BITFIELD, _userData, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, +#define DMXELEMENT_UNPACK_FIELD_UTLSTRING_USERDATA( _attributeName, _defaultString, _varName, _userData ) \ + { _attributeName, _defaultString, AT_STRING, offsetof( DestStructType_t, _varName ), UTL_STRING_SIZE, NO_BIT_OFFSET, NOT_A_BITFIELD, _userData, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR, NOT_AN_ARRAY }, + +#define END_DMXELEMENT_UNPACK( _structName, _varName ) \ + { NULL, NULL, AT_UNKNOWN, 0, 0, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR } \ + }; \ + return unpack; \ + } \ + DmxElementUnpackStructure_t *_varName = _structName##_UnpackInit::s_pUnpack; + +#define END_DMXELEMENT_UNPACK_TEMPLATE( _structName, _varName ) \ + { NULL, NULL, AT_UNKNOWN, 0, 0, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR } \ + }; \ + return unpack; \ + } \ + template<> DmxElementUnpackStructure_t *_varName = _structName##_UnpackInit::s_pUnpack; + +// Macros for when your class is inside a namespace. +#define BEGIN_DMXELEMENT_NAMESPACE_UNPACK( _nameSpace, _structName ) \ + template DmxElementUnpackStructure_t *DmxElementUnpackInit(T *); \ + template <> DmxElementUnpackStructure_t *_nameSpace::DmxElementUnpackInit<_nameSpace::_structName>( _nameSpace::_structName * ); \ + namespace _structName##_UnpackInit \ + { \ + static DmxElementUnpackStructure_t *s_pUnpack = _nameSpace::DmxElementUnpackInit( (_nameSpace::_structName *)NULL ); \ + } \ + \ + template <> DmxElementUnpackStructure_t *_nameSpace::DmxElementUnpackInit<_nameSpace::_structName>( _nameSpace::_structName * ) \ + { \ + typedef _nameSpace::_structName DestStructType_t; \ + static DmxElementUnpackStructure_t unpack[] = \ + { \ + +//#define DECLARE_DMXELEMENT_UNPACK_NAMESPACE( _namespace ) \ +// template friend DmxElementUnpackStructure_t *DmxElementUnpackInit##_namespace(T *); + +// Adds serialization unpack structure and unpack func to your class. +#define DECLARE_DMXELEMENT_UNPACK_NAMESPACE( _namespace ) \ + template friend DmxElementUnpackStructure_t *DmxElementUnpackInit##_namespace(T *); \ + private: \ + static DmxElementUnpackStructure_t *s_pUnpackParams; \ + public: \ + virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const { return s_pUnpackParams; } \ + +// Use when your panel class is derived from another baseclass +#define BEGIN_DMXELEMENT_UNPACK_NAMESPACE_SIMPLE( _namespace, _structName ) \ + BEGIN_DMXELEMENT_UNPACK_NAMESPACE( _namespace, _structName ) \ + VGUI_UNPACK_BASEPANEL() \ + +// Use when your panel class has no base class +#define BEGIN_DMXELEMENT_UNPACK_NAMESPACE_SIMPLE_NO_BASE( _namespace, _structName ) \ + BEGIN_DMXELEMENT_UNPACK_NAMESPACE( _namespace, _structName ) \ + +#define BEGIN_DMXELEMENT_UNPACK_NAMESPACE( _namespace, _structName ) \ + template DmxElementUnpackStructure_t *_namespace::DmxElementUnpackInit##_namespace(T *); \ + template <> DmxElementUnpackStructure_t *_namespace::DmxElementUnpackInit##_namespace<_structName>( _namespace::_structName * ); \ + namespace _namespace##_structName##_UnpackInit \ + { \ + static DmxElementUnpackStructure_t *s_pUnpack = _namespace::DmxElementUnpackInit##_namespace( (_namespace::_structName *)NULL ); \ + } \ + \ + template <> DmxElementUnpackStructure_t *_namespace::DmxElementUnpackInit##_namespace<_structName>( _namespace::_structName * ) \ + { \ + typedef _structName DestStructType_t; \ + static DmxElementUnpackStructure_t unpack[] = \ + { \ + +// Use to end BEGIN_DMXELEMENT_UNPACK_NAMESPACE* macros +#define END_DMXELEMENT_UNPACK_NAMESPACE( _namespace, _structName, _varName ) \ + { NULL, NULL, AT_UNKNOWN, 0, 0, NO_BIT_OFFSET, NOT_A_BITFIELD, NO_USER_DATA, NO_EMBEDDED_TYPENAME, NO_EMBEDDED_STRUCT_PTR } \ + }; \ + return unpack; \ + } \ + DmxElementUnpackStructure_t *_structName::_varName = _namespace##_structName##_UnpackInit::s_pUnpack; \ + +#ifdef PLATFORM_POSIX +#define PRAGMA_DISABLE_4310 +#define PRAGMA_ENABLE_4310 +#else +#define PRAGMA_DISABLE_4310 __pragma(warning(disable:4310)) +#define PRAGMA_ENABLE_4310 __pragma(warning(default:4310)) +#endif + +#define DECLARE_DMXELEMENT_BITFIELD( _fieldName, _type, _structName ) \ + class CBitFieldInfo_##_fieldName \ + { \ + public: \ + CBitFieldInfo_##_fieldName () \ + { \ + int nSize = ( sizeof(_structName) + 3 ) & ~0x3; \ + unsigned char *pBuf = ( unsigned char * )stackalloc(nSize); \ + memset( pBuf, 0, nSize ); \ +PRAGMA_DISABLE_4310; \ + (( _structName * )pBuf)->_fieldName = (_type)(0xFFFFFFFF); \ +PRAGMA_ENABLE_4310; \ + _type *pTest = (_type *)pBuf; \ + for ( int i = 0; i < sizeof(_structName); ++i ) \ + { \ + if ( pTest[i] == 0 ) \ + continue; \ + \ + for ( int j = 0; j < 8*sizeof(_type); ++j ) \ + { \ + unsigned int temp = ((unsigned int)pTest[i]) & ( 1 << j ) ; \ + if ( temp == 0 ) \ + continue; \ + \ + m_nByteOffset = i*sizeof(_type) + j / 8; \ + m_nBitOffset = j & 0x7; \ + \ + int k; \ + for ( k = j+1; k < 8*sizeof(_type); ++k ) \ + { \ + unsigned int temp = ((unsigned int)pTest[i]) & ( 1 << k ) ; \ + if ( temp != 0 ) \ + continue; \ + break; \ + } \ + m_nBitCount = k - j; \ + break; \ + } \ + break; \ + } \ + } \ + \ + int GetByteOffset() const \ + { \ + return m_nByteOffset; \ + } \ + \ + int GetBitCount() const \ + { \ + return m_nBitCount; \ + } \ + \ + int GetBitOffset() const \ + { \ + return m_nBitOffset; \ + } \ + \ + private: \ + int m_nByteOffset; \ + int m_nBitCount; \ + int m_nBitOffset; \ + }; \ + \ + static int Get##_fieldName##BitCount() \ + { \ + CBitFieldInfo_##_fieldName info; \ + return info.GetBitCount(); \ + } \ + \ + static int Get##_fieldName##ByteOffset() \ + { \ + CBitFieldInfo_##_fieldName info; \ + return info.GetByteOffset(); \ + } \ + \ + static int Get##_fieldName##BitOffset() \ + { \ + CBitFieldInfo_##_fieldName info; \ + return info.GetBitOffset(); \ + } \ + friend class CBitFieldInfo_##_fieldName; \ + + +// A bit of a hack, but we don't have access to +extern CUtlSymbolTableLargeMT g_DmxAttributeStrings; + +//----------------------------------------------------------------------------- +// Element used to read dmx files from mod code. Similar to keyvalues. +//----------------------------------------------------------------------------- +class CDmxElement +{ + DECLARE_DMX_ALLOCATOR( ); + +public: + bool HasAttribute( const char *pAttributeName ) const; + CDmxAttribute *GetAttribute( const char *pAttributeName ); + const CDmxAttribute *GetAttribute( const char *pAttributeName ) const; + int AttributeCount() const; + CDmxAttribute *GetAttribute( int nIndex ); + const CDmxAttribute *GetAttribute( int nIndex ) const; + CUtlSymbolLarge GetType() const; + const char* GetTypeString() const; + const char* GetName() const; + const DmObjectId_t &GetId() const; + + // Add+remove+rename can only occur during lock + // NOTE: AddAttribute will find or add; returning an existing attribute if + // one with the appropriate name exists + void LockForChanges( bool bLock ); + CDmxAttribute *AddAttribute( const char *pAttributeName ); + void RemoveAttribute( const char *pAttributeName ); + void RemoveAttributeByPtr( CDmxAttribute *pAttribute ); + void RemoveAllAttributes(); + void RenameAttribute( const char *pAttributeName, const char *pNewName ); + + // Simple methods to read attributes + const char *GetValueString( const char *pAttributeName ) const; + template< class T > const T& GetValue( const char *pAttributeName ) const; + template< class T > const T& GetValue( const char *pAttributeName, const T& defaultValue ) const; + + template< class T > const CUtlVector& GetArray( const char *pAttributeName ) const; + template< class T > const CUtlVector& GetArray( const char *pAttributeName, const CUtlVector& defaultValue ) const; + + // Set methods + void SetName( const char *pName ); + CDmxAttribute* SetValue( const char *pAttributeName, const char *pString ); + CDmxAttribute* SetValue( const char *pAttributeName, void *pBuffer, int nLen ); + template< class T > CDmxAttribute* SetValue( const char *pAttributeName, const T& value ); + + // Method to unpack data into a structure + void UnpackIntoStructure( void *pData, const DmxElementUnpackStructure_t *pUnpack ) const; + + // Creates attributes based on the unpack structure + void AddAttributesFromStructure( const void *pData, const DmxElementUnpackStructure_t *pUnpack ); + +private: + typedef CUtlSortVector< CDmxAttribute*, CDmxAttributeLess > AttributeList_t; + + CDmxElement( const char *pType ); + ~CDmxElement(); + + // Removes all elements recursively + void RemoveAllElementsRecursive(); + + // Adds elements to delete to the deletion list + void AddElementsToDelete( CUtlVector< CDmxElement * >& elementsToDelete ); + + // Sorts the vector when a change has occurred + void Resort( ) const; + + // Finds an attribute by name + int FindAttribute( const char *pAttributeName ) const; + int FindAttribute( CUtlSymbolLarge attributeName ) const; + + // Sets the object id + void SetId( const DmObjectId_t &id ); + + // Are we locked? + bool IsLocked() const; + + template void UnpackBitfield( T *pDest2, const DmxElementUnpackStructure_t *pUnpack, const CDmxAttribute *pAttribute ) const; + + AttributeList_t m_Attributes; + DmObjectId_t m_Id; // We need this strictly because we support serialization + CUtlSymbolLarge m_Type; + char m_nLockCount; + mutable bool m_bResortNeeded : 1; + bool m_bIsMarkedForDeletion : 1; + + static CUtlSymbolTableLargeMT s_TypeSymbols; + + friend class CDmxSerializer; + friend class CDmxSerializerKeyValues2; + friend void CleanupDMX( CDmxElement* pElement ); + friend CDmxElement* CreateDmxElement( const char *pType ); +}; + + +//----------------------------------------------------------------------------- +// inline methods +//----------------------------------------------------------------------------- + +// Are we locked? +inline bool CDmxElement::IsLocked() const +{ + return m_nLockCount > 0; +} + +inline const char *CDmxElement::GetValueString( const char *pAttributeName ) const +{ + const CDmxAttribute* pAttribute = GetAttribute( pAttributeName ); + if ( pAttribute ) + return pAttribute->GetValueString(); + return ""; +} + +template< class T > +inline const T& CDmxElement::GetValue( const char *pAttributeName ) const +{ + const CDmxAttribute* pAttribute = GetAttribute( pAttributeName ); + if ( pAttribute ) + return pAttribute->GetValue(); + + static T defaultValue; + CDmAttributeInfo::SetDefaultValue( defaultValue ); + return defaultValue; +} + +template< class T > +inline const T& CDmxElement::GetValue( const char *pAttributeName, const T& defaultValue ) const +{ + const CDmxAttribute* pAttribute = GetAttribute( pAttributeName ); + if ( pAttribute ) + return pAttribute->GetValue(); + return defaultValue; +} + +template< class T > +inline const CUtlVector& CDmxElement::GetArray( const char *pAttributeName ) const +{ + const CDmxAttribute* pAttribute = GetAttribute( pAttributeName ); + if ( pAttribute ) + return pAttribute->GetArray(); + + static CUtlVector defaultValue; + return defaultValue; +} + +template< class T > +inline const CUtlVector& CDmxElement::GetArray( const char *pAttributeName, const CUtlVector& defaultValue ) const +{ + const CDmxAttribute* pAttribute = GetAttribute( pAttributeName ); + if ( pAttribute ) + return pAttribute->GetArray(); + return defaultValue; +} + + +//----------------------------------------------------------------------------- +// Creates a dmx element +//----------------------------------------------------------------------------- +CDmxElement* CreateDmxElement( const char *pType ); + + +//----------------------------------------------------------------------------- +// Helper class to lock elements for changes +//----------------------------------------------------------------------------- +class CDmxElementModifyScope +{ +public: + CDmxElementModifyScope( CDmxElement *pElement ) : m_pElement( pElement ) + { + m_pElement->LockForChanges( true ); + } + ~CDmxElementModifyScope() + { + Release(); + } + void Release() + { + if ( m_pElement ) + { + m_pElement->LockForChanges( false ); + m_pElement = NULL; + } + } +private: + CDmxElement *m_pElement; +}; + + +//----------------------------------------------------------------------------- +// Set methods +//----------------------------------------------------------------------------- +inline CDmxAttribute* CDmxElement::SetValue( const char *pAttributeName, const char *pString ) +{ + CDmxElementModifyScope modify( this ); + CDmxAttribute *pAttribute = AddAttribute( pAttributeName ); + pAttribute->SetValue( pString ); + return pAttribute; +} + +inline CDmxAttribute* CDmxElement::SetValue( const char *pAttributeName, void *pBuffer, int nLen ) +{ + CDmxElementModifyScope modify( this ); + CDmxAttribute *pAttribute = AddAttribute( pAttributeName ); + pAttribute->SetValue( pBuffer, nLen ); + return pAttribute; +} + +template< class T > +inline CDmxAttribute* CDmxElement::SetValue( const char *pAttributeName, const T& value ) +{ + CDmxElementModifyScope modify( this ); + CDmxAttribute *pAttribute = AddAttribute( pAttributeName ); + pAttribute->SetValue( value ); + return pAttribute; +} + + +#endif // DMXELEMENT_H diff --git a/public/dmxloader/dmxloader.h b/public/dmxloader/dmxloader.h new file mode 100644 index 0000000..8c1b46d --- /dev/null +++ b/public/dmxloader/dmxloader.h @@ -0,0 +1,72 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef DMXLOADER_H +#define DMXLOADER_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; +class CDmxElement; + + +//----------------------------------------------------------------------------- +// Serialization/Unserialization +//----------------------------------------------------------------------------- +bool SerializeDMX( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName = NULL ); +bool SerializeDMX( const char *pFileName, const char *pPathID, bool bTextMode, CDmxElement *pRoot ); + +bool UnserializeDMX( CUtlBuffer &buf, CDmxElement **ppRoot, const char *pFileName = NULL ); +bool UnserializeDMX( const char *pFileName, const char *pPathID, bool bTextMode, CDmxElement **ppRoot ); + +//----------------------------------------------------------------------------- +// DMX elements/attributes can only be accessed inside a dmx context +//----------------------------------------------------------------------------- +void BeginDMXContext( ); +void EndDMXContext( bool bDecommitMemory ); +void DecommitDMXMemory(); + + +//----------------------------------------------------------------------------- +// Helper macro +//----------------------------------------------------------------------------- +class CDMXContextHelper +{ +public: + CDMXContextHelper( bool bDecommitMemory ) { m_bDecommitMemory = bDecommitMemory; BeginDMXContext(); } + ~CDMXContextHelper() { EndDMXContext( m_bDecommitMemory ); } + +private: + bool m_bDecommitMemory; +}; + +#define DECLARE_DMX_CONTEXT( ) CDMXContextHelper __dmxContextHelper( true ); +#define DECLARE_DMX_CONTEXT_NODECOMMIT( ) CDMXContextHelper __dmxContextHelper( false ); +#define DECLARE_DMX_CONTEXT_DECOMMIT( _decommit ) CDMXContextHelper __dmxContextHelper( _decommit ); + + +//----------------------------------------------------------------------------- +// Used for allocation. All will be freed when we leave the DMX context +//----------------------------------------------------------------------------- +void* DMXAlloc( size_t size ); + + +//----------------------------------------------------------------------------- +// Helper macro +//----------------------------------------------------------------------------- +#define DECLARE_DMX_ALLOCATOR( ) \ + public: \ + inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_( "DMXAlloc" ); return DMXAlloc(size); } \ + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_( "DMXAlloc" ); return DMXAlloc(size); } \ + inline void operator delete( void* p ) { } \ + inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { } \ + +#endif // DMXLOADER_H diff --git a/public/dt_common.h b/public/dt_common.h new file mode 100644 index 0000000..6288b5c --- /dev/null +++ b/public/dt_common.h @@ -0,0 +1,213 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef DATATABLE_COMMON_H +#define DATATABLE_COMMON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include + +#ifdef LINUX +#undef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + +// Max number of properties in a datatable and its children. +#define MAX_DATATABLES 1024 // must be a power of 2. +#define MAX_DATATABLE_PROPS 4096 + +#define MAX_ARRAY_ELEMENTS 2048 // a network array should have more that 1024 elements + +#define HIGH_DEFAULT -121121.121121f + +#define BITS_FULLRES -1 // Use the full resolution of the type being encoded. +#define BITS_WORLDCOORD -2 // Encode as a world coordinate. + +#define DT_MAX_STRING_BITS 9 +#define DT_MAX_STRING_BUFFERSIZE (1<varName ) + +// Gets the size of a variable in a class. +#define PROPSIZEOF(className, varName) sizeof(((className*)0)->varName) + + +// SendProp::m_Flags. +#define SPROP_UNSIGNED (1<<0) // Unsigned integer data. + +#define SPROP_COORD (1<<1) // If this is set, the float/vector is treated like a world coordinate. + // Note that the bit count is ignored in this case. + +#define SPROP_NOSCALE (1<<2) // For floating point, don't scale into range, just take value as is. + +#define SPROP_ROUNDDOWN (1<<3) // For floating point, limit high value to range minus one bit unit + +#define SPROP_ROUNDUP (1<<4) // For floating point, limit low value to range minus one bit unit + +#define SPROP_NORMAL (1<<5) // If this is set, the vector is treated like a normal (only valid for vectors) + +#define SPROP_EXCLUDE (1<<6) // This is an exclude prop (not excludED, but it points at another prop to be excluded). + +#define SPROP_XYZE (1<<7) // Use XYZ/Exponent encoding for vectors. + +#define SPROP_INSIDEARRAY (1<<8) // This tells us that the property is inside an array, so it shouldn't be put into the + // flattened property list. Its array will point at it when it needs to. + +#define SPROP_PROXY_ALWAYS_YES (1<<9) // Set for datatable props using one of the default datatable proxies like + // SendProxy_DataTableToDataTable that always send the data to all clients. + +#define SPROP_IS_A_VECTOR_ELEM (1<<10) // Set automatically if SPROP_VECTORELEM is used. + +#define SPROP_COLLAPSIBLE (1<<11) // Set automatically if it's a datatable with an offset of 0 that doesn't change the pointer + // (ie: for all automatically-chained base classes). + // In this case, it can get rid of this SendPropDataTable altogether and spare the + // trouble of walking the hierarchy more than necessary. + +#define SPROP_COORD_MP (1<<12) // Like SPROP_COORD, but special handling for multiplayer games +#define SPROP_COORD_MP_LOWPRECISION (1<<13) // Like SPROP_COORD, but special handling for multiplayer games where the fractional component only gets a 3 bits instead of 5 +#define SPROP_COORD_MP_INTEGRAL (1<<14) // SPROP_COORD_MP, but coordinates are rounded to integral boundaries +#define SPROP_CELL_COORD (1<<15) // Like SPROP_COORD, but special encoding for cell coordinates that can't be negative, bit count indicate maximum value +#define SPROP_CELL_COORD_LOWPRECISION (1<<16) // Like SPROP_CELL_COORD, but special handling where the fractional component only gets a 3 bits instead of 5 +#define SPROP_CELL_COORD_INTEGRAL (1<<17) // SPROP_CELL_COORD, but coordinates are rounded to integral boundaries + +#define SPROP_CHANGES_OFTEN (1<<18) // this is an often changed field, moved to head of sendtable so it gets a small index + +#define SPROP_NUMFLAGBITS_NETWORKED 19 + + +// This is server side only, it's used to mark properties whose SendProxy_* functions encode against gpGlobals->tickcount (the only ones that currently do this are +// m_flAnimTime and m_flSimulationTime. MODs shouldn't need to mess with this probably +#define SPROP_ENCODED_AGAINST_TICKCOUNT (1<<19) + +// See SPROP_NUMFLAGBITS_NETWORKED for the ones which are networked +#define SPROP_NUMFLAGBITS 20 + +// Used by the SendProp and RecvProp functions to disable debug checks on type sizes. +#define SIZEOF_IGNORE -1 + + +// Use this to extern send and receive datatables, and reference them. +#define EXTERN_SEND_TABLE(tableName) namespace tableName {extern SendTable g_SendTable;} +#define EXTERN_RECV_TABLE(tableName) namespace tableName {extern RecvTable g_RecvTable;} + +#define REFERENCE_SEND_TABLE(tableName) tableName::g_SendTable +#define REFERENCE_RECV_TABLE(tableName) tableName::g_RecvTable + + +class SendProp; + + +typedef enum +{ + DPT_Int=0, + DPT_Float, + DPT_Vector, + DPT_VectorXY, // Only encodes the XY of a vector, ignores Z + DPT_String, + DPT_Array, // An array of the base types (can't be of datatables). + DPT_DataTable, +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + DPT_Quaternion, +#endif + DPT_Int64, + DPT_NUMSendPropTypes +} SendPropType; + + +class DVariant +{ +public: + DVariant() {m_Type = DPT_Float;} + DVariant(float val) {m_Type = DPT_Float; m_Float = val;} + + const char *ToString() + { + static char text[128]; + + switch ( m_Type ) + { + case DPT_Int : + Q_snprintf( text, sizeof(text), "%i", m_Int ); + break; + case DPT_Float : + Q_snprintf( text, sizeof(text), "%.3f", m_Float ); + break; + case DPT_Vector : + Q_snprintf( text, sizeof(text), "(%.3f,%.3f,%.3f)", + m_Vector[0], m_Vector[1], m_Vector[2] ); + break; + case DPT_VectorXY : + Q_snprintf( text, sizeof(text), "(%.3f,%.3f)", + m_Vector[0], m_Vector[1] ); + break; +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + case DPT_Quaternion : + Q_snprintf( text, sizeof(text), "(%.3f,%.3f,%.3f %.3f)", + m_Vector[0], m_Vector[1], m_Vector[2], m_Vector[3] ); + break; +#endif + case DPT_String : + if ( m_pString ) + return m_pString; + else + return "NULL"; + break; + case DPT_Array : + Q_snprintf( text, sizeof(text), "Array" ); + break; + case DPT_DataTable : + Q_snprintf( text, sizeof(text), "DataTable" ); + break; + case DPT_Int64: + Q_snprintf( text, sizeof(text), "%I64d", m_Int64 ); + break; + default : + Q_snprintf( text, sizeof(text), "DVariant type %i unknown", m_Type ); + break; + } + + return text; + } + + union + { + float m_Float; + long m_Int; + char *m_pString; + void *m_pData; // For DataTables. +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + float m_Vector[4]; +#else + float m_Vector[3]; +#endif + int64 m_Int64; + }; + SendPropType m_Type; +}; + + +// This can be used to set the # of bits used to transmit a number between 0 and nMaxElements-1. +inline int NumBitsForCount( int nMaxElements ) +{ + int nBits = 0; + while ( nMaxElements > 0 ) + { + ++nBits; + nMaxElements >>= 1; + } + return nBits; +} + + +#endif // DATATABLE_COMMON_H diff --git a/public/dt_recv.cpp b/public/dt_recv.cpp new file mode 100644 index 0000000..d7c94da --- /dev/null +++ b/public/dt_recv.cpp @@ -0,0 +1,530 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "dt_recv.h" +#include "mathlib/vector.h" +#include "tier1/strtools.h" +#include "dt_utlvector_common.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL) + +char *s_ClientElementNames[MAX_ARRAY_ELEMENTS] = +{ + "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", + "010", "011", "012", "013", "014", "015", "016", "017", "018", "019", + "020", "021", "022", "023", "024", "025", "026", "027", "028", "029", + "030", "031", "032", "033", "034", "035", "036", "037", "038", "039", + "040", "041", "042", "043", "044", "045", "046", "047", "048", "049", + "050", "051", "052", "053", "054", "055", "056", "057", "058", "059", + "060", "061", "062", "063", "064", "065", "066", "067", "068", "069", + "070", "071", "072", "073", "074", "075", "076", "077", "078", "079", + "080", "081", "082", "083", "084", "085", "086", "087", "088", "089", + "090", "091", "092", "093", "094", "095", "096", "097", "098", "099", + "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", + "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", + "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", + "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", + "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", + "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", + "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", + "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", + "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", + "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", + "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", + "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", + "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", + "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", + "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", + "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", + "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", + "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", + "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", + "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", + "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", + "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", + "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", + "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", + "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", + "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", + "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", + "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", + "510", "511", "512", "513", "514", "515", "516", "517", "518", "519", + "520", "521", "522", "523", "524", "525", "526", "527", "528", "529", + "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", + "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", + "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", + "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", + "570", "571", "572", "573", "574", "575", "576", "577", "578", "579", + "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", + "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", + "600", "601", "602", "603", "604", "605", "606", "607", "608", "609", + "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", + "620", "621", "622", "623", "624", "625", "626", "627", "628", "629", + "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", + "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", + "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", + "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", + "670", "671", "672", "673", "674", "675", "676", "677", "678", "679", + "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", + "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", + "700", "701", "702", "703", "704", "705", "706", "707", "708", "709", + "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", + "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", + "730", "731", "732", "733", "734", "735", "736", "737", "738", "739", + "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", + "750", "751", "752", "753", "754", "755", "756", "757", "758", "759", + "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", + "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", + "780", "781", "782", "783", "784", "785", "786", "787", "788", "789", + "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", + "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", + "810", "811", "812", "813", "814", "815", "816", "817", "818", "819", + "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", + "830", "831", "832", "833", "834", "835", "836", "837", "838", "839", + "840", "841", "842", "843", "844", "845", "846", "847", "848", "849", + "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", + "860", "861", "862", "863", "864", "865", "866", "867", "868", "869", + "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", + "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", + "890", "891", "892", "893", "894", "895", "896", "897", "898", "899", + "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", + "910", "911", "912", "913", "914", "915", "916", "917", "918", "919", + "920", "921", "922", "923", "924", "925", "926", "927", "928", "929", + "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", + "940", "941", "942", "943", "944", "945", "946", "947", "948", "949", + "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", + "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", + "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", + "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", + "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", + "1000", "1001", "1002", "1003", "1004", "1005", "1006", "1007", "1008", "1009", + "1010", "1011", "1012", "1013", "1014", "1015", "1016", "1017", "1018", "1019", + "1020", "1021", "1022", "1023" + +}; + +CStandardRecvProxies::CStandardRecvProxies() +{ + m_Int32ToInt8 = RecvProxy_Int32ToInt8; + m_Int32ToInt16 = RecvProxy_Int32ToInt16; + m_Int32ToInt32 = RecvProxy_Int32ToInt32; + m_Int64ToInt64 = RecvProxy_Int64ToInt64; + m_FloatToFloat = RecvProxy_FloatToFloat; + m_VectorToVector = RecvProxy_VectorToVector; +} + +CStandardRecvProxies g_StandardRecvProxies; + + +// ---------------------------------------------------------------------- // +// RecvProp. +// ---------------------------------------------------------------------- // +RecvProp::RecvProp() +{ + m_pExtraData = NULL; + m_pVarName = NULL; + m_Offset = 0; + m_RecvType = DPT_Int; + m_Flags = 0; + m_ProxyFn = NULL; + m_DataTableProxyFn = NULL; + m_pDataTable = NULL; + m_nElements = 1; + m_ElementStride = -1; + m_pArrayProp = NULL; + m_ArrayLengthProxy = NULL; + m_bInsideArray = false; +} + +// ---------------------------------------------------------------------- // +// RecvTable. +// ---------------------------------------------------------------------- // +RecvTable::RecvTable() +{ + Construct( NULL, 0, NULL ); +} + +RecvTable::RecvTable(RecvProp *pProps, int nProps, char *pNetTableName) +{ + Construct( pProps, nProps, pNetTableName ); +} + +RecvTable::~RecvTable() +{ +} + +void RecvTable::Construct( RecvProp *pProps, int nProps, char *pNetTableName ) +{ + m_pProps = pProps; + m_nProps = nProps; + m_pDecoder = NULL; + m_pNetTableName = pNetTableName; + m_bInitialized = false; + m_bInMainList = false; +} + + +// ---------------------------------------------------------------------- // +// Prop setup functions (for building tables). +// ---------------------------------------------------------------------- // + +RecvProp RecvPropFloat( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + +#ifdef _DEBUG + if ( varProxy == RecvProxy_FloatToFloat ) + { + Assert( sizeofVar == 0 || sizeofVar == 4 ); + } +#endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Float; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropVector( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + +#ifdef _DEBUG + if ( varProxy == RecvProxy_VectorToVector ) + { + Assert( sizeofVar == sizeof( Vector ) ); + } +#endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Vector; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropVectorXY( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + +#ifdef _DEBUG + if ( varProxy == RecvProxy_VectorToVector ) + { + Assert( sizeofVar == sizeof( Vector ) ); + } +#endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_VectorXY; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + +RecvProp RecvPropQuaternion( + char *pVarName, + int offset, + int sizeofVar, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + +#ifdef _DEBUG + if ( varProxy == RecvProxy_QuaternionToQuaternion ) + { + Assert( sizeofVar == sizeof( Quaternion ) ); + } +#endif + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_Quaternion; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} +#endif + +RecvProp RecvPropInt( + char *pVarName, + int offset, + int sizeofVar, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + // If they didn't specify a proxy, then figure out what type we're writing to. + if (varProxy == NULL) + { + if (sizeofVar == 1) + { + varProxy = RecvProxy_Int32ToInt8; + } + else if (sizeofVar == 2) + { + varProxy = RecvProxy_Int32ToInt16; + } + else if (sizeofVar == 4) + { + varProxy = RecvProxy_Int32ToInt32; + } + else if (sizeofVar == 8) + { + varProxy = RecvProxy_Int64ToInt64; + } + else + { + Assert(!"RecvPropInt var has invalid size"); + varProxy = RecvProxy_Int32ToInt8; // safest one... + } + } + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = (sizeofVar == 8) ? DPT_Int64 : DPT_Int; + ret.m_Flags = flags; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropString( + char *pVarName, + int offset, + int bufferSize, + int flags, + RecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_String; + ret.m_Flags = flags; + ret.m_StringBufferSize = bufferSize; + ret.SetProxyFn( varProxy ); + + return ret; +} + +RecvProp RecvPropDataTable( + char *pVarName, + int offset, + int flags, + RecvTable *pTable, + DataTableRecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_DataTable; + ret.m_Flags = flags; + ret.SetDataTableProxyFn( varProxy ); + ret.SetDataTable( pTable ); + + return ret; +} + +RecvProp RecvPropArray3( + char *pVarName, + int offset, + int sizeofVar, + int elements, + RecvProp pArrayProp, + DataTableRecvVarProxyFn varProxy + ) +{ + RecvProp ret; + + Assert( elements <= MAX_ARRAY_ELEMENTS ); + + ret.m_pVarName = pVarName; + ret.SetOffset( offset ); + ret.m_RecvType = DPT_DataTable; + ret.SetDataTableProxyFn( varProxy ); + + RecvProp *pProps = new RecvProp[elements]; // TODO free that again + + const char *pParentArrayPropName = AllocateStringHelper( "%s", pVarName ); + + for ( int i=0; i < elements; i++ ) + { + pProps[i] = pArrayProp; // copy basic property settings + pProps[i].SetOffset( i * sizeofVar ); // adjust offset + pProps[i].m_pVarName = s_ClientElementNames[i]; // give unique name + pProps[i].SetParentArrayPropName( pParentArrayPropName ); // For debugging... + } + + RecvTable *pTable = new RecvTable( pProps, elements, pVarName ); // TODO free that again + + ret.SetDataTable( pTable ); + + return ret; +} + +RecvProp InternalRecvPropArray( + const int elementCount, + const int elementStride, + char *pName, + ArrayLengthRecvProxyFn proxy + ) +{ + RecvProp ret; + + ret.InitArray( elementCount, elementStride ); + ret.m_pVarName = pName; + ret.SetArrayLengthProxy( proxy ); + + return ret; +} + + +// ---------------------------------------------------------------------- // +// Proxies. +// ---------------------------------------------------------------------- // + +void RecvProxy_FloatToFloat( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + Assert( IsFinite( pData->m_Value.m_Float ) ); + *((float*)pOut) = pData->m_Value.m_Float; +} + +void RecvProxy_VectorToVector( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const float *v = pData->m_Value.m_Vector; + + Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) ); + ((float*)pOut)[0] = v[0]; + ((float*)pOut)[1] = v[1]; + ((float*)pOut)[2] = v[2]; +} + +void RecvProxy_VectorXYToVectorXY( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const float *v = pData->m_Value.m_Vector; + + Assert( IsFinite( v[0] ) && IsFinite( v[1] ) ); + ((float*)pOut)[0] = v[0]; + ((float*)pOut)[1] = v[1]; +} + +void RecvProxy_QuaternionToQuaternion( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + const float *v = pData->m_Value.m_Vector; + + Assert( IsFinite( v[0] ) && IsFinite( v[1] ) && IsFinite( v[2] ) && IsFinite( v[3] ) ); + ((float*)pOut)[0] = v[0]; + ((float*)pOut)[1] = v[1]; + ((float*)pOut)[2] = v[2]; + ((float*)pOut)[3] = v[3]; +} + +void RecvProxy_Int32ToInt8( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((unsigned char*)pOut) = (unsigned char)pData->m_Value.m_Int; +} + +void RecvProxy_Int32ToInt16( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((unsigned short*)pOut) = (unsigned short)pData->m_Value.m_Int; +} + +void RecvProxy_Int32ToInt32( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((unsigned long*)pOut) = (unsigned long)pData->m_Value.m_Int; +} + +void RecvProxy_Int32ToColor32( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + //Always send/receive as little endian to preserve byte order across network byte swaps + *((uint32*)pOut) = LittleDWord((uint32)pData->m_Value.m_Int); +} + +void RecvProxy_Int64ToInt64( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + *((int64*)pOut) = (int64)pData->m_Value.m_Int64; +} + +void RecvProxy_StringToString( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + char *pStrOut = (char*)pOut; + if ( pData->m_pRecvProp->m_StringBufferSize <= 0 ) + { + return; + } + + for ( int i=0; i < pData->m_pRecvProp->m_StringBufferSize; i++ ) + { + pStrOut[i] = pData->m_Value.m_pString[i]; + if ( pStrOut[i] == 0 ) + break; + } + + pStrOut[pData->m_pRecvProp->m_StringBufferSize-1] = 0; +} + +void DataTableRecvProxy_StaticDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + *pOut = pData; +} + +void DataTableRecvProxy_PointerDataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + *pOut = *((void**)pData); +} + +#endif diff --git a/public/dt_recv.h b/public/dt_recv.h new file mode 100644 index 0000000..7838898 --- /dev/null +++ b/public/dt_recv.h @@ -0,0 +1,584 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef DATATABLE_RECV_H +#define DATATABLE_RECV_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "dt_common.h" +#include "tier0/dbg.h" + + +#define ADDRESSPROXY_NONE -1 + + +class RecvTable; +class RecvProp; + + +// This is passed into RecvProxy functions. +class CRecvProxyData +{ +public: + const RecvProp *m_pRecvProp; // The property it's receiving. + + DVariant m_Value; // The value given to you to store. + + int m_iElement; // Which array element you're getting. + + int m_ObjectID; // The object being referred to. +}; + + +//----------------------------------------------------------------------------- +// pStruct = the base structure of the datatable this variable is in (like C_BaseEntity) +// pOut = the variable that this this proxy represents (like C_BaseEntity::m_SomeValue). +// +// Convert the network-standard-type value in m_Value into your own format in pStruct/pOut. +//----------------------------------------------------------------------------- +typedef void (*RecvVarProxyFn)( const CRecvProxyData *pData, void *pStruct, void *pOut ); + +// ------------------------------------------------------------------------ // +// ArrayLengthRecvProxies are optionally used to get the length of the +// incoming array when it changes. +// ------------------------------------------------------------------------ // +typedef void (*ArrayLengthRecvProxyFn)( void *pStruct, int objectID, int currentArrayLength ); + + +// NOTE: DataTable receive proxies work differently than the other proxies. +// pData points at the object + the recv table's offset. +// pOut should be set to the location of the object to unpack the data table into. +// If the parent object just contains the child object, the default proxy just does *pOut = pData. +// If the parent object points at the child object, you need to dereference the pointer here. +// NOTE: don't ever return null from a DataTable receive proxy function. Bad things will happen. +typedef void (*DataTableRecvVarProxyFn)(const RecvProp *pProp, void **pOut, void *pData, int objectID); + + +// This is used to fork over the standard proxy functions to the engine so it can +// make some optimizations. +class CStandardRecvProxies +{ +public: + CStandardRecvProxies(); + + RecvVarProxyFn m_Int32ToInt8; + RecvVarProxyFn m_Int32ToInt16; + RecvVarProxyFn m_Int32ToInt32; + RecvVarProxyFn m_Int64ToInt64; + RecvVarProxyFn m_FloatToFloat; + RecvVarProxyFn m_VectorToVector; +}; +extern CStandardRecvProxies g_StandardRecvProxies; + + +class CRecvDecoder; + + +class RecvProp +{ +// This info comes from the receive data table. +public: + RecvProp(); + + void InitArray( int nElements, int elementStride ); + + int GetNumElements() const; + void SetNumElements( int nElements ); + + int GetElementStride() const; + void SetElementStride( int stride ); + + int GetFlags() const; + + const char* GetName() const; + SendPropType GetType() const; + + RecvTable* GetDataTable() const; + void SetDataTable( RecvTable *pTable ); + + RecvVarProxyFn GetProxyFn() const; + void SetProxyFn( RecvVarProxyFn fn ); + + DataTableRecvVarProxyFn GetDataTableProxyFn() const; + void SetDataTableProxyFn( DataTableRecvVarProxyFn fn ); + + int GetOffset() const; + void SetOffset( int o ); + + // Arrays only. + RecvProp* GetArrayProp() const; + void SetArrayProp( RecvProp *pProp ); + + // Arrays only. + void SetArrayLengthProxy( ArrayLengthRecvProxyFn proxy ); + ArrayLengthRecvProxyFn GetArrayLengthProxy() const; + + bool IsInsideArray() const; + void SetInsideArray(); + + // Some property types bind more data to the prop in here. + const void* GetExtraData() const; + void SetExtraData( const void *pData ); + + // If it's one of the numbered "000", "001", etc properties in an array, then + // these can be used to get its array property name for debugging. + const char* GetParentArrayPropName(); + void SetParentArrayPropName( const char *pArrayPropName ); + +public: + + char *m_pVarName; + SendPropType m_RecvType; + int m_Flags; + int m_StringBufferSize; + + +private: + + bool m_bInsideArray; // Set to true by the engine if this property sits inside an array. + + // Extra data that certain special property types bind to the property here. + const void *m_pExtraData; + + // If this is an array (DPT_Array). + RecvProp *m_pArrayProp; + ArrayLengthRecvProxyFn m_ArrayLengthProxy; + + RecvVarProxyFn m_ProxyFn; + DataTableRecvVarProxyFn m_DataTableProxyFn; // For RDT_DataTable. + + RecvTable *m_pDataTable; // For RDT_DataTable. + int m_Offset; + + int m_ElementStride; + int m_nElements; + + // If it's one of the numbered "000", "001", etc properties in an array, then + // these can be used to get its array property name for debugging. + const char *m_pParentArrayPropName; +}; + + +class RecvTable +{ +public: + + typedef RecvProp PropType; + + RecvTable(); + RecvTable( RecvProp *pProps, int nProps, char *pNetTableName ); + ~RecvTable(); + + void Construct( RecvProp *pProps, int nProps, char *pNetTableName ); + + int GetNumProps(); + RecvProp* GetProp( int i ); + + const char* GetName(); + + // Used by the engine while initializing array props. + void SetInitialized( bool bInitialized ); + bool IsInitialized() const; + + // Used by the engine. + void SetInMainList( bool bInList ); + bool IsInMainList() const; + + +public: + + // Properties described in a table. + RecvProp *m_pProps; + int m_nProps; + + // The decoder. NOTE: this covers each RecvTable AND all its children (ie: its children + // will have their own decoders that include props for all their children). + CRecvDecoder *m_pDecoder; + + char *m_pNetTableName; // The name matched between client and server. + + +private: + + bool m_bInitialized; + bool m_bInMainList; +}; + + +inline int RecvTable::GetNumProps() +{ + return m_nProps; +} + +inline RecvProp* RecvTable::GetProp( int i ) +{ + Assert( i >= 0 && i < m_nProps ); + return &m_pProps[i]; +} + +inline const char* RecvTable::GetName() +{ + return m_pNetTableName; +} + +inline void RecvTable::SetInitialized( bool bInitialized ) +{ + m_bInitialized = bInitialized; +} + +inline bool RecvTable::IsInitialized() const +{ + return m_bInitialized; +} + +inline void RecvTable::SetInMainList( bool bInList ) +{ + m_bInMainList = bInList; +} + +inline bool RecvTable::IsInMainList() const +{ + return m_bInMainList; +} + + +// ------------------------------------------------------------------------------------------------------ // +// See notes on BEGIN_SEND_TABLE for a description. These macros work similarly. +// ------------------------------------------------------------------------------------------------------ // +#define BEGIN_RECV_TABLE(className, tableName) \ + BEGIN_RECV_TABLE_NOBASE(className, tableName) \ + RecvPropDataTable("baseclass", 0, 0, className::BaseClass::m_pClassRecvTable, DataTableRecvProxy_StaticDataTable), + +#define BEGIN_RECV_TABLE_NOBASE(className, tableName) \ + template int ClientClassInit(T *); \ + namespace tableName { \ + struct ignored; \ + } \ + template <> int ClientClassInit(tableName::ignored *); \ + namespace tableName { \ + RecvTable g_RecvTable; \ + int g_RecvTableInit = ClientClassInit((tableName::ignored *)NULL); \ + } \ + template <> int ClientClassInit(tableName::ignored *) \ + { \ + typedef className currentRecvDTClass; \ + char *pRecvTableName = #tableName; \ + RecvTable &RecvTable = tableName::g_RecvTable; \ + static RecvProp RecvProps[] = { \ + RecvPropInt("should_never_see_this", 0, sizeof(int)), // It adds a dummy property at the start so you can define "empty" SendTables. + +#define END_RECV_TABLE() \ + }; \ + RecvTable.Construct(RecvProps+1, sizeof(RecvProps) / sizeof(RecvProp) - 1, pRecvTableName); \ + return 1; \ + } + + +#define RECVINFO(varName) #varName, offsetof(currentRecvDTClass, varName), sizeof(((currentRecvDTClass*)0)->varName) +#define RECVINFO_NAME(varName, remoteVarName) #remoteVarName, offsetof(currentRecvDTClass, varName), sizeof(((currentRecvDTClass*)0)->varName) +#define RECVINFO_STRING(varName) #varName, offsetof(currentRecvDTClass, varName), STRINGBUFSIZE(currentRecvDTClass, varName) +#define RECVINFO_BASECLASS(tableName) RecvPropDataTable("this", 0, 0, &REFERENCE_RECV_TABLE(tableName)) +#define RECVINFO_ARRAY(varName) #varName, offsetof(currentRecvDTClass, varName), sizeof(((currentRecvDTClass*)0)->varName[0]), sizeof(((currentRecvDTClass*)0)->varName)/sizeof(((currentRecvDTClass*)0)->varName[0]) + +// Just specify the name and offset. Used for strings and data tables. +#define RECVINFO_NOSIZE(varName) #varName, offsetof(currentRecvDTClass, varName) +#define RECVINFO_DT(varName) RECVINFO_NOSIZE(varName) +#define RECVINFO_DTNAME(varName,remoteVarName) #remoteVarName, offsetof(currentRecvDTClass, varName) + + +void RecvProxy_FloatToFloat ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_VectorToVector( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_VectorXYToVectorXY( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_QuaternionToQuaternion( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_Int32ToInt8 ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_Int32ToInt16 ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_StringToString( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_Int32ToInt32 ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_Int64ToInt64 ( const CRecvProxyData *pData, void *pStruct, void *pOut ); +void RecvProxy_Int32ToColor32( const CRecvProxyData *pData, void *pStruct, void *pOut ); + +// StaticDataTable does *pOut = pData. +void DataTableRecvProxy_StaticDataTable(const RecvProp *pProp, void **pOut, void *pData, int objectID); + +// PointerDataTable does *pOut = *((void**)pData) (ie: pData is a pointer to the object to decode into). +void DataTableRecvProxy_PointerDataTable(const RecvProp *pProp, void **pOut, void *pData, int objectID); + + +RecvProp RecvPropFloat( + char *pVarName, + int offset, + int sizeofVar=SIZEOF_IGNORE, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags=0, + RecvVarProxyFn varProxy=RecvProxy_FloatToFloat + ); + +RecvProp RecvPropVector( + char *pVarName, + int offset, + int sizeofVar=SIZEOF_IGNORE, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags=0, + RecvVarProxyFn varProxy=RecvProxy_VectorToVector + ); + +RecvProp RecvPropVectorXY( + char *pVarName, + int offset, + int sizeofVar=SIZEOF_IGNORE, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags=0, + RecvVarProxyFn varProxy=RecvProxy_VectorXYToVectorXY + ); + +// This is here so the RecvTable can look more like the SendTable. +#define RecvPropQAngles RecvPropVector + +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! + +RecvProp RecvPropQuaternion( + char *pVarName, + int offset, + int sizeofVar=SIZEOF_IGNORE, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags=0, + RecvVarProxyFn varProxy=RecvProxy_QuaternionToQuaternion + ); +#endif + +RecvProp RecvPropInt( + char *pVarName, + int offset, + int sizeofVar=SIZEOF_IGNORE, // Handled by RECVINFO macro, but set to SIZEOF_IGNORE if you don't want to bother. + int flags=0, + RecvVarProxyFn varProxy=0 + ); + +RecvProp RecvPropString( + char *pVarName, + int offset, + int bufferSize, + int flags=0, + RecvVarProxyFn varProxy=RecvProxy_StringToString + ); + +RecvProp RecvPropDataTable( + char *pVarName, + int offset, + int flags, + RecvTable *pTable, + DataTableRecvVarProxyFn varProxy=DataTableRecvProxy_StaticDataTable + ); + +RecvProp RecvPropArray3( + char *pVarName, + int offset, + int sizeofVar, + int elements, + RecvProp pArrayProp, + DataTableRecvVarProxyFn varProxy=DataTableRecvProxy_StaticDataTable + ); + +// Use the macro to let it automatically generate a table name. You shouldn't +// ever need to reference the table name. If you want to exclude this array, then +// reference the name of the variable in varTemplate. +RecvProp InternalRecvPropArray( + const int elementCount, + const int elementStride, + char *pName, + ArrayLengthRecvProxyFn proxy + ); + + +// +// Use this to match a SendPropArray_AllAtOnce +// +#define RecvPropArray_AllAtOnce( arrayName, propDefinition ) \ + RecvPropArray( propDefinition, arrayName ) + +// +// Use this to match a SendPropArray_UniqueElements +// +#define RecvPropArray_UniqueElements( arrayName, propDefinition ) \ + RecvPropArray3( RECVINFO_ARRAY( m_nValues ), propDefinition ) + + + +// +// Use this if you want to completely manage the way the array data is stored. +// You'll need to provide a proxy inside varTemplate that looks for 'iElement' +// to figure out where to store the specified element. +// +#define RecvPropVirtualArray( arrayLengthProxy, maxArrayLength, varTemplate, propertyName ) \ + varTemplate, \ + InternalRecvPropArray( \ + maxArrayLength, \ + 0, \ + #propertyName, \ + arrayLengthProxy \ + ) + + +// Use this and pass the array name and it will figure out the count and stride automatically. +#define RecvPropVariableLengthArray( arrayLengthProxy, varTemplate, arrayName ) \ + varTemplate, \ + InternalRecvPropArray( \ + sizeof(((currentRecvDTClass*)0)->arrayName) / PROPSIZEOF(currentRecvDTClass, arrayName[0]), \ + PROPSIZEOF(currentRecvDTClass, arrayName[0]), \ + #arrayName, \ + arrayLengthProxy \ + ) + + +// Use this and pass the array name and it will figure out the count and stride automatically. +#define RecvPropArray( varTemplate, arrayName ) \ + RecvPropVariableLengthArray( 0, varTemplate, arrayName ) + + +// Use this one to specify the element count and stride manually. +#define RecvPropArray2( arrayLengthProxy, varTemplate, elementCount, elementStride, arrayName ) \ + varTemplate, \ + InternalRecvPropArray( elementCount, elementStride, #arrayName, arrayLengthProxy ) + + +// ---------------------------------------------------------------------------------------- // +// Inlines. +// ---------------------------------------------------------------------------------------- // + +inline void RecvProp::InitArray( int nElements, int elementStride ) +{ + m_RecvType = DPT_Array; + m_nElements = nElements; + m_ElementStride = elementStride; +} + +inline int RecvProp::GetNumElements() const +{ + return m_nElements; +} + +inline void RecvProp::SetNumElements( int nElements ) +{ + m_nElements = nElements; +} + +inline int RecvProp::GetElementStride() const +{ + return m_ElementStride; +} + +inline void RecvProp::SetElementStride( int stride ) +{ + m_ElementStride = stride; +} + +inline int RecvProp::GetFlags() const +{ + return m_Flags; +} + +inline const char* RecvProp::GetName() const +{ + return m_pVarName; +} + +inline SendPropType RecvProp::GetType() const +{ + return m_RecvType; +} + +inline RecvTable* RecvProp::GetDataTable() const +{ + return m_pDataTable; +} + +inline void RecvProp::SetDataTable( RecvTable *pTable ) +{ + m_pDataTable = pTable; +} + +inline RecvVarProxyFn RecvProp::GetProxyFn() const +{ + return m_ProxyFn; +} + +inline void RecvProp::SetProxyFn( RecvVarProxyFn fn ) +{ + m_ProxyFn = fn; +} + +inline DataTableRecvVarProxyFn RecvProp::GetDataTableProxyFn() const +{ + return m_DataTableProxyFn; +} + +inline void RecvProp::SetDataTableProxyFn( DataTableRecvVarProxyFn fn ) +{ + m_DataTableProxyFn = fn; +} + +inline int RecvProp::GetOffset() const +{ + return m_Offset; +} + +inline void RecvProp::SetOffset( int o ) +{ + m_Offset = o; +} + +inline RecvProp* RecvProp::GetArrayProp() const +{ + return m_pArrayProp; +} + +inline void RecvProp::SetArrayProp( RecvProp *pProp ) +{ + m_pArrayProp = pProp; +} + +inline void RecvProp::SetArrayLengthProxy( ArrayLengthRecvProxyFn proxy ) +{ + m_ArrayLengthProxy = proxy; +} + +inline ArrayLengthRecvProxyFn RecvProp::GetArrayLengthProxy() const +{ + return m_ArrayLengthProxy; +} + +inline bool RecvProp::IsInsideArray() const +{ + return m_bInsideArray; +} + +inline void RecvProp::SetInsideArray() +{ + m_bInsideArray = true; +} + +inline const void* RecvProp::GetExtraData() const +{ + return m_pExtraData; +} + +inline void RecvProp::SetExtraData( const void *pData ) +{ + m_pExtraData = pData; +} + +inline const char* RecvProp::GetParentArrayPropName() +{ + return m_pParentArrayPropName; +} + +inline void RecvProp::SetParentArrayPropName( const char *pArrayPropName ) +{ + m_pParentArrayPropName = pArrayPropName; +} + +#endif // DATATABLE_RECV_H diff --git a/public/dt_send.cpp b/public/dt_send.cpp new file mode 100644 index 0000000..5a120ca --- /dev/null +++ b/public/dt_send.cpp @@ -0,0 +1,910 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +#include "dt_send.h" +#include "mathlib/mathlib.h" +#include "mathlib/vector.h" +#include "tier0/dbg.h" +#include "dt_utlvector_common.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if !defined(_STATIC_LINKED) || defined(GAME_DLL) + + +static CNonModifiedPointerProxy *s_pNonModifiedPointerProxyHead = NULL; + + +void SendProxy_UInt8ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID); +void SendProxy_UInt16ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID); +void SendProxy_UInt32ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID); +void SendProxy_UInt64ToInt64( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID); + +char *s_ElementNames[MAX_ARRAY_ELEMENTS] = +{ + "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", + "010", "011", "012", "013", "014", "015", "016", "017", "018", "019", + "020", "021", "022", "023", "024", "025", "026", "027", "028", "029", + "030", "031", "032", "033", "034", "035", "036", "037", "038", "039", + "040", "041", "042", "043", "044", "045", "046", "047", "048", "049", + "050", "051", "052", "053", "054", "055", "056", "057", "058", "059", + "060", "061", "062", "063", "064", "065", "066", "067", "068", "069", + "070", "071", "072", "073", "074", "075", "076", "077", "078", "079", + "080", "081", "082", "083", "084", "085", "086", "087", "088", "089", + "090", "091", "092", "093", "094", "095", "096", "097", "098", "099", + "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", + "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", + "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", + "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", + "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", + "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", + "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", + "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", + "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", + "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", + "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", + "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", + "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", + "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", + "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", + "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", + "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", + "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", + "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", + "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", + "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", + "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", + "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", + "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", + "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", + "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", + "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", + "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", + "510", "511", "512", "513", "514", "515", "516", "517", "518", "519", + "520", "521", "522", "523", "524", "525", "526", "527", "528", "529", + "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", + "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", + "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", + "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", + "570", "571", "572", "573", "574", "575", "576", "577", "578", "579", + "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", + "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", + "600", "601", "602", "603", "604", "605", "606", "607", "608", "609", + "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", + "620", "621", "622", "623", "624", "625", "626", "627", "628", "629", + "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", + "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", + "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", + "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", + "670", "671", "672", "673", "674", "675", "676", "677", "678", "679", + "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", + "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", + "700", "701", "702", "703", "704", "705", "706", "707", "708", "709", + "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", + "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", + "730", "731", "732", "733", "734", "735", "736", "737", "738", "739", + "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", + "750", "751", "752", "753", "754", "755", "756", "757", "758", "759", + "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", + "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", + "780", "781", "782", "783", "784", "785", "786", "787", "788", "789", + "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", + "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", + "810", "811", "812", "813", "814", "815", "816", "817", "818", "819", + "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", + "830", "831", "832", "833", "834", "835", "836", "837", "838", "839", + "840", "841", "842", "843", "844", "845", "846", "847", "848", "849", + "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", + "860", "861", "862", "863", "864", "865", "866", "867", "868", "869", + "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", + "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", + "890", "891", "892", "893", "894", "895", "896", "897", "898", "899", + "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", + "910", "911", "912", "913", "914", "915", "916", "917", "918", "919", + "920", "921", "922", "923", "924", "925", "926", "927", "928", "929", + "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", + "940", "941", "942", "943", "944", "945", "946", "947", "948", "949", + "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", + "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", + "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", + "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", + "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", + "1000", "1001", "1002", "1003", "1004", "1005", "1006", "1007", "1008", "1009", + "1010", "1011", "1012", "1013", "1014", "1015", "1016", "1017", "1018", "1019", + "1020", "1021", "1022", "1023" + +}; + + +CNonModifiedPointerProxy::CNonModifiedPointerProxy( SendTableProxyFn fn ) +{ + m_pNext = s_pNonModifiedPointerProxyHead; + s_pNonModifiedPointerProxyHead = this; + m_Fn = fn; +} + + +CStandardSendProxiesV1::CStandardSendProxiesV1() +{ + m_Int8ToInt32 = SendProxy_Int8ToInt32; + m_Int16ToInt32 = SendProxy_Int16ToInt32; + m_Int32ToInt32 = SendProxy_Int32ToInt32; + m_Int64ToInt64 = SendProxy_Int64ToInt64; + + m_UInt8ToInt32 = SendProxy_UInt8ToInt32; + m_UInt16ToInt32 = SendProxy_UInt16ToInt32; + m_UInt32ToInt32 = SendProxy_UInt32ToInt32; + m_UInt64ToInt64 = SendProxy_UInt64ToInt64; + + m_FloatToFloat = SendProxy_FloatToFloat; + m_VectorToVector = SendProxy_VectorToVector; +} + +CStandardSendProxies::CStandardSendProxies() +{ + m_DataTableToDataTable = SendProxy_DataTableToDataTable; + m_ppNonModifiedPointerProxies = &s_pNonModifiedPointerProxyHead; + +} +CStandardSendProxies g_StandardSendProxies; + + +// ---------------------------------------------------------------------- // +// Proxies. +// ---------------------------------------------------------------------- // +void SendProxy_AngleToFloat( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + float angle; + + angle = *((float*)pData); + pOut->m_Float = anglemod( angle ); + + Assert( IsFinite( pOut->m_Float ) ); +} + +void SendProxy_FloatToFloat( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Float = *((float*)pData); + Assert( IsFinite( pOut->m_Float ) ); +} + +void SendProxy_QAngles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) +{ + QAngle *v = (QAngle*)pData; + pOut->m_Vector[0] = anglemod( v->x ); + pOut->m_Vector[1] = anglemod( v->y ); + pOut->m_Vector[2] = anglemod( v->z ); +} + +void SendProxy_VectorToVector( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + Vector& v = *(Vector*)pData; + Assert( v.IsValid() ); + pOut->m_Vector[0] = v[0]; + pOut->m_Vector[1] = v[1]; + pOut->m_Vector[2] = v[2]; +} + +void SendProxy_VectorXYToVectorXY( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + Vector& v = *(Vector*)pData; + Assert( v.IsValid() ); + pOut->m_Vector[0] = v[0]; + pOut->m_Vector[1] = v[1]; +} + +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! +void SendProxy_QuaternionToQuaternion( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + Quaternion& q = *(Quaternion*)pData; + Assert( q.IsValid() ); + pOut->m_Vector[0] = q[0]; + pOut->m_Vector[1] = q[1]; + pOut->m_Vector[2] = q[2]; + pOut->m_Vector[3] = q[3]; +} +#endif + +void SendProxy_Int8ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int = *((char*)pData); +} + +void SendProxy_Int16ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int = *((short*)pData); +} + +void SendProxy_Int32ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int = *((int*)pData); +} + +void SendProxy_Color32ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) +{ + //Always send/receive as little endian to preserve byte order across network byte swaps + pOut->m_Int = LittleDWord( *((uint32 *)pData) ); +} + +void SendProxy_Int64ToInt64( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int64 = *((int64*)pData); +} + +void SendProxy_UInt8ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int = *((unsigned char*)pData); +} + +void SendProxy_UInt16ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_Int = *((unsigned short*)pData); +} + +void SendProxy_UInt32ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + *((unsigned long*)&pOut->m_Int) = *((unsigned long*)pData); +} + +void SendProxy_UInt64ToInt64( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + *((int64*)&pOut->m_Int64) = *((uint64*)pData); +} + +void SendProxy_StringToString( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ + pOut->m_pString = (char*)pData; +} + +void* SendProxy_DataTableToDataTable( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) +{ + return (void*)pData; +} + +void* SendProxy_DataTablePtrToDataTable( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) +{ + return *((void**)pData); +} + +static void SendProxy_Empty( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) +{ +} + +// ---------------------------------------------------------------------- // +// Prop setup functions (for building tables). +// ---------------------------------------------------------------------- // +float AssignRangeMultiplier( int nBits, double range ) +{ + unsigned long iHighValue; + if ( nBits == 32 ) + iHighValue = 0xFFFFFFFE; + else + iHighValue = ((1 << (unsigned long)nBits) - 1); + + float fHighLowMul = iHighValue / range; + if ( CloseEnough( range, 0 ) ) + fHighLowMul = iHighValue; + + // If the precision is messing us up, then adjust it so it won't. + if ( (unsigned long)(fHighLowMul * range) > iHighValue || + (fHighLowMul * range) > (double)iHighValue ) + { + // Squeeze it down smaller and smaller until it's going to produce an integer + // in the valid range when given the highest value. + float multipliers[] = { 0.9999, 0.99, 0.9, 0.8, 0.7 }; + int i; + for ( i=0; i < ARRAYSIZE( multipliers ); i++ ) + { + float fHighLowMul = (float)( iHighValue / range ) * multipliers[i]; + if ( (unsigned long)(fHighLowMul * range) > iHighValue || + (fHighLowMul * range) > (double)iHighValue ) + { + } + else + { + break; + } + } + + if ( i == ARRAYSIZE( multipliers ) ) + { + // Doh! We seem to be unable to represent this range. + Assert( false ); + return 0; + } + } + + return fHighLowMul; +} + + + +SendProp SendPropFloat( + char *pVarName, + // Variable name. + int offset, // Offset into container structure. + int sizeofVar, + int nBits, // Number of bits to use when encoding. + int flags, + float fLowValue, // For floating point, low and high values. + float fHighValue, // High value. If HIGH_DEFAULT, it's (1<m_Int = atoi(pUserStr); + +// pProp : the SendProp that has the proxy +// pStructBase : the base structure (like CBaseEntity*). +// pData : the address of the variable to proxy. +// pOut : where to output the proxied value. +// iElement : the element index if this data is part of an array (or 0 if not). +// objectID : entity index for debugging purposes. + +// Return false if you don't want the engine to register and send a delta to +// the clients for this property (regardless of whether it actually changed or not). +// ------------------------------------------------------------------------ // +typedef void (*SendVarProxyFn)( const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID ); + +// Return the pointer to the data for the datatable. +// If the proxy returns null, it's the same as if pRecipients->ClearAllRecipients() was called. +class CSendProxyRecipients; + +typedef void* (*SendTableProxyFn)( + const SendProp *pProp, + const void *pStructBase, + const void *pData, + CSendProxyRecipients *pRecipients, + int objectID ); + + +class CNonModifiedPointerProxy +{ +public: + CNonModifiedPointerProxy( SendTableProxyFn fn ); + +public: + + SendTableProxyFn m_Fn; + CNonModifiedPointerProxy *m_pNext; +}; + + +// This tells the engine that the send proxy will not modify the pointer +// - it only plays with the recipients. This must be set on proxies that work +// this way, otherwise the engine can't track which properties changed +// in NetworkStateChanged(). +#define REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( sendProxyFn ) static CNonModifiedPointerProxy __proxy_##sendProxyFn( sendProxyFn ); + + +class CStandardSendProxiesV1 +{ +public: + CStandardSendProxiesV1(); + + SendVarProxyFn m_Int8ToInt32; + SendVarProxyFn m_Int16ToInt32; + SendVarProxyFn m_Int32ToInt32; + SendVarProxyFn m_Int64ToInt64; + + SendVarProxyFn m_UInt8ToInt32; + SendVarProxyFn m_UInt16ToInt32; + SendVarProxyFn m_UInt32ToInt32; + SendVarProxyFn m_UInt64ToInt64; + + SendVarProxyFn m_FloatToFloat; + SendVarProxyFn m_VectorToVector; +}; + +class CStandardSendProxies : public CStandardSendProxiesV1 +{ +public: + CStandardSendProxies(); + + SendTableProxyFn m_DataTableToDataTable; + CNonModifiedPointerProxy **m_ppNonModifiedPointerProxies; +}; + +extern CStandardSendProxies g_StandardSendProxies; + + +// Max # of datatable send proxies you can have in a tree. +#define MAX_DATATABLE_PROXIES 32 + +// ------------------------------------------------------------------------ // +// Datatable send proxies are used to tell the engine where the datatable's +// data is and to specify which clients should get the data. +// +// pRecipients is the object that allows you to specify which clients will +// receive the data. +// ------------------------------------------------------------------------ // +class CSendProxyRecipients +{ +public: + void SetAllRecipients(); // Note: recipients are all set by default when each proxy is called. + void ClearAllRecipients(); + + void SetRecipient( int iClient ); // Note: these are CLIENT indices, not entity indices (so the first player's index is 0). + void ClearRecipient( int iClient ); + + // Clear all recipients and set only the specified one. + void SetOnly( int iClient ); + // Set all recipients, save for the specified on + void ExcludeOnly( int iClient ); + +public: + // Make sure we have enough room for the max possible player count + CPlayerBitVec m_Bits; +}; + +inline void CSendProxyRecipients::SetAllRecipients() +{ + m_Bits.SetAll(); +} + +inline void CSendProxyRecipients::ClearAllRecipients() +{ + m_Bits.ClearAll(); +} + + + + +// ------------------------------------------------------------------------ // +// ArrayLengthSendProxies are used when you want to specify an array's length +// dynamically. +// ------------------------------------------------------------------------ // +typedef int (*ArrayLengthSendProxyFn)( const void *pStruct, int objectID ); + + + +class RecvProp; +class SendTable; +class CSendTablePrecalc; + + +// -------------------------------------------------------------------------------------------------------------- // +// SendProp. +// -------------------------------------------------------------------------------------------------------------- // + +// If SendProp::GetDataTableProxyIndex() returns this, then the proxy is one that always sends +// the data to all clients, so we don't need to store the results. +#define DATATABLE_PROXY_INDEX_NOPROXY 255 +#define DATATABLE_PROXY_INDEX_INVALID 254 + +#define SENDPROP_DEFAULT_PRIORITY ((byte)128) +#define SENDPROP_CHANGES_OFTEN_PRIORITY ((byte)64) + +class SendProp +{ +public: + SendProp(); + virtual ~SendProp(); + + void Clear(); + + int GetOffset() const; + void SetOffset( int i ); + + SendVarProxyFn GetProxyFn() const; + void SetProxyFn( SendVarProxyFn f ); + + SendTableProxyFn GetDataTableProxyFn() const; + void SetDataTableProxyFn( SendTableProxyFn f ); + + SendTable* GetDataTable() const; + void SetDataTable( SendTable *pTable ); + + char const* GetExcludeDTName() const; + + // If it's one of the numbered "000", "001", etc properties in an array, then + // these can be used to get its array property name for debugging. + const char* GetParentArrayPropName() const; + void SetParentArrayPropName( char *pArrayPropName ); + + const char* GetName() const; + + bool IsSigned() const; + + bool IsExcludeProp() const; + + bool IsInsideArray() const; // Returns true if SPROP_INSIDEARRAY is set. + void SetInsideArray(); + + // Arrays only. + void SetArrayProp( SendProp *pProp ); + SendProp* GetArrayProp() const; + + // Arrays only. + void SetArrayLengthProxy( ArrayLengthSendProxyFn fn ); + ArrayLengthSendProxyFn GetArrayLengthProxy() const; + + int GetNumElements() const; + void SetNumElements( int nElements ); + + // Return the # of bits to encode an array length (must hold GetNumElements()). + int GetNumArrayLengthBits() const; + + int GetElementStride() const; + + SendPropType GetType() const; + + int GetFlags() const; + void SetFlags( int flags ); + + // Some property types bind more data to the SendProp in here. + const void* GetExtraData() const; + void SetExtraData( const void *pData ); + + byte GetPriority() const; + void SetPriority( byte priority ); + +public: + + RecvProp *m_pMatchingRecvProp; // This is temporary and only used while precalculating + // data for the decoders. + + SendPropType m_Type; + int m_nBits; + float m_fLowValue; + float m_fHighValue; + + SendProp *m_pArrayProp; // If this is an array, this is the property that defines each array element. + ArrayLengthSendProxyFn m_ArrayLengthProxy; // This callback returns the array length. + + int m_nElements; // Number of elements in the array (or 1 if it's not an array). + int m_ElementStride; // Pointer distance between array elements. + + char *m_pExcludeDTName; // If this is an exclude prop, then this is the name of the datatable to exclude a prop from. + char *m_pParentArrayPropName; + + char *m_pVarName; + float m_fHighLowMul; + + byte m_priority; + +private: + + int m_Flags; // SPROP_ flags. + + SendVarProxyFn m_ProxyFn; // NULL for DPT_DataTable. + SendTableProxyFn m_DataTableProxyFn; // Valid for DPT_DataTable. + + SendTable *m_pDataTable; + + // SENDPROP_VECTORELEM makes this negative to start with so we can detect that and + // set the SPROP_IS_VECTOR_ELEM flag. + int m_Offset; + + // Extra data bound to this property. + const void *m_pExtraData; +}; + + +inline int SendProp::GetOffset() const +{ + return m_Offset; +} + +inline void SendProp::SetOffset( int i ) +{ + m_Offset = i; +} + +inline SendVarProxyFn SendProp::GetProxyFn() const +{ + Assert( m_Type != DPT_DataTable ); + return m_ProxyFn; +} + +inline void SendProp::SetProxyFn( SendVarProxyFn f ) +{ + m_ProxyFn = f; +} + +inline SendTableProxyFn SendProp::GetDataTableProxyFn() const +{ + Assert( m_Type == DPT_DataTable ); + return m_DataTableProxyFn; +} + +inline void SendProp::SetDataTableProxyFn( SendTableProxyFn f ) +{ + m_DataTableProxyFn = f; +} + +inline SendTable* SendProp::GetDataTable() const +{ + return m_pDataTable; +} + +inline void SendProp::SetDataTable( SendTable *pTable ) +{ + m_pDataTable = pTable; +} + +inline char const* SendProp::GetExcludeDTName() const +{ + return m_pExcludeDTName; +} + +inline const char* SendProp::GetParentArrayPropName() const +{ + return m_pParentArrayPropName; +} + +inline void SendProp::SetParentArrayPropName( char *pArrayPropName ) +{ + Assert( !m_pParentArrayPropName ); + m_pParentArrayPropName = pArrayPropName; +} + +inline const char* SendProp::GetName() const +{ + return m_pVarName; +} + + +inline bool SendProp::IsSigned() const +{ + return !(m_Flags & SPROP_UNSIGNED); +} + +inline bool SendProp::IsExcludeProp() const +{ + return (m_Flags & SPROP_EXCLUDE) != 0; +} + +inline bool SendProp::IsInsideArray() const +{ + return (m_Flags & SPROP_INSIDEARRAY) != 0; +} + +inline void SendProp::SetInsideArray() +{ + m_Flags |= SPROP_INSIDEARRAY; +} + +inline void SendProp::SetArrayProp( SendProp *pProp ) +{ + m_pArrayProp = pProp; +} + +inline SendProp* SendProp::GetArrayProp() const +{ + return m_pArrayProp; +} + +inline void SendProp::SetArrayLengthProxy( ArrayLengthSendProxyFn fn ) +{ + m_ArrayLengthProxy = fn; +} + +inline ArrayLengthSendProxyFn SendProp::GetArrayLengthProxy() const +{ + return m_ArrayLengthProxy; +} + +inline int SendProp::GetNumElements() const +{ + return m_nElements; +} + +inline void SendProp::SetNumElements( int nElements ) +{ + m_nElements = nElements; +} + +inline int SendProp::GetElementStride() const +{ + return m_ElementStride; +} + +inline SendPropType SendProp::GetType() const +{ + return m_Type; +} + +inline int SendProp::GetFlags() const +{ + return m_Flags; +} + +inline void SendProp::SetFlags( int flags ) +{ + // Make sure they're using something from the valid set of flags. + Assert( !( flags & ~((1 << SPROP_NUMFLAGBITS) - 1) ) ); + m_Flags = flags; +} + +inline const void* SendProp::GetExtraData() const +{ + return m_pExtraData; +} + +inline void SendProp::SetExtraData( const void *pData ) +{ + m_pExtraData = pData; +} + +inline byte SendProp::GetPriority() const +{ + return m_priority; +} + +inline void SendProp::SetPriority( byte priority ) +{ + m_priority = priority; +} + +// -------------------------------------------------------------------------------------------------------------- // +// SendTable. +// -------------------------------------------------------------------------------------------------------------- // + +class SendTable +{ +public: + + typedef SendProp PropType; + + SendTable(); + SendTable( SendProp *pProps, int nProps, char *pNetTableName ); + ~SendTable(); + + void Construct( SendProp *pProps, int nProps, char *pNetTableName ); + + const char* GetName() const; + + int GetNumProps() const; + SendProp* GetProp( int i ); + + // Used by the engine. + bool IsInitialized() const; + void SetInitialized( bool bInitialized ); + + // Used by the engine while writing info into the signon. + void SetWriteFlag(bool bHasBeenWritten); + bool GetWriteFlag() const; + + bool HasPropsEncodedAgainstTickCount() const; + void SetHasPropsEncodedAgainstTickcount( bool bState ); + +public: + + SendProp *m_pProps; + int m_nProps; + + char *m_pNetTableName; // The name matched between client and server. + + // The engine hooks the SendTable here. + CSendTablePrecalc *m_pPrecalc; + + +protected: + bool m_bInitialized : 1; + bool m_bHasBeenWritten : 1; + bool m_bHasPropsEncodedAgainstCurrentTickCount : 1; // m_flSimulationTime and m_flAnimTime, e.g. +}; + + +inline const char* SendTable::GetName() const +{ + return m_pNetTableName; +} + + +inline int SendTable::GetNumProps() const +{ + return m_nProps; +} + + +inline SendProp* SendTable::GetProp( int i ) +{ + Assert( i >= 0 && i < m_nProps ); + return &m_pProps[i]; +} + + +inline bool SendTable::IsInitialized() const +{ + return m_bInitialized; +} + + +inline void SendTable::SetInitialized( bool bInitialized ) +{ + m_bInitialized = bInitialized; +} + + +inline bool SendTable::GetWriteFlag() const +{ + return m_bHasBeenWritten; +} + + +inline void SendTable::SetWriteFlag(bool bHasBeenWritten) +{ + m_bHasBeenWritten = bHasBeenWritten; +} + +inline bool SendTable::HasPropsEncodedAgainstTickCount() const +{ + return m_bHasPropsEncodedAgainstCurrentTickCount; +} + +inline void SendTable::SetHasPropsEncodedAgainstTickcount( bool bState ) +{ + m_bHasPropsEncodedAgainstCurrentTickCount = bState; +} + +// ------------------------------------------------------------------------------------------------------ // +// Use BEGIN_SEND_TABLE if you want to declare a SendTable and have it inherit all the properties from +// its base class. There are two requirements for this to work: + +// 1. Its base class must have a static SendTable pointer member variable called m_pClassSendTable which +// points to its send table. The DECLARE_SERVERCLASS and IMPLEMENT_SERVERCLASS macros do this automatically. + +// 2. Your class must typedef its base class as BaseClass. So it would look like this: +// class Derived : public CBaseEntity +// { +// typedef CBaseEntity BaseClass; +// }; + +// If you don't want to interit a base class's properties, use BEGIN_SEND_TABLE_NOBASE. +// ------------------------------------------------------------------------------------------------------ // +#define BEGIN_SEND_TABLE(className, tableName) \ + BEGIN_SEND_TABLE_NOBASE(className, tableName) \ + SendPropDataTable("baseclass", 0, className::BaseClass::m_pClassSendTable, SendProxy_DataTableToDataTable), + +#define BEGIN_SEND_TABLE_NOBASE(className, tableName) \ + template int ServerClassInit(T *); \ + namespace tableName { \ + struct ignored; \ + } \ + template <> int ServerClassInit(tableName::ignored *); \ + namespace tableName { \ + SendTable g_SendTable;\ + int g_SendTableInit = ServerClassInit((tableName::ignored *)NULL); \ + } \ + template <> int ServerClassInit(tableName::ignored *) \ + { \ + typedef className currentSendDTClass; \ + static char *g_pSendTableName = #tableName; \ + SendTable &sendTable = tableName::g_SendTable; \ + static SendProp g_SendProps[] = { \ + SendPropInt("should_never_see_this", 0, sizeof(int)), // It adds a dummy property at the start so you can define "empty" SendTables. + +#define END_SEND_TABLE() \ + };\ + sendTable.Construct(g_SendProps+1, sizeof(g_SendProps) / sizeof(SendProp) - 1, g_pSendTableName);\ + return 1; \ + } + + +// These can simplify creating the variables. +// Note: currentSendDTClass::MakeANetworkVar_##varName equates to currentSendDTClass. It's +// there as a check to make sure all networked variables use the CNetworkXXXX macros in network_var.h. +#define SENDINFO(varName) #varName, offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName), sizeof(((currentSendDTClass*)0)->varName) +#define SENDINFO_ARRAY(varName) #varName, offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName), sizeof(((currentSendDTClass*)0)->varName[0]) +#define SENDINFO_ARRAY3(varName) #varName, offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName), sizeof(((currentSendDTClass*)0)->varName[0]), sizeof(((currentSendDTClass*)0)->varName)/sizeof(((currentSendDTClass*)0)->varName[0]) +#define SENDINFO_ARRAYELEM(varName, i) #varName "[" #i "]", offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName[i]), sizeof(((currentSendDTClass*)0)->varName[0]) +#define SENDINFO_NETWORKARRAYELEM(varName, i)#varName "[" #i "]", offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName.m_Value[i]), sizeof(((currentSendDTClass*)0)->varName.m_Value[0]) + +// NOTE: Be VERY careful to specify any other vector elems for the same vector IN ORDER and +// right after each other, otherwise it might miss the Y or Z component in SP. +// +// Note: this macro specifies a negative offset so the engine can detect it and setup m_pNext +#define SENDINFO_VECTORELEM(varName, i) #varName "[" #i "]", -(int)offsetof(currentSendDTClass::MakeANetworkVar_##varName, varName.m_Value[i]), sizeof(((currentSendDTClass*)0)->varName.m_Value[0]) + +#define SENDINFO_STRUCTELEM(varName) #varName, offsetof(currentSendDTClass, varName), sizeof(((currentSendDTClass*)0)->varName.m_Value) +#define SENDINFO_STRUCTARRAYELEM(varName, i)#varName "[" #i "]", offsetof(currentSendDTClass, varName.m_Value[i]), sizeof(((currentSendDTClass*)0)->varName.m_Value[0]) + +// Use this when you're not using a CNetworkVar to represent the data you're sending. +#define SENDINFO_NOCHECK(varName) #varName, offsetof(currentSendDTClass, varName), sizeof(((currentSendDTClass*)0)->varName) +#define SENDINFO_STRING_NOCHECK(varName) #varName, offsetof(currentSendDTClass, varName) +#define SENDINFO_DT(varName) #varName, offsetof(currentSendDTClass, varName) +#define SENDINFO_DT_NAME(varName, remoteVarName) #remoteVarName, offsetof(currentSendDTClass, varName) +#define SENDINFO_NAME(varName,remoteVarName) #remoteVarName, offsetof(currentSendDTClass, varName), sizeof(((currentSendDTClass*)0)->varName) + + + + +// ------------------------------------------------------------------------ // +// Built-in proxy types. +// See the definition of SendVarProxyFn for information about these. +// ------------------------------------------------------------------------ // +void SendProxy_QAngles ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_AngleToFloat ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_FloatToFloat ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_VectorToVector ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_VectorXYToVectorXY( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!! +void SendProxy_QuaternionToQuaternion( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +#endif + +void SendProxy_Int8ToInt32 ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_Int16ToInt32 ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_Int32ToInt32 ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_Int64ToInt64 ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_Color32ToInt32 ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); +void SendProxy_StringToString ( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); + + +// pData is the address of a data table. +void* SendProxy_DataTableToDataTable( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ); + +// pData is the address of a pointer to a data table. +void* SendProxy_DataTablePtrToDataTable( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ); + +// ------------------------------------------------------------------------ // +// Use these functions to setup your data tables. +// ------------------------------------------------------------------------ // +SendProp SendPropFloat( + char *pVarName, // Variable name. + int offset, // Offset into container structure. + int sizeofVar=SIZEOF_IGNORE, + int nBits=32, // Number of bits to use when encoding. + int flags=0, + float fLowValue=0.0f, // For floating point, low and high values. + float fHighValue=HIGH_DEFAULT, // High value. If HIGH_DEFAULT, it's (1<arrayName) / PROPSIZEOF(currentSendDTClass, arrayName[0]), \ + PROPSIZEOF(currentSendDTClass, arrayName[0]), \ + #arrayName, \ + arrayLengthSendProxy \ + ) + +// Use this one to specify the element count and stride manually. +#define SendPropArray2( arrayLengthSendProxy, varTemplate, elementCount, elementStride, arrayName ) \ + varTemplate, \ + InternalSendPropArray( elementCount, elementStride, #arrayName, arrayLengthSendProxy ) + + + + +// Use these to create properties that exclude other properties. This is useful if you want to use most of +// a base class's datatable data, but you want to override some of its variables. +SendProp SendPropExclude( + char *pDataTableName, // Data table name (given to BEGIN_SEND_TABLE and BEGIN_RECV_TABLE). + char *pPropName // Name of the property to exclude. + ); + + +#endif // DATATABLE_SEND_H diff --git a/public/dt_shared.cpp b/public/dt_shared.cpp new file mode 100644 index 0000000..d0e4576 --- /dev/null +++ b/public/dt_shared.cpp @@ -0,0 +1,113 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dt_shared.h" + +#if !defined (CLIENT_DLL) +#include "sendproxy.h" +#else +#include "recvproxy.h" +#endif + + +// ------------------------------------------------------------------------ // +// Just wrappers to make shared code look easier... +// ------------------------------------------------------------------------ // + +// Use these functions to setup your data tables. +DataTableProp PropFloat( + char *pVarName, // Variable name. + int offset, // Offset into container structure. + int sizeofVar, + int nBits, // Number of bits to use when encoding. + int flags, + float fLowValue, // For floating point, low and high values. + float fHighValue // High value. If HIGH_DEFAULT, it's (1< *g_STDict = 0; +static CUtlDict *g_RTDict = 0; + + +char* AllocateStringHelper2( const char *pFormat, va_list marker ) +{ + char str[512]; + _vsnprintf( str, sizeof( str ), pFormat, marker ); + + int len = strlen( str ) + 1; + char *pRet = new char[len]; + memcpy( pRet, str, len ); + + return pRet; +} + + +char* AllocateStringHelper( const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + char *pRet = AllocateStringHelper2( pFormat, marker ); + va_end( marker ); + + return pRet; +} + + +char* AllocateUniqueDataTableName( bool bSendTable, const char *pFormat, ... ) +{ + // Setup the string. + va_list marker; + va_start( marker, pFormat ); + char *pRet = AllocateStringHelper2( pFormat, marker ); + va_end( marker ); + + // Make sure it's unique. +#ifdef _DEBUG + // Have to allocate them here because if they're declared as straight global variables, + // their constructors won't have been called yet by the time we get in here. + if ( !g_STDict ) + { + g_STDict = new CUtlDict; + g_RTDict = new CUtlDict; + } + + CUtlDict *pDict = bSendTable ? g_STDict : g_RTDict; + if ( pDict->Find( pRet ) != pDict->InvalidIndex() ) + { + // If it hits this, then they have 2 utlvectors in different data tables with the same name and the + // same size limit. The names of + Assert( false ); + } + pDict->Insert( pRet, 0 ); +#endif + + return pRet; +} diff --git a/public/dt_utlvector_common.h b/public/dt_utlvector_common.h new file mode 100644 index 0000000..89b31d2 --- /dev/null +++ b/public/dt_utlvector_common.h @@ -0,0 +1,85 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DT_UTLVECTOR_COMMON_H +#define DT_UTLVECTOR_COMMON_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "utlvector.h" + + +typedef void (*EnsureCapacityFn)( void *pVoid, int offsetToUtlVector, int len ); +typedef void (*ResizeUtlVectorFn)( void *pVoid, int offsetToUtlVector, int len ); + +template< class T > +void UtlVector_InitializeAllocatedElements( T *pBase, int count ) +{ + memset( pBase, 0, count * sizeof( T ) ); +} + +template< class T, class A > +class UtlVectorTemplate +{ +public: + static void ResizeUtlVector( void *pStruct, int offsetToUtlVector, int len ) + { + CUtlVector *pVec = (CUtlVector*)((char*)pStruct + offsetToUtlVector); + if ( pVec->Count() < len ) + pVec->AddMultipleToTail( len - pVec->Count() ); + else if ( pVec->Count() > len ) + pVec->RemoveMultiple( len, pVec->Count()-len ); + + // Ensure capacity + pVec->EnsureCapacity( len ); + + int nNumAllocated = pVec->NumAllocated(); + + // This is important to do because EnsureCapacity doesn't actually call the constructors + // on the elements, but we need them to be initialized, otherwise it'll have out-of-range + // values which will piss off the datatable encoder. + UtlVector_InitializeAllocatedElements( pVec->Base() + pVec->Count(), nNumAllocated - pVec->Count() ); + } + + static void EnsureCapacity( void *pStruct, int offsetToUtlVector, int len ) + { + CUtlVector *pVec = (CUtlVector*)((char*)pStruct + offsetToUtlVector); + + pVec->EnsureCapacity( len ); + + int nNumAllocated = pVec->NumAllocated(); + + // This is important to do because EnsureCapacity doesn't actually call the constructors + // on the elements, but we need them to be initialized, otherwise it'll have out-of-range + // values which will piss off the datatable encoder. + UtlVector_InitializeAllocatedElements( pVec->Base() + pVec->Count(), nNumAllocated - pVec->Count() ); + } +}; + +template< class T, class A > +inline ResizeUtlVectorFn GetResizeUtlVectorTemplate( CUtlVector &vec ) +{ + return &UtlVectorTemplate::ResizeUtlVector; +} + +template< class T, class A > +inline EnsureCapacityFn GetEnsureCapacityTemplate( CUtlVector &vec ) +{ + return &UtlVectorTemplate::EnsureCapacity; +} + + +// Format and allocate a string. +char* AllocateStringHelper( const char *pFormat, ... ); + +// Allocates a string for a data table name. Data table names must be unique, so this will +// assert if you try to allocate a duplicate. +char* AllocateUniqueDataTableName( bool bSendTable, const char *pFormat, ... ); + + +#endif // DT_UTLVECTOR_COMMON_H diff --git a/public/dt_utlvector_recv.cpp b/public/dt_utlvector_recv.cpp new file mode 100644 index 0000000..2ecc248 --- /dev/null +++ b/public/dt_utlvector_recv.cpp @@ -0,0 +1,157 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dt_utlvector_recv.h" + +#include "tier0/memdbgon.h" + + +extern char *s_ClientElementNames[MAX_ARRAY_ELEMENTS]; + + +class CRecvPropExtra_UtlVector +{ +public: + DataTableRecvVarProxyFn m_DataTableProxyFn; // If it's a datatable, then this is the proxy they specified. + RecvVarProxyFn m_ProxyFn; // If it's a non-datatable, then this is the proxy they specified. + ResizeUtlVectorFn m_ResizeFn; // The function used to resize the CUtlVector. + EnsureCapacityFn m_EnsureCapacityFn; + int m_ElementStride; // Distance between each element in the array. + int m_Offset; // Offset of the CUtlVector from its parent structure. + int m_nMaxElements; // For debugging... +}; + +void RecvProxy_UtlVectorLength( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pData->m_pRecvProp->GetExtraData(); + pExtra->m_ResizeFn( pStruct, pExtra->m_Offset, pData->m_Value.m_Int ); +} + +void RecvProxy_UtlVectorElement( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pData->m_pRecvProp->GetExtraData(); + + // Kind of lame overloading element stride to hold the element index, + // but we can easily move it into its SetExtraData stuff if we need to. + int iElement = pData->m_pRecvProp->GetElementStride(); + + // NOTE: this is cheesy, but it does the trick. + CUtlVector *pUtlVec = (CUtlVector*)((char*)pStruct + pExtra->m_Offset); + + // Call through to the proxy they passed in, making pStruct=the CUtlVector. + // Note: there should be space here as long as the element is < the max # elements + // that we ensured capacity for in DataTableRecvProxy_LengthProxy. + pExtra->m_ProxyFn( pData, pOut, (char*)pUtlVec->Base() + iElement*pExtra->m_ElementStride ); +} + +void RecvProxy_UtlVectorElement_DataTable( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pProp->GetExtraData(); + + int iElement = pProp->GetElementStride(); + Assert( iElement < pExtra->m_nMaxElements ); + + // NOTE: this is cheesy, but it does the trick. + CUtlVector *pUtlVec = (CUtlVector*)((char*)pData + pExtra->m_Offset); + + // Call through to the proxy they passed in, making pStruct=the CUtlVector and forcing iElement to 0. + pExtra->m_DataTableProxyFn( pProp, pOut, (char*)pUtlVec->Base() + iElement*pExtra->m_ElementStride, objectID ); +} + + +void DataTableRecvProxy_LengthProxy( const RecvProp *pProp, void **pOut, void *pData, int objectID ) +{ + // This is VERY important - since it calls all the datatable proxies in the tree first, + // particularly BEFORE it calls our array length proxy, we need to make sure we return + // valid pointers that aren't going to change when it starts to copy the data into + // the datatable elements. + CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pProp->GetExtraData(); + pExtra->m_EnsureCapacityFn( pData, pExtra->m_Offset, pExtra->m_nMaxElements ); + + *pOut = pData; +} + + +RecvProp RecvPropUtlVector( + char *pVarName, // Use RECVINFO_UTLVECTOR to generate these 4. + int offset, // Used to generate pData in the function specified in varProxy. + int sizeofVar, // The size of each element in the utlvector. + ResizeUtlVectorFn fn, + EnsureCapacityFn ensureFn, + int nMaxElements, // Max # of elements in the array. Keep this as low as possible. + RecvProp pArrayProp + ) +{ + RecvProp ret; + + Assert( nMaxElements <= MAX_ARRAY_ELEMENTS ); + + ret.m_RecvType = DPT_DataTable; + ret.m_pVarName = pVarName; + ret.SetOffset( 0 ); + ret.SetDataTableProxyFn( DataTableRecvProxy_StaticDataTable ); + + RecvProp *pProps = new RecvProp[nMaxElements+1]; // TODO free that again + + + // Extra data bound to each of the properties. + CRecvPropExtra_UtlVector *pExtraData = new CRecvPropExtra_UtlVector; + + pExtraData->m_nMaxElements = nMaxElements; + pExtraData->m_ElementStride = sizeofVar; + pExtraData->m_ResizeFn = fn; + pExtraData->m_EnsureCapacityFn = ensureFn; + pExtraData->m_Offset = offset; + + if ( pArrayProp.m_RecvType == DPT_DataTable ) + pExtraData->m_DataTableProxyFn = pArrayProp.GetDataTableProxyFn(); + else + pExtraData->m_ProxyFn = pArrayProp.GetProxyFn(); + + + // The first property is datatable with an int that tells the length of the array. + // It has to go in a datatable, otherwise if this array holds datatable properties, it will be received last. + RecvProp *pLengthProp = new RecvProp; + *pLengthProp = RecvPropInt( AllocateStringHelper( "lengthprop%d", nMaxElements ), 0, 0, 0, RecvProxy_UtlVectorLength ); + pLengthProp->SetExtraData( pExtraData ); + + char *pLengthProxyTableName = AllocateUniqueDataTableName( false, "_LPT_%s_%d", pVarName, nMaxElements ); + RecvTable *pLengthTable = new RecvTable( pLengthProp, 1, pLengthProxyTableName ); + pProps[0] = RecvPropDataTable( "lengthproxy", 0, 0, pLengthTable, DataTableRecvProxy_LengthProxy ); + pProps[0].SetExtraData( pExtraData ); + + // The first element is a sub-datatable. + for ( int i = 1; i < nMaxElements+1; i++ ) + { + pProps[i] = pArrayProp; // copy array element property setting + pProps[i].SetOffset( 0 ); // leave offset at 0 so pStructBase is always a pointer to the CUtlVector + pProps[i].m_pVarName = s_ClientElementNames[i-1]; // give unique name + pProps[i].SetExtraData( pExtraData ); + pProps[i].SetElementStride( i-1 ); // Kind of lame overloading element stride to hold the element index, + // but we can easily move it into its SetExtraData stuff if we need to. + + // We provide our own proxy here. + if ( pArrayProp.m_RecvType == DPT_DataTable ) + { + pProps[i].SetDataTableProxyFn( RecvProxy_UtlVectorElement_DataTable ); + } + else + { + pProps[i].SetProxyFn( RecvProxy_UtlVectorElement ); + } + } + + RecvTable *pTable = new RecvTable( + pProps, + nMaxElements+1, + AllocateUniqueDataTableName( false, "_ST_%s_%d", pVarName, nMaxElements ) + ); // TODO free that again + + ret.SetDataTable( pTable ); + return ret; +} diff --git a/public/dt_utlvector_recv.h b/public/dt_utlvector_recv.h new file mode 100644 index 0000000..5252466 --- /dev/null +++ b/public/dt_utlvector_recv.h @@ -0,0 +1,60 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL1. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DT_UTLVECTOR_RECV_H +#define DT_UTLVECTOR_RECV_H +#pragma once + + +#include "dt_recv.h" +#include "dt_utlvector_common.h" + + + +#define RECVINFO_UTLVECTOR( varName ) #varName, \ + offsetof(currentRecvDTClass, varName), \ + sizeof(((currentRecvDTClass*)0)->varName[0]), \ + GetResizeUtlVectorTemplate( ((currentRecvDTClass*)0)->varName ), \ + GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) + +// Use this macro to specify a utlvector where you specify the function +// that gets called to make sure the size of the utlvector is correct. +// The size function looks like this: void ResizeUtlVector( void *pVoid, int len ) +#define RECVINFO_UTLVECTOR_SIZEFN( varName, resizeFn ) #varName, \ + offsetof(currentRecvDTClass, varName), \ + sizeof(((currentRecvDTClass*)0)->varName[0]), \ + resizeFn, \ + GetEnsureCapacityTemplate( ((currentRecvDTClass*)0)->varName ) + + +#define RecvPropUtlVectorDataTable( varName, nMaxElements, dataTableName ) \ + RecvPropUtlVector( RECVINFO_UTLVECTOR( varName ), nMaxElements, RecvPropDataTable(NULL,0,0, &REFERENCE_RECV_TABLE( dataTableName ) ) ) + + +// +// Receive a property sent with SendPropUtlVector. +// +// Example usage: +// +// RecvPropUtlVectorDataTable( m_StructArray, 11, DT_StructArray ) +// +// RecvPropUtlVector( RECVINFO_UTLVECTOR( m_FloatArray ), 16, RecvPropFloat(NULL,0,0) ) +// +RecvProp RecvPropUtlVector( + char *pVarName, // Use RECVINFO_UTLVECTOR to generate these first 5 parameters. + int offset, + int sizeofVar, + ResizeUtlVectorFn fn, + EnsureCapacityFn ensureFn, + + int nMaxElements, // Max # of elements in the array. Keep this as low as possible. + RecvProp pArrayProp // The definition of the property you're receiving into. + // You can leave all of its parameters at 0 (name, offset, size, etc). + ); + + +#endif // DT_UTLVECTOR_RECV_H diff --git a/public/dt_utlvector_send.cpp b/public/dt_utlvector_send.cpp new file mode 100644 index 0000000..47fe9f4 --- /dev/null +++ b/public/dt_utlvector_send.cpp @@ -0,0 +1,220 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "dt_utlvector_send.h" + +#include "tier0/memdbgon.h" + + +extern char *s_ElementNames[MAX_ARRAY_ELEMENTS]; + +// This gets associated with SendProps inside a utlvector and stores extra data needed to make it work. +class CSendPropExtra_UtlVector +{ +public: + CSendPropExtra_UtlVector() : + m_DataTableProxyFn( NULL ), + m_ProxyFn( NULL ), + m_EnsureCapacityFn( NULL ), + m_ElementStride( 0 ), + m_Offset( 0 ), + m_nMaxElements( 0 ) + { + } + + SendTableProxyFn m_DataTableProxyFn; // If it's a datatable, then this is the proxy they specified. + SendVarProxyFn m_ProxyFn; // If it's a non-datatable, then this is the proxy they specified. + EnsureCapacityFn m_EnsureCapacityFn; + int m_ElementStride; // Distance between each element in the array. + int m_Offset; // # bytes from the parent structure to its utlvector. + int m_nMaxElements; // For debugging... +}; + + +void SendProxy_UtlVectorElement( + const SendProp *pProp, + const void *pStruct, + const void *pData, + DVariant *pOut, + int iElement, + int objectID ) +{ + CSendPropExtra_UtlVector *pExtra = (CSendPropExtra_UtlVector*)pProp->GetExtraData(); + Assert( pExtra ); + + // Kind of lame overloading element stride to hold the element index, + // but we can easily move it into its SetExtraData stuff if we need to. + iElement = pProp->GetElementStride(); + + // NOTE: this is cheesy, but it does the trick. + CUtlVector *pUtlVec = (CUtlVector*)((char*)pStruct + pExtra->m_Offset); + if ( iElement >= pUtlVec->Count() ) + { + // Pass in zero value. + memset( pOut, 0, sizeof( *pOut ) ); + } + else + { + // Call through to the proxy they passed in, making pStruct=the CUtlVector and forcing iElement to 0. + pExtra->m_ProxyFn( pProp, pData, (char*)pUtlVec->Base() + iElement*pExtra->m_ElementStride, pOut, 0, objectID ); + } +} + +void* SendProxy_UtlVectorElement_DataTable( + const SendProp *pProp, + const void *pStructBase, + const void *pData, + CSendProxyRecipients *pRecipients, + int objectID ) +{ + CSendPropExtra_UtlVector *pExtra = (CSendPropExtra_UtlVector*)pProp->GetExtraData(); + + int iElement = pProp->m_ElementStride; + Assert( iElement < pExtra->m_nMaxElements ); + + // This should have gotten called in SendProxy_LengthTable before we get here, so + // the capacity should be correct. +#ifdef _DEBUG + pExtra->m_EnsureCapacityFn( (void*)pStructBase, pExtra->m_Offset, pExtra->m_nMaxElements ); +#endif + + // NOTE: this is cheesy because we're assuming the type of the template class, but it does the trick. + CUtlVector *pUtlVec = (CUtlVector*)((char*)pStructBase + pExtra->m_Offset); + + // Call through to the proxy they passed in, making pStruct=the CUtlVector and forcing iElement to 0. + return pExtra->m_DataTableProxyFn( pProp, pData, (char*)pUtlVec->Base() + iElement*pExtra->m_ElementStride, pRecipients, objectID ); +} + +void SendProxy_UtlVectorLength( + const SendProp *pProp, + const void *pStruct, + const void *pData, + DVariant *pOut, + int iElement, + int objectID ) +{ + CSendPropExtra_UtlVector *pExtra = (CSendPropExtra_UtlVector*)pProp->GetExtraData(); + + // NOTE: this is cheesy because we're assuming the type of the template class, but it does the trick. + CUtlVector *pUtlVec = (CUtlVector*)((char*)pStruct + pExtra->m_Offset); + + // Don't let them overflow the buffer because they might expect that to get transmitted to the client. + pOut->m_Int = pUtlVec->Count(); + if ( pOut->m_Int > pExtra->m_nMaxElements ) + { + Assert( false ); + pOut->m_Int = pExtra->m_nMaxElements; + } +} + + +void* SendProxy_LengthTable( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) +{ + // Make sure the array has space to hold all the elements. + CSendPropExtra_UtlVector *pExtra = (CSendPropExtra_UtlVector*)pProp->GetExtraData(); + pExtra->m_EnsureCapacityFn( (void*)pStructBase, pExtra->m_Offset, pExtra->m_nMaxElements ); + return (void*)pData; +} + + +// Note: your pArrayProp will NOT get iElement set to anything other than 0, because this function installs its +// own proxy in front of yours. pStruct will point at the CUtlVector and pData will point at the element in the CUtlVector.It will pass you the direct pointer to the element inside the CUtlVector. +// +// You can skip the first 3 parameters in pArrayProp because they're ignored. So your array specification +// could look like this: +// SendPropUtlVector( +// SENDINFO_UTLVECTOR( m_FloatArray ), +// SendPropFloat( NULL, 0, 0, 0 [# bits], SPROP_NOSCALE [flags] ) ); +// +// Note: you have to be DILIGENT about calling NetworkStateChanged whenever an element in your CUtlVector changes +// since CUtlVector doesn't do this automatically. +SendProp SendPropUtlVector( + char *pVarName, // Use SENDINFO_UTLVECTOR to generate these 4. + int offset, // Used to generate pData in the function specified in varProxy. + int sizeofVar, // The size of each element in the utlvector. + EnsureCapacityFn ensureFn, // This is the value returned for elements out of the array's current range. + int nMaxElements, // Max # of elements in the array. Keep this as low as possible. + SendProp pArrayProp, // Describe the data inside of each element in the array. + SendTableProxyFn varProxy // This can be overridden to control who the array is sent to. + ) +{ + SendProp ret; + + Assert( nMaxElements <= MAX_ARRAY_ELEMENTS ); + + ret.m_Type = DPT_DataTable; + ret.m_pVarName = pVarName; + ret.SetOffset( 0 ); + ret.SetDataTableProxyFn( varProxy ); + + // Handle special proxy types where they always let all clients get the results. + if ( varProxy == SendProxy_DataTableToDataTable || varProxy == SendProxy_DataTablePtrToDataTable ) + { + ret.SetFlags( SPROP_PROXY_ALWAYS_YES ); + } + + + // Extra data bound to each of the properties. + CSendPropExtra_UtlVector *pExtraData = new CSendPropExtra_UtlVector; + + pExtraData->m_nMaxElements = nMaxElements; + pExtraData->m_ElementStride = sizeofVar; + pExtraData->m_EnsureCapacityFn = ensureFn; + pExtraData->m_Offset = offset; + + if ( pArrayProp.m_Type == DPT_DataTable ) + pExtraData->m_DataTableProxyFn = pArrayProp.GetDataTableProxyFn(); + else + pExtraData->m_ProxyFn = pArrayProp.GetProxyFn(); + + + SendProp *pProps = new SendProp[nMaxElements+1]; // TODO free that again + + // The first property is datatable with an int that tells the length of the array. + // It has to go in a datatable, otherwise if this array holds datatable properties, it will be received last. + SendProp *pLengthProp = new SendProp; + *pLengthProp = SendPropInt( AllocateStringHelper( "lengthprop%d", nMaxElements ), 0, 0, NumBitsForCount( nMaxElements ), SPROP_UNSIGNED, SendProxy_UtlVectorLength ); + pLengthProp->SetExtraData( pExtraData ); + + char *pLengthProxyTableName = AllocateUniqueDataTableName( true, "_LPT_%s_%d", pVarName, nMaxElements ); + SendTable *pLengthTable = new SendTable( pLengthProp, 1, pLengthProxyTableName ); + pProps[0] = SendPropDataTable( "lengthproxy", 0, pLengthTable, SendProxy_LengthTable ); + pProps[0].SetExtraData( pExtraData ); + + + // The first element is a sub-datatable. + for ( int i = 1; i < nMaxElements+1; i++ ) + { + pProps[i] = pArrayProp; // copy array element property setting + pProps[i].SetOffset( 0 ); // leave offset at 0 so pStructBase is always a pointer to the CUtlVector + pProps[i].m_pVarName = s_ElementNames[i-1]; // give unique name + pProps[i].SetExtraData( pExtraData ); + pProps[i].m_ElementStride = i-1; // Kind of lame overloading element stride to hold the element index, + // but we can easily move it into its SetExtraData stuff if we need to. + + // We provide our own proxy here. + if ( pArrayProp.m_Type == DPT_DataTable ) + { + pProps[i].SetDataTableProxyFn( SendProxy_UtlVectorElement_DataTable ); + pProps[i].SetFlags( SPROP_PROXY_ALWAYS_YES ); + } + else + { + pProps[i].SetProxyFn( SendProxy_UtlVectorElement ); + } + } + + SendTable *pTable = new SendTable( + pProps, + nMaxElements+1, + AllocateUniqueDataTableName( true, "_ST_%s_%d", pVarName, nMaxElements ) + ); + + ret.SetDataTable( pTable ); + return ret; +} diff --git a/public/dt_utlvector_send.h b/public/dt_utlvector_send.h new file mode 100644 index 0000000..9094039 --- /dev/null +++ b/public/dt_utlvector_send.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL1. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DT_UTLVECTOR_SEND_H +#define DT_UTLVECTOR_SEND_H +#pragma once + + +#include "dt_send.h" +#include "dt_utlvector_common.h" + + +#define SENDINFO_UTLVECTOR( varName ) #varName, \ + offsetof(currentSendDTClass, varName), \ + sizeof(((currentSendDTClass*)0)->varName[0]), \ + GetEnsureCapacityTemplate( ((currentSendDTClass*)0)->varName ) + + + +#define SendPropUtlVectorDataTable( varName, nMaxElements, dataTableName ) \ + SendPropUtlVector( \ + SENDINFO_UTLVECTOR( varName ), \ + nMaxElements, \ + SendPropDataTable( NULL, 0, &REFERENCE_SEND_TABLE( dataTableName ) ) \ + ) + +// +// Set it up to transmit a CUtlVector of basic types or of structures. +// +// pArrayProp doesn't need a name, offset, or size. You can pass 0 for all those. +// Example usage: +// +// SendPropUtlVectorDataTable( m_StructArray, 11, DT_TestStruct ) +// +// SendPropUtlVector( +// SENDINFO_UTLVECTOR( m_FloatArray ), +// 16, // max elements +// SendPropFloat( NULL, 0, 0, 0, SPROP_NOSCALE ) +// ) +// +SendProp SendPropUtlVector( + char *pVarName, // Use SENDINFO_UTLVECTOR to generate these first 4 parameters. + int offset, + int sizeofVar, + EnsureCapacityFn ensureFn, + + int nMaxElements, // Max # of elements in the array. Keep this as low as possible. + SendProp pArrayProp, // Describe the data inside of each element in the array. + SendTableProxyFn varProxy=SendProxy_DataTableToDataTable // This can be overridden to control who the array is sent to. + ); + + +#endif // DT_UTLVECTOR_SEND_H diff --git a/public/edict.h b/public/edict.h new file mode 100644 index 0000000..44b26ae --- /dev/null +++ b/public/edict.h @@ -0,0 +1,437 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef EDICT_H +#define EDICT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" +#include "cmodel.h" +#include "const.h" +#include "iserverentity.h" +#include "globalvars_base.h" +#include "engine/ICollideable.h" +#include "iservernetworkable.h" +#include "bitvec.h" +#include "tier1/convar.h" + +struct edict_t; + + +//----------------------------------------------------------------------------- +// Purpose: Defines the ways that a map can be loaded. +//----------------------------------------------------------------------------- +enum MapLoadType_t +{ + MapLoad_NewGame = 0, + MapLoad_LoadGame, + MapLoad_Transition, + MapLoad_Background, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Global variables shared between the engine and the game .dll +//----------------------------------------------------------------------------- +class CGlobalVars : public CGlobalVarsBase +{ +public: + + CGlobalVars( bool bIsClient ); + +public: + + // Current map + string_t mapname; + int mapversion; + string_t startspot; + MapLoadType_t eLoadType; // How the current map was loaded + bool bMapLoadFailed; // Map has failed to load, we need to kick back to the main menu + + // game specific flags + bool deathmatch; + bool coop; + bool teamplay; + // current maxentities + int maxEntities; + + int serverCount; + edict_t *pEdicts; +}; + +inline CGlobalVars::CGlobalVars( bool bIsClient ) : + CGlobalVarsBase( bIsClient ) +{ + serverCount = 0; +} + + +class CPlayerState; +class IServerNetworkable; +class IServerEntity; + + +#define FL_EDICT_CHANGED (1<<0) // Game DLL sets this when the entity state changes + // Mutually exclusive with FL_EDICT_PARTIAL_CHANGE. + +#define FL_EDICT_FREE (1<<1) // this edict if free for reuse +#define FL_EDICT_FULL (1<<2) // this is a full server entity + +#define FL_EDICT_FULLCHECK (0<<0) // call ShouldTransmit() each time, this is a fake flag +#define FL_EDICT_ALWAYS (1<<3) // always transmit this entity +#define FL_EDICT_DONTSEND (1<<4) // don't transmit this entity +#define FL_EDICT_PVSCHECK (1<<5) // always transmit entity, but cull against PVS + +// Used by local network backdoor. +#define FL_EDICT_PENDING_DORMANT_CHECK (1<<6) + +// This is always set at the same time EFL_DIRTY_PVS_INFORMATION is set, but it +// gets cleared in a different place. +#define FL_EDICT_DIRTY_PVS_INFORMATION (1<<7) + +// This is used internally to edict_t to remember that it's carrying a +// "full change list" - all its properties might have changed their value. +#define FL_FULL_EDICT_CHANGED (1<<8) + + +// Max # of variable changes we'll track in an entity before we treat it +// like they all changed. +#define MAX_CHANGE_OFFSETS 19 +#define MAX_EDICT_CHANGE_INFOS 100 + + +class CEdictChangeInfo +{ +public: + // Edicts remember the offsets of properties that change + unsigned short m_ChangeOffsets[MAX_CHANGE_OFFSETS]; + unsigned short m_nChangeOffsets; +}; + +// Shared between engine and game DLL. +class CSharedEdictChangeInfo +{ +public: + CSharedEdictChangeInfo() + { + m_iSerialNumber = 1; + } + + // Matched against edict_t::m_iChangeInfoSerialNumber to determine if its + // change info is valid. + unsigned short m_iSerialNumber; + +#ifdef NETWORK_VARS_ENABLED + CEdictChangeInfo m_ChangeInfos[MAX_EDICT_CHANGE_INFOS]; + unsigned short m_nChangeInfos; // How many are in use this frame. +#endif +}; +extern CSharedEdictChangeInfo *g_pSharedChangeInfo; + +class IChangeInfoAccessor +{ +public: + inline void SetChangeInfo( unsigned short info ) + { + m_iChangeInfo = info; + } + + inline void SetChangeInfoSerialNumber( unsigned short sn ) + { + m_iChangeInfoSerialNumber = sn; + } + + inline unsigned short GetChangeInfo() const + { + return m_iChangeInfo; + } + + inline unsigned short GetChangeInfoSerialNumber() const + { + return m_iChangeInfoSerialNumber; + } + +private: + unsigned short m_iChangeInfo; + unsigned short m_iChangeInfoSerialNumber; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +// NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN COMPATIBLE WITH HL2_VC6!!!!! +class CBaseEdict +{ +public: + + // Returns an IServerEntity if FL_FULLEDICT is set or NULL if this + // is a lightweight networking entity. + IServerEntity* GetIServerEntity(); + const IServerEntity* GetIServerEntity() const; + + IServerNetworkable* GetNetworkable(); + IServerUnknown* GetUnknown(); + + // Set when initting an entity. If it's only a networkable, this is false. + void SetEdict( IServerUnknown *pUnk, bool bFullEdict ); + + int AreaNum() const; + const char * GetClassName() const; + + bool IsFree() const; + void SetFree(); + void ClearFree(); + + bool HasStateChanged() const; + void ClearStateChanged(); + void StateChanged(); + void StateChanged( unsigned short offset ); + + void ClearTransmitState(); + + void SetChangeInfo( unsigned short info ); + void SetChangeInfoSerialNumber( unsigned short sn ); + unsigned short GetChangeInfo() const; + unsigned short GetChangeInfoSerialNumber() const; + +public: + + // NOTE: this is in the edict instead of being accessed by a virtual because the engine needs fast access to it. + // NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN COMPATIBLE WITH HL2_VC6!!!!! +#ifdef _XBOX + unsigned short m_fStateFlags; +#else + int m_fStateFlags; +#endif + + // NOTE: this is in the edict instead of being accessed by a virtual because the engine needs fast access to it. + int m_NetworkSerialNumber; // Game DLL sets this when it gets a serial number for its EHANDLE. + + // NOTE: this is in the edict instead of being accessed by a virtual because the engine needs fast access to it. + IServerNetworkable *m_pNetworkable; + +protected: + IServerUnknown *m_pUnk; + + +public: + + IChangeInfoAccessor *GetChangeAccessor(); // The engine implements this and the game .dll implements as + const IChangeInfoAccessor *GetChangeAccessor() const; // The engine implements this and the game .dll implements as + // as callback through to the engine!!! + + // NOTE: YOU CAN'T CHANGE THE LAYOUT OR SIZE OF CBASEEDICT AND REMAIN COMPATIBLE WITH HL2_VC6!!!!! + // This breaks HL2_VC6!!!!! + // References a CEdictChangeInfo with a list of modified network props. + //unsigned short m_iChangeInfo; + //unsigned short m_iChangeInfoSerialNumber; + + friend void InitializeEntityDLLFields( edict_t *pEdict ); +}; + + +//----------------------------------------------------------------------------- +// CBaseEdict inlines. +//----------------------------------------------------------------------------- +inline IServerEntity* CBaseEdict::GetIServerEntity() +{ + if ( m_fStateFlags & FL_EDICT_FULL ) + return (IServerEntity*)m_pUnk; + else + return 0; +} + +inline bool CBaseEdict::IsFree() const +{ + return (m_fStateFlags & FL_EDICT_FREE) != 0; +} + + + +inline bool CBaseEdict::HasStateChanged() const +{ + return (m_fStateFlags & FL_EDICT_CHANGED) != 0; +} + +inline void CBaseEdict::ClearStateChanged() +{ + m_fStateFlags &= ~(FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED); + SetChangeInfoSerialNumber( 0 ); +} + +inline void CBaseEdict::StateChanged() +{ + // Note: this should only happen for properties in data tables that used some kind of pointer + // dereference. If the data is directly offsetable, then changes will automatically be detected + m_fStateFlags |= (FL_EDICT_CHANGED | FL_FULL_EDICT_CHANGED); + SetChangeInfoSerialNumber( 0 ); +} + +inline void CBaseEdict::StateChanged( unsigned short offset ) +{ +#ifdef NETWORK_VARS_ENABLED + if ( m_fStateFlags & FL_FULL_EDICT_CHANGED ) + return; + + m_fStateFlags |= FL_EDICT_CHANGED; + + IChangeInfoAccessor *accessor = GetChangeAccessor(); + + if ( accessor->GetChangeInfoSerialNumber() == g_pSharedChangeInfo->m_iSerialNumber ) + { + // Ok, I still own this one. + CEdictChangeInfo *p = &g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()]; + + // Now add this offset to our list of changed variables. + for ( unsigned short i=0; i < p->m_nChangeOffsets; i++ ) + if ( p->m_ChangeOffsets[i] == offset ) + return; + + if ( p->m_nChangeOffsets == MAX_CHANGE_OFFSETS ) + { + // Invalidate our change info. + accessor->SetChangeInfoSerialNumber( 0 ); + m_fStateFlags |= FL_FULL_EDICT_CHANGED; // So we don't get in here again. + } + else + { + p->m_ChangeOffsets[p->m_nChangeOffsets++] = offset; + } + } + else + { + if ( g_pSharedChangeInfo->m_nChangeInfos == MAX_EDICT_CHANGE_INFOS ) + { + // Shucks.. have to mark the edict as fully changed because we don't have room to remember this change. + accessor->SetChangeInfoSerialNumber( 0 ); + m_fStateFlags |= FL_FULL_EDICT_CHANGED; + } + else + { + // Get a new CEdictChangeInfo and fill it out. + accessor->SetChangeInfo( g_pSharedChangeInfo->m_nChangeInfos ); + g_pSharedChangeInfo->m_nChangeInfos++; + + accessor->SetChangeInfoSerialNumber( g_pSharedChangeInfo->m_iSerialNumber ); + + CEdictChangeInfo *p = &g_pSharedChangeInfo->m_ChangeInfos[accessor->GetChangeInfo()]; + p->m_ChangeOffsets[0] = offset; + p->m_nChangeOffsets = 1; + } + } +#else + StateChanged(); +#endif +} + + + +inline void CBaseEdict::SetFree() +{ + m_fStateFlags |= FL_EDICT_FREE; +} + +inline void CBaseEdict::ClearFree() +{ + m_fStateFlags &= ~FL_EDICT_FREE; +} + +inline void CBaseEdict::ClearTransmitState() +{ + m_fStateFlags &= ~(FL_EDICT_ALWAYS|FL_EDICT_PVSCHECK|FL_EDICT_DONTSEND); +} + +inline const IServerEntity* CBaseEdict::GetIServerEntity() const +{ + if ( m_fStateFlags & FL_EDICT_FULL ) + return (IServerEntity*)m_pUnk; + else + return 0; +} + +inline IServerUnknown* CBaseEdict::GetUnknown() +{ + return m_pUnk; +} + +inline IServerNetworkable* CBaseEdict::GetNetworkable() +{ + return m_pNetworkable; +} + +inline void CBaseEdict::SetEdict( IServerUnknown *pUnk, bool bFullEdict ) +{ + m_pUnk = pUnk; + if ( (pUnk != NULL) && bFullEdict ) + { + m_fStateFlags = FL_EDICT_FULL; + } + else + { + m_fStateFlags = 0; + } +} + +inline int CBaseEdict::AreaNum() const +{ + if ( !m_pUnk ) + return 0; + + return m_pNetworkable->AreaNum(); +} + +inline const char * CBaseEdict::GetClassName() const +{ + if ( !m_pUnk ) + return ""; + return m_pNetworkable->GetClassName(); +} + +inline void CBaseEdict::SetChangeInfo( unsigned short info ) +{ + GetChangeAccessor()->SetChangeInfo( info ); +} + +inline void CBaseEdict::SetChangeInfoSerialNumber( unsigned short sn ) +{ + GetChangeAccessor()->SetChangeInfoSerialNumber( sn ); +} + +inline unsigned short CBaseEdict::GetChangeInfo() const +{ + return GetChangeAccessor()->GetChangeInfo(); +} + +inline unsigned short CBaseEdict::GetChangeInfoSerialNumber() const +{ + return GetChangeAccessor()->GetChangeInfoSerialNumber(); +} + +//----------------------------------------------------------------------------- +// Purpose: The engine's internal representation of an entity, including some +// basic collision and position info and a pointer to the class wrapped on top +// of the structure +//----------------------------------------------------------------------------- +struct edict_t : public CBaseEdict +{ +public: + ICollideable *GetCollideable(); +}; + +inline ICollideable *edict_t::GetCollideable() +{ + IServerEntity *pEnt = GetIServerEntity(); + if ( pEnt ) + return pEnt->GetCollideable(); + else + return NULL; +} + + +#endif // EDICT_H diff --git a/public/editor_sendcommand.cpp b/public/editor_sendcommand.cpp new file mode 100644 index 0000000..ef17a5e --- /dev/null +++ b/public/editor_sendcommand.cpp @@ -0,0 +1,228 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements an interface to the map editor for the execution of +// editor shell commands from another application. Commands allow the +// creation and deletion of entities, AI nodes, and AI node connections. +// +// $NoKeywords: $ +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#if !defined(_X360) && defined(_WIN32) +#include +#endif +#include +#include "editor_sendcommand.h" +#include "tier1/strtools.h" +#include "mathlib/vector.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +const int MAX_COMMAND_BUFFER = 2048; +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to begin an editing session +// of a given map. +// Input : pszMapName - Name of the bsp, without the path or extension: "c1a3a_port" +// nMapVersion - Map version number, from the BSP file. +// Output : Returns Editor_OK on success, an error code if the session was rejected. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_BeginSession(const char *pszMapName, int nMapVersion, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "session_begin %s %d", pszMapName, nMapVersion); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to verify the version and +// map name being edited. +// Input : pszMapName - Name of the bsp, without the path or extension: "c1a3a_port" +// nMapVersion - Map version number, from the BSP file. +// Output : Returns Editor_OK on success, an error code if the session was rejected. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_CheckVersion(const char *pszMapName, int nMapVersion, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "map_check_version %s %d", pszMapName, nMapVersion); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to create an entity at +// a given (x, y, z) coordinate. +// Input : pszEntity - Class name of entity to create, ie "info_player_start". +// x, y, z - World coordinates at which to create entity. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_CreateEntity(const char *pszEntity, float x, float y, float z, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "entity_create %s %g %g %g", pszEntity, x, y, z); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to create an entity at +// a given (x, y, z) coordinate. +// Input : pszNodeClass - Class name of node to create, ie "info_node". +// nID - Unique ID to assign the node. +// x, y, z - World coordinates at which to create node. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_CreateNode(const char *pszNodeClass, int nID, float x, float y, float z, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "node_create %s %d %g %g %g", pszNodeClass, nID, x, y, z); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to create an entity at +// a given (x, y, z) coordinate. +// Input : pszNodeClass - Class name of node to create, ie "info_node". +// nID - Unique ID to assign the node. +// x, y, z - World coordinates at which to create node. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_CreateNodeLink(int nStartID, int nEndID, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "nodelink_create %d %d", nStartID, nEndID); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to delete an entity at +// a given (x, y, z) coordinate. +// Input : pszEntity - Class name of entity to delete, ie "info_player_start". +// x, y, z - World coordinates of entity to delete. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_DeleteEntity(const char *pszEntity, float x, float y, float z, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "entity_delete %s %g %g %g", pszEntity, x, y, z); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +// sets an arbitrary key/value pair in the entity +EditorSendResult_t Editor_SetKeyValue(const char *pszEntity, float x, float y, float z, const char *pKey, const char *pValue, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "entity_set_keyvalue %s %f %f %f \"%s\" \"%s\"", pszEntity, x, y, z, pKey, pValue); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +// applies an incremental rotation to an entity +EditorSendResult_t Editor_RotateEntity(const char *pszEntity, float x, float y, float z, const QAngle &incrementalRotation, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "entity_rotate_incremental %s %f %f %f %f %f %f", pszEntity, x, y, z, incrementalRotation.x, incrementalRotation.y, incrementalRotation.z ); + return(Editor_SendCommand(szCommand, bShowUI)); +} +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to delete an entity at +// a given (x, y, z) coordinate. +// Input : nID - unique ID of node to delete. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_DeleteNode(int nID, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "node_delete %d", nID); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to delete an entity at +// a given (x, y, z) coordinate. +// Input : nStartID - unique ID of one node that the link is connected to. +// nEndID - unique ID of the other node that the link is connected to. +// Output : Returns Editor_OK on success, an error code on failure. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_DeleteNodeLink(int nStartID, int nEndID, bool bShowUI) +{ + char szCommand[MAX_COMMAND_BUFFER]; + Q_snprintf(szCommand,sizeof(szCommand), "nodelink_delete %d %d", nStartID, nEndID); + return(Editor_SendCommand(szCommand, bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Sends a command to the editor (if running) to end the current remote +// editing session. +// Output : Returns Editor_OK on success, an error code if the session was rejected. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_EndSession(bool bShowUI) +{ + return(Editor_SendCommand("session_end", bShowUI)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Attempts to sends a shell command to the editor. +// Input : pszCommand - Shell command to send. +// bShowUI - Whether to display mesage boxes on failure. +// Output : Returns one of the following values: +// Editor_OK - The command was executed successfully. +// Editor_NotRunning - Unable to establish a communications channel with the editor. +// Editor_BadCommand - The editor did not accept the command. +//----------------------------------------------------------------------------- +EditorSendResult_t Editor_SendCommand(const char *pszCommand, bool bShowUI) +{ +#ifdef _WIN32 + HWND hwnd = FindWindow("Worldcraft_ShellMessageWnd", "Worldcraft_ShellMessageWnd"); + if (hwnd != NULL) + { + // + // Fill out the data structure to send to the editor. + // + + COPYDATASTRUCT CopyData; + CopyData.cbData = strlen(pszCommand) + 1; + CopyData.dwData = 0; + CopyData.lpData = (void *)pszCommand; + + if (!SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&CopyData)) + { + if (bShowUI) + { + char szError[1024]; + Q_snprintf(szError,sizeof(szError), "Worldcraft did not accept the command: \n\n\"%s\"\n\n Make sure the command is valid and that Worldcraft is still running properly.", pszCommand); + MessageBox(NULL, szError, "Editor_SendCommand Error", MB_OK); + } + + return(Editor_BadCommand); + } + } + else + { + if (bShowUI) + { + char szError[1024]; + Q_snprintf(szError,sizeof(szError), "Could not contact Worldcraft to send the command: \n\n\"%s\"\n\n Worldcraft does not appear to be running.", pszCommand); + MessageBox(NULL, szError, "Editor_SendCommand Error", MB_OK); + } + + return(Editor_NotRunning); + } +#endif + + return(Editor_OK); +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/editor_sendcommand.h b/public/editor_sendcommand.h new file mode 100644 index 0000000..3f6649b --- /dev/null +++ b/public/editor_sendcommand.h @@ -0,0 +1,52 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines an interface to the map editor for the execution of +// editor shell commands from another application. Commands allow the +// creation and deletion of entities, AI nodes, and AI node connections. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef EDITOR_SENDCOMMAND_H +#define EDITOR_SENDCOMMAND_H +#pragma once + +class QAngle; + +// +// Result codes from Worldcraft_SendCommand. +// +enum EditorSendResult_t +{ + Editor_OK = 0, // Success. + Editor_NotRunning, // Unable to establish a communications channel with the editor. + Editor_BadCommand, // The editor did not accept the command. +}; + + +// +// Wrappers around specific commands for convenience. +// +EditorSendResult_t Editor_BeginSession(const char *pszMapName, int nMapVersion, bool bShowUI = false); +EditorSendResult_t Editor_EndSession(bool bShowUI); +EditorSendResult_t Editor_CheckVersion(const char *pszMapName, int nMapVersion, bool bShowUI = false); + +EditorSendResult_t Editor_CreateEntity(const char *pszEntity, float x, float y, float z, bool bShowUI = false); +EditorSendResult_t Editor_DeleteEntity(const char *pszEntity, float x, float y, float z, bool bShowUI = false); +EditorSendResult_t Editor_SetKeyValue(const char *pszEntity, float x, float y, float z, const char *pKey, const char *pValue, bool bShowUI = false); +EditorSendResult_t Editor_RotateEntity(const char *pszEntity, float x, float y, float z, const QAngle &incrementalRotation, bool bShowUI = false); + +EditorSendResult_t Editor_CreateNode(const char *pszNodeClass, int nID, float x, float y, float z, bool bShowUI = false); +EditorSendResult_t Editor_DeleteNode(int nID, bool bShowUI = false); + +EditorSendResult_t Editor_CreateNodeLink(int nStartID, int nEndID, bool bShowUI = false); +EditorSendResult_t Editor_DeleteNodeLink(int nStartID, int nEndID, bool bShowUI = false); + + +// +// Actually does the work. All the above commands route through this. +// +EditorSendResult_t Editor_SendCommand(const char *pszCommand, bool bShowUI); + + +#endif // EDITOR_SENDCOMMAND_H diff --git a/public/eiface.h b/public/eiface.h new file mode 100644 index 0000000..d6d31f6 --- /dev/null +++ b/public/eiface.h @@ -0,0 +1,764 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef EIFACE_H +#define EIFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "convar.h" +#include "icvar.h" +#include "edict.h" +#include "mathlib/vplane.h" +#include "iserverentity.h" +#include "engine/ivmodelinfo.h" +#include "soundflags.h" +#include "bitvec.h" +#include "engine/iserverplugin.h" +#include "tier1/bitbuf.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class SendTable; +class ServerClass; +class IMoveHelper; +struct Ray_t; +class CGameTrace; +typedef CGameTrace trace_t; +struct typedescription_t; +class CSaveRestoreData; +struct datamap_t; +class SendTable; +class ServerClass; +class IMoveHelper; +struct Ray_t; +struct studiohdr_t; +class CBaseEntity; +class CRestore; +class CSave; +class variant_t; +struct vcollide_t; +class IRecipientFilter; +class CBaseEntity; +class ITraceFilter; +struct client_textmessage_t; +class INetChannelInfo; +class ISpatialPartition; +class IScratchPad3D; +class CStandardSendProxies; +class IAchievementMgr; +class CGamestatsData; +class CSteamID; +class ISPSharedMemory; +class CGamestatsData; + +typedef struct player_info_s player_info_t; + +//----------------------------------------------------------------------------- +// defines +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define DLLEXPORT __stdcall +#else +#define DLLEXPORT /* */ +#endif + +#define INTERFACEVERSION_VENGINESERVER "VEngineServer022" + +struct bbox_t +{ + Vector mins; + Vector maxs; +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface the engine exposes to the game DLL +//----------------------------------------------------------------------------- +abstract_class IVEngineServer +{ +public: + // Tell engine to change level ( "changelevel s1\n" or "changelevel2 s1 s2\n" ) + virtual void ChangeLevel( const char *s1, const char *s2 ) = 0; + + // Ask engine whether the specified map is a valid map file (exists and has valid version number). + virtual int IsMapValid( const char *filename ) = 0; + + // Is this a dedicated server? + virtual bool IsDedicatedServer( void ) = 0; + + // Is in Hammer editing mode? + virtual int IsInEditMode( void ) = 0; + + // get arbitrary launch options + virtual KeyValues* GetLaunchOptions( void ) = 0; + + // Add to the server/client lookup/precache table, the specified string is given a unique index + // NOTE: The indices for PrecacheModel are 1 based + // a 0 returned from those methods indicates the model or sound was not correctly precached + // However, generic and decal are 0 based + // If preload is specified, the file is loaded into the server/client's cache memory before level startup, otherwise + // it'll only load when actually used (which can cause a disk i/o hitch if it occurs during play of a level). + virtual int PrecacheModel( const char *s, bool preload = false ) = 0; + virtual int PrecacheSentenceFile( const char *s, bool preload = false ) = 0; + virtual int PrecacheDecal( const char *name, bool preload = false ) = 0; + virtual int PrecacheGeneric( const char *s, bool preload = false ) = 0; + + // Check's if the name is precached, but doesn't actually precache the name if not... + virtual bool IsModelPrecached( char const *s ) const = 0; + virtual bool IsDecalPrecached( char const *s ) const = 0; + virtual bool IsGenericPrecached( char const *s ) const = 0; + + // Note that sounds are precached using the IEngineSound interface + + // Special purpose PVS checking + // Get the cluster # for the specified position + virtual int GetClusterForOrigin( const Vector &org ) = 0; + // Get the PVS bits for a specified cluster and copy the bits into outputpvs. Returns the number of bytes needed to pack the PVS + virtual int GetPVSForCluster( int cluster, int outputpvslength, unsigned char *outputpvs ) = 0; + // Check whether the specified origin is inside the specified PVS + virtual bool CheckOriginInPVS( const Vector &org, const unsigned char *checkpvs, int checkpvssize ) = 0; + // Check whether the specified worldspace bounding box is inside the specified PVS + virtual bool CheckBoxInPVS( const Vector &mins, const Vector &maxs, const unsigned char *checkpvs, int checkpvssize ) = 0; + + // Returns the server assigned userid for this player. Useful for logging frags, etc. + // returns -1 if the edict couldn't be found in the list of players. + virtual int GetPlayerUserId( const edict_t *e ) = 0; + virtual const char *GetPlayerNetworkIDString( const edict_t *e ) = 0; + virtual bool IsUserIDInUse( int userID ) = 0; // TERROR: used for transitioning + virtual int GetLoadingProgressForUserID( int userID ) = 0; // TERROR: used for transitioning + + // Return the current number of used edict slots + virtual int GetEntityCount( void ) = 0; + + // Get stats info interface for a client netchannel + virtual INetChannelInfo* GetPlayerNetInfo( int playerIndex ) = 0; + + // Allocate space for string and return index/offset of string in global string list + // If iForceEdictIndex is not -1, then it will return the edict with that index. If that edict index + // is already used, it'll return null. + virtual edict_t *CreateEdict( int iForceEdictIndex = -1 ) = 0; + // Remove the specified edict and place back into the free edict list + virtual void RemoveEdict( edict_t *e ) = 0; + + // Memory allocation for entity class data + virtual void *PvAllocEntPrivateData( long cb ) = 0; + virtual void FreeEntPrivateData( void *pEntity ) = 0; + + // Save/restore uses a special memory allocator (which zeroes newly allocated memory, etc.) + virtual void *SaveAllocMemory( size_t num, size_t size ) = 0; + virtual void SaveFreeMemory( void *pSaveMem ) = 0; + + // Emit an ambient sound associated with the specified entity + virtual void EmitAmbientSound( int entindex, const Vector &pos, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float delay = 0.0f ) = 0; + + // Fade out the client's volume level toward silence (or fadePercent) + virtual void FadeClientVolume( const edict_t *pEdict, float fadePercent, float fadeOutSeconds, float holdTime, float fadeInSeconds ) = 0; + + // Sentences / sentence groups + virtual int SentenceGroupPick( int groupIndex, char *name, int nameBufLen ) = 0; + virtual int SentenceGroupPickSequential( int groupIndex, char *name, int nameBufLen, int sentenceIndex, int reset ) = 0; + virtual int SentenceIndexFromName( const char *pSentenceName ) = 0; + virtual const char *SentenceNameFromIndex( int sentenceIndex ) = 0; + virtual int SentenceGroupIndexFromName( const char *pGroupName ) = 0; + virtual const char *SentenceGroupNameFromIndex( int groupIndex ) = 0; + virtual float SentenceLength( int sentenceIndex ) = 0; + + // Issue a command to the command parser as if it was typed at the server console. + virtual void ServerCommand( const char *str ) = 0; + // Execute any commands currently in the command parser immediately (instead of once per frame) + virtual void ServerExecute( void ) = 0; + // Issue the specified command to the specified client (mimics that client typing the command at the console). + virtual void ClientCommand( edict_t *pEdict, const char *szFmt, ... ) FMTFUNCTION( 3, 4 ) = 0; + + // Set the lightstyle to the specified value and network the change to any connected clients. Note that val must not + // change place in memory (use MAKE_STRING) for anything that's not compiled into your mod. + virtual void LightStyle( int style, const char *val ) = 0; + + // Project a static decal onto the specified entity / model (for level placed decals in the .bsp) + virtual void StaticDecal( const Vector &originInEntitySpace, int decalIndex, int entityIndex, int modelIndex, bool lowpriority ) = 0; + + // Given the current PVS(or PAS) and origin, determine which players should hear/receive the message + virtual void Message_DetermineMulticastRecipients( bool usepas, const Vector& origin, CPlayerBitVec& playerbits ) = 0; + + // Begin a message from a server side entity to its client side counterpart (func_breakable glass, e.g.) + virtual bf_write *EntityMessageBegin( int ent_index, ServerClass * ent_class, bool reliable ) = 0; + // Begin a usermessage from the server to the client .dll + virtual bf_write *UserMessageBegin( IRecipientFilter *filter, int msg_type, char const *pchMsgName ) = 0; + // Finish the Entity or UserMessage and dispatch to network layer + virtual void MessageEnd( void ) = 0; + + // Print szMsg to the client console. + virtual void ClientPrintf( edict_t *pEdict, const char *szMsg ) = 0; + + // SINGLE PLAYER/LISTEN SERVER ONLY (just matching the client .dll api for this) + // Prints the formatted string to the notification area of the screen ( down the right hand edge + // numbered lines starting at position 0 + virtual void Con_NPrintf( int pos, const char *fmt, ... ) = 0; + // SINGLE PLAYER/LISTEN SERVER ONLY(just matching the client .dll api for this) + // Similar to Con_NPrintf, but allows specifying custom text color and duration information + virtual void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... ) = 0; + + // Change a specified player's "view entity" (i.e., use the view entity position/orientation for rendering the client view) + virtual void SetView( const edict_t *pClient, const edict_t *pViewent ) = 0; + + // Set the player's crosshair angle + virtual void CrosshairAngle( const edict_t *pClient, float pitch, float yaw ) = 0; + + // Get the current game directory (hl2, tf2, hl1, cstrike, etc.) + virtual void GetGameDir( char *szGetGameDir, int maxlength ) = 0; + + // Used by AI node graph code to determine if .bsp and .ain files are out of date + virtual int CompareFileTime( const char *filename1, const char *filename2, int *iCompare ) = 0; + + // Locks/unlocks the network string tables (.e.g, when adding bots to server, this needs to happen). + // Be sure to reset the lock after executing your code!!! + virtual bool LockNetworkStringTables( bool lock ) = 0; + + // Create a bot with the given name. Returns NULL if fake client can't be created + virtual edict_t *CreateFakeClient( const char *netname ) = 0; + + // Get a convar keyvalue for s specified client + virtual const char *GetClientConVarValue( int clientIndex, const char *name ) = 0; + + // Parse a token from a file + virtual const char *ParseFile( const char *data, char *token, int maxlen ) = 0; + // Copies a file + virtual bool CopyFile( const char *source, const char *destination ) = 0; + + // Reset the pvs, pvssize is the size in bytes of the buffer pointed to by pvs. + // This should be called right before any calls to AddOriginToPVS + virtual void ResetPVS( byte *pvs, int pvssize ) = 0; + // Merge the pvs bits into the current accumulated pvs based on the specified origin ( not that each pvs origin has an 8 world unit fudge factor ) + virtual void AddOriginToPVS( const Vector &origin ) = 0; + + // Mark a specified area portal as open/closed. + // Use SetAreaPortalStates if you want to set a bunch of them at a time. + virtual void SetAreaPortalState( int portalNumber, int isOpen ) = 0; + + // Queue a temp entity for transmission + virtual void PlaybackTempEntity( IRecipientFilter& filter, float delay, const void *pSender, const SendTable *pST, int classID ) = 0; + // Given a node number and the specified PVS, return with the node is in the PVS + virtual int CheckHeadnodeVisible( int nodenum, const byte *pvs, int vissize ) = 0; + // Using area bits, cheeck whether area1 flows into area2 and vice versa (depends on area portal state) + virtual int CheckAreasConnected( int area1, int area2 ) = 0; + // Given an origin, determine which area index the origin is within + virtual int GetArea( const Vector &origin ) = 0; + // Get area portal bit set + virtual void GetAreaBits( int area, unsigned char *bits, int buflen ) = 0; + // Given a view origin (which tells us the area to start looking in) and a portal key, + // fill in the plane that leads out of this area (it points into whatever area it leads to). + virtual bool GetAreaPortalPlane( Vector const &vViewOrigin, int portalKey, VPlane *pPlane ) = 0; + + // Save/restore wrapper - FIXME: At some point we should move this to it's own interface + virtual bool LoadGameState( char const *pMapName, bool createPlayers ) = 0; + virtual void LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ) = 0; + virtual void ClearSaveDir() = 0; + + // Get the pristine map entity lump string. (e.g., used by CS to reload the map entities when restarting a round.) + virtual const char* GetMapEntitiesString() = 0; + + // Text message system -- lookup the text message of the specified name + virtual client_textmessage_t *TextMessageGet( const char *pName ) = 0; + + // Print a message to the server log file + virtual void LogPrint( const char *msg ) = 0; + virtual bool IsLogEnabled() = 0; + // Builds PVS information for an entity + virtual void BuildEntityClusterList( edict_t *pEdict, PVSInfo_t *pPVSInfo ) = 0; + + // A solid entity moved, update spatial partition + virtual void SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool testSurroundingBoundsOnly ) = 0; + // A trigger entity moved, update spatial partition + virtual void TriggerMoved( edict_t *pTriggerEnt, bool testSurroundingBoundsOnly ) = 0; + + // Create/destroy a custom spatial partition + virtual ISpatialPartition *CreateSpatialPartition( const Vector& worldmin, const Vector& worldmax ) = 0; + virtual void DestroySpatialPartition( ISpatialPartition * ) = 0; + + // Draw the brush geometry in the map into the scratch pad. + // Flags is currently unused. + virtual void DrawMapToScratchPad( IScratchPad3D *pPad, unsigned long iFlags ) = 0; + + // This returns which entities, to the best of the server's knowledge, the client currently knows about. + // This is really which entities were in the snapshot that this client last acked. + // This returns a bit vector with one bit for each entity. + // + // USE WITH CARE. Whatever tick the client is really currently on is subject to timing and + // ordering differences, so you should account for about a quarter-second discrepancy in here. + // Also, this will return NULL if the client doesn't exist or if this client hasn't acked any frames yet. + // + // iClientIndex is the CLIENT index, so if you use pPlayer->entindex(), subtract 1. + virtual const CBitVec* GetEntityTransmitBitsForClient( int iClientIndex ) = 0; + + // Is the game paused? + virtual bool IsPaused() = 0; + + // What is the game timescale multiplied with the host_timescale? + virtual float GetTimescale( void ) const = 0; + + // Marks the filename for consistency checking. This should be called after precaching the file. + virtual void ForceExactFile( const char *s ) = 0; + virtual void ForceModelBounds( const char *s, const Vector &mins, const Vector &maxs ) = 0; + virtual void ClearSaveDirAfterClientLoad() = 0; + + // Sets a USERINFO client ConVar for a fakeclient + virtual void SetFakeClientConVarValue( edict_t *pEntity, const char *cvar, const char *value ) = 0; + + // Marks the material (vmt file) for consistency checking. If the client and server have different + // contents for the file, the client's vmt can only use the VertexLitGeneric shader, and can only + // contain $baseTexture and $bumpmap vars. + virtual void ForceSimpleMaterial( const char *s ) = 0; + + // Is the engine in Commentary mode? + virtual int IsInCommentaryMode( void ) = 0; + + // Is the engine running a background map? + virtual bool IsLevelMainMenuBackground( void ) = 0; + + // Mark some area portals as open/closed. It's more efficient to use this + // than a bunch of individual SetAreaPortalState calls. + virtual void SetAreaPortalStates( const int *portalNumbers, const int *isOpen, int nPortals ) = 0; + + // Called when relevant edict state flags change. + virtual void NotifyEdictFlagsChange( int iEdict ) = 0; + + // Only valid during CheckTransmit. Also, only the PVS, networked areas, and + // m_pTransmitInfo are valid in the returned strucutre. + virtual const CCheckTransmitInfo* GetPrevCheckTransmitInfo( edict_t *pPlayerEdict ) = 0; + + virtual CSharedEdictChangeInfo* GetSharedEdictChangeInfo() = 0; + + // Tells the engine we can immdiately re-use all edict indices + // even though we may not have waited enough time + virtual void AllowImmediateEdictReuse( ) = 0; + + // Returns true if the engine is an internal build. i.e. is using the internal bugreporter. + virtual bool IsInternalBuild( void ) = 0; + + virtual IChangeInfoAccessor *GetChangeAccessor( const edict_t *pEdict ) = 0; + + // Name of most recently load .sav file + virtual char const *GetMostRecentlyLoadedFileName() = 0; + virtual char const *GetSaveFileName() = 0; + + // Cleans up the cluster list + virtual void CleanUpEntityClusterList( PVSInfo_t *pPVSInfo ) = 0; + + virtual int GetAppID() = 0; + + virtual bool IsLowViolence() = 0; + + virtual bool IsAnyClientLowViolence() = 0; + + // Call this to find out the value of a cvar on the client. + // + // It is an asynchronous query, and it will call IServerGameDLL::OnQueryCvarValueFinished when + // the value comes in from the client. + // + // Store the return value if you want to match this specific query to the OnQueryCvarValueFinished call. + // Returns InvalidQueryCvarCookie if the entity is invalid. + virtual QueryCvarCookie_t StartQueryCvarValue( edict_t *pPlayerEntity, const char *pName ) = 0; + + virtual void InsertServerCommand( const char *str ) = 0; + + // Fill in the player info structure for the specified player index (name, model, etc.) + virtual bool GetPlayerInfo( int ent_num, player_info_t *pinfo ) = 0; + + // Returns true if this client has been fully authenticated by Steam + virtual bool IsClientFullyAuthenticated( edict_t *pEdict ) = 0; + + // This makes the host run 1 tick per frame instead of checking the system timer to see how many ticks to run in a certain frame. + // i.e. it does the same thing timedemo does. + virtual void SetDedicatedServerBenchmarkMode( bool bBenchmarkMode ) = 0; + + virtual bool IsSplitScreenPlayer( int ent_num ) = 0; + virtual edict_t *GetSplitScreenPlayerAttachToEdict( int ent_num ) = 0; + virtual int GetNumSplitScreenUsersAttachedToEdict( int ent_num ) = 0; + virtual edict_t *GetSplitScreenPlayerForEdict( int ent_num, int nSlot ) = 0; + + // Used by Foundry to hook into the loadgame process and override the entities that are getting loaded. + virtual bool IsOverrideLoadGameEntsOn() = 0; + + // Used by Foundry when it changes an entity (and possibly its class) but preserves its serial number. + virtual void ForceFlushEntity( int iEntity ) = 0; + + //Finds or Creates a shared memory space, the returned pointer will automatically be AddRef()ed + virtual ISPSharedMemory *GetSinglePlayerSharedMemorySpace( const char *szName, int ent_num = MAX_EDICTS ) = 0; + + // Allocate hunk memory + virtual void *AllocLevelStaticData( size_t bytes ) = 0; + + // Gets a list of all clusters' bounds. Returns total number of clusters. + virtual int GetClusterCount() = 0; + virtual int GetAllClusterBounds( bbox_t *pBBoxList, int maxBBox ) = 0; + + virtual bool IsCreatingReslist() = 0; + virtual bool IsCreatingXboxReslist() = 0; + virtual bool IsDedicatedServerForXbox() = 0; + + virtual void Pause( bool bPause, bool bForce = false ) = 0; + + virtual void SetTimescale( float flTimescale ) = 0; + + // Methods to set/get a gamestats data container so client & server running in same process can send combined data + virtual void SetGamestatsData( CGamestatsData *pGamestatsData ) = 0; + virtual CGamestatsData *GetGamestatsData() = 0; + + // Returns the SteamID of the specified player. It'll be NULL if the player hasn't authenticated yet. + virtual const CSteamID *GetClientSteamID( edict_t *pPlayerEdict ) = 0; + + // Returns the SteamID of the game server + virtual const CSteamID *GetGameServerSteamID() = 0; + + // Validate session + virtual void HostValidateSession() = 0; + + // Update the 360 pacifier/spinner + virtual void RefreshScreenIfNecessary() = 0; + + // Tells the engine to allocate paint surfaces + virtual bool HasPaintmap() = 0; + + // Calls ShootPaintSphere + virtual bool SpherePaintSurface( const model_t *pModel, const Vector &, unsigned char, float, float ) = 0; + + virtual void SphereTracePaintSurface( const model_t *pModel, const Vector &, const Vector &, float, CUtlVector> & ) = 0; + + virtual void RemoveAllPaint() = 0; + + virtual void PaintAllSurfaces( unsigned char ) = 0; + virtual void RemovePaint( const model_t *pModel ) = 0; + + // Send a client command keyvalues + // keyvalues are deleted inside the function + virtual void ClientCommandKeyValues( edict_t *pEdict, KeyValues *pCommand ) = 0; + + // Returns the XUID of the specified player. It'll be NULL if the player hasn't connected yet. + virtual uint64 GetClientXUID( edict_t *pPlayerEdict ) = 0; + virtual bool IsActiveApp() = 0; + + virtual void SetNoClipEnabled( bool bEnabled ) = 0; + + virtual void GetPaintmapDataRLE( CUtlVector> &mapdata ) = 0; + virtual void LoadPaintmapDataRLE( CUtlVector> &mapdata ) = 0; + virtual void SendPaintmapDataToClient( edict_t *pEdict ) = 0; + + virtual float GetLatencyForChoreoSounds() = 0; + + virtual int GetClientCrossPlayPlatform( int client_index ) = 0; +}; + +#define INTERFACEVERSION_SERVERGAMEDLL "ServerGameDLL005" + +//----------------------------------------------------------------------------- +// Purpose: These are the interfaces that the game .dll exposes to the engine +//----------------------------------------------------------------------------- +abstract_class IServerGameDLL +{ +public: + // Initialize the game (one-time call when the DLL is first loaded ) + // Return false if there is an error during startup. + virtual bool DLLInit( CreateInterfaceFn engineFactory, + CreateInterfaceFn physicsFactory, + CreateInterfaceFn fileSystemFactory, + CGlobalVars *pGlobals) = 0; + + // This is called when a new game is started. (restart, map) + virtual bool GameInit( void ) = 0; + + // Called any time a new level is started (after GameInit() also on level transitions within a game) + virtual bool LevelInit( char const *pMapName, + char const *pMapEntities, char const *pOldLevel, + char const *pLandmarkName, bool loadGame, bool background ) = 0; + + // The server is about to activate + virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) = 0; + + // The server should run physics/think on all edicts + virtual void GameFrame( bool simulating ) = 0; + + // Called once per simulation frame on the final tick + virtual void PreClientUpdate( bool simulating ) = 0; + + // Called when a level is shutdown (including changing levels) + virtual void LevelShutdown( void ) = 0; + // This is called when a game ends (server disconnect, death, restart, load) + // NOT on level transitions within a game + virtual void GameShutdown( void ) = 0; + + // Called once during DLL shutdown + virtual void DLLShutdown( void ) = 0; + + // Get the simulation interval (must be compiled with identical values into both client and game .dll for MOD!!!) + // Right now this is only requested at server startup time so it can't be changed on the fly, etc. + virtual float GetTickInterval( void ) const = 0; + + // Give the list of datatable classes to the engine. The engine matches class names from here with + // edict_t::classname to figure out how to encode a class's data for networking + virtual ServerClass* GetAllServerClasses( void ) = 0; + + // Returns string describing current .dll. e.g., TeamFortress 2, Half-Life 2. + // Hey, it's more descriptive than just the name of the game directory + virtual const char *GetGameDescription( void ) = 0; + + // Let the game .dll allocate it's own network/shared string tables + virtual void CreateNetworkStringTables( void ) = 0; + + // Save/restore system hooks + virtual CSaveRestoreData *SaveInit( int size ) = 0; + virtual void SaveWriteFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int ) = 0; + virtual void SaveReadFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int ) = 0; + virtual void SaveGlobalState( CSaveRestoreData * ) = 0; + virtual void RestoreGlobalState( CSaveRestoreData * ) = 0; + virtual void PreSave( CSaveRestoreData * ) = 0; + virtual void Save( CSaveRestoreData * ) = 0; + virtual void GetSaveComment( char *comment, int maxlength, float flMinutes, float flSeconds, bool bNoTime = false ) = 0; + virtual void WriteSaveHeaders( CSaveRestoreData * ) = 0; + virtual void ReadRestoreHeaders( CSaveRestoreData * ) = 0; + virtual void Restore( CSaveRestoreData *, bool ) = 0; + virtual bool IsRestoring() = 0; + virtual bool SupportsSaveRestore() = 0; + + // Returns the number of entities moved across the transition + virtual int CreateEntityTransitionList( CSaveRestoreData *, int ) = 0; + // Build the list of maps adjacent to the current map + virtual void BuildAdjacentMapList( void ) = 0; + + // Retrieve info needed for parsing the specified user message + virtual bool GetUserMessageInfo( int msg_type, char *name, int maxnamelength, int& size ) = 0; + + // Hand over the StandardSendProxies in the game DLL's module. + virtual CStandardSendProxies* GetStandardSendProxies() = 0; + + // Called once during startup, after the game .dll has been loaded and after the client .dll has also been loaded + virtual void PostInit() = 0; + // Called once per frame even when no level is loaded... + virtual void Think( bool finalTick ) = 0; + +#ifdef _XBOX + virtual void GetTitleName( const char *pMapName, char* pTitleBuff, int titleBuffSize ) = 0; +#endif + + virtual void PreSaveGameLoaded( char const *pSaveName, bool bCurrentlyInGame ) = 0; + + // Returns true if the game DLL wants the server not to be made public. + // Used by commentary system to hide multiplayer commentary servers from the master. + virtual bool ShouldHideServer( void ) = 0; + + virtual void InvalidateMdlCache() = 0; + + // * This function is new with version 6 of the interface. + // + // This is called when a query from IServerPluginHelpers::StartQueryCvarValue is finished. + // iCookie is the value returned by IServerPluginHelpers::StartQueryCvarValue. + // Added with version 2 of the interface. + virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue ) = 0; + + // Called after tools are initialized (i.e. when Foundry is initialized so we can get IServerFoundry). + virtual void PostToolsInit() = 0; + + // Called after the steam API has been activated post-level startup + virtual void GameServerSteamAPIActivated( void ) = 0; + + // Called to apply lobby settings to a dedicated server + virtual void ApplyGameSettings( KeyValues *pKV ) = 0; + + // + virtual void GetMatchmakingTags( char *buf, size_t bufSize ) = 0; + + virtual void ServerHibernationUpdate( bool bHibernating ) = 0; + + virtual bool ShouldPreferSteamAuth() = 0; +}; + +//----------------------------------------------------------------------------- +// Just an interface version name for the random number interface +// See vstdlib/random.h for the interface definition +// NOTE: If you change this, also change VENGINE_CLIENT_RANDOM_INTERFACE_VERSION in cdll_int.h +//----------------------------------------------------------------------------- +#define VENGINE_SERVER_RANDOM_INTERFACE_VERSION "VEngineRandom001" + +#define INTERFACEVERSION_SERVERGAMEENTS "ServerGameEnts001" +//----------------------------------------------------------------------------- +// Purpose: Interface to get at server entities +//----------------------------------------------------------------------------- +abstract_class IServerGameEnts +{ +public: + virtual ~IServerGameEnts() {} + + // The engine wants to mark two entities as touching + virtual void MarkEntitiesAsTouching( edict_t *e1, edict_t *e2 ) = 0; + + // Frees the entity attached to this edict + virtual void FreeContainingEntity( edict_t * ) = 0; + + // This allows the engine to get at edicts in a CGameTrace. + virtual edict_t* BaseEntityToEdict( CBaseEntity *pEnt ) = 0; + virtual CBaseEntity* EdictToBaseEntity( edict_t *pEdict ) = 0; + + // This sets a bit in pInfo for each edict in the list that wants to be transmitted to the + // client specified in pInfo. + // + // This is also where an entity can force other entities to be transmitted if it refers to them + // with ehandles. + virtual void CheckTransmit( CCheckTransmitInfo *pInfo, const unsigned short *pEdictIndices, int nEdicts ) = 0; + + // TERROR: Perform any PVS cleanup before a full update + virtual void PrepareForFullUpdate( edict_t *pEdict ) = 0; +}; + +#define INTERFACEVERSION_SERVERGAMECLIENTS "ServerGameClients004" + +//----------------------------------------------------------------------------- +// Purpose: Player / Client related functions +//----------------------------------------------------------------------------- +abstract_class IServerGameClients +{ +public: + // Get server maxplayers and lower bound for same + virtual void GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const = 0; + + // Client is connecting to server ( return false to reject the connection ) + // You can specify a rejection message by writing it into reject + virtual bool ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) = 0; + + // Client is going active + // If bLoadGame is true, don't spawn the player because its state is already setup. + virtual void ClientActive( edict_t *pEntity, bool bLoadGame ) = 0; + + virtual void ClientFullyConnect( edict_t *pEntity ) = 0; + + // Client is disconnecting from server + virtual void ClientDisconnect( edict_t *pEntity ) = 0; + + // Client is connected and should be put in the game + virtual void ClientPutInServer( edict_t *pEntity, char const *playername ) = 0; + + // The client has typed a command at the console + virtual void ClientCommand( edict_t *pEntity, const CCommand &args ) = 0; + + // Sets the client index for the client who typed the command into his/her console + virtual void SetCommandClient( int index ) = 0; + + // A player changed one/several replicated cvars (name etc) + virtual void ClientSettingsChanged( edict_t *pEdict ) = 0; + + // Determine PVS origin and set PVS for the player/viewentity + virtual void ClientSetupVisibility( edict_t *pViewEntity, edict_t *pClient, unsigned char *pvs, int pvssize ) = 0; + + // A block of CUserCmds has arrived from the user, decode them and buffer for execution during player simulation + virtual float ProcessUsercmds( edict_t *player, bf_read *buf, int numcmds, int totalcmds, + int dropped_packets, bool ignore, bool paused ) = 0; + + // Let the game .dll do stuff after messages have been sent to all of the clients once the server frame is complete + virtual void PostClientMessagesSent( void ) = 0; + + // For players, looks up the CPlayerState structure corresponding to the player + virtual CPlayerState *GetPlayerState( edict_t *player ) = 0; + + // Get the ear position for a specified client + virtual void ClientEarPosition( edict_t *pEntity, Vector *pEarOrigin ) = 0; + + // returns number of delay ticks if player is in Replay mode (0 = no delay) + virtual int GetReplayDelay( edict_t *player, int& entity ) = 0; + + // Anything this game .dll wants to add to the bug reporter text (e.g., the entity/model under the picker crosshair) + // can be added here + virtual void GetBugReportInfo( char *buf, int buflen ) = 0; + + // TERROR: A player sent a voice packet + virtual void ClientVoice( edict_t *pEdict ) = 0; + + // A user has had their network id setup and validated + virtual void NetworkIDValidated( const char *pszUserName, const char *pszNetworkID ) = 0; + + // Returns max splitscreen slot count ( 1 == no splits, 2 for 2-player split screen ) + virtual int GetMaxSplitscreenPlayers() = 0; + + // Return # of human slots, -1 if can't determine or don't care (engine will assume it's == maxplayers ) + virtual int GetMaxHumanPlayers() = 0; + + // The client has submitted a keyvalues command + virtual void ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues ) = 0; +}; + +#define INTERFACEVERSION_UPLOADGAMESTATS "ServerUploadGameStats001" + +abstract_class IUploadGameStats +{ +public: + // Note that this call will block the server until the upload is completed, so use only at levelshutdown if at all. + virtual bool UploadGameStats( + char const *mapname, // Game map name + unsigned int blobversion, // Version of the binary blob data + unsigned int blobsize, // Size in bytes of blob data + const void *pvBlobData ) = 0; // Pointer to the blob data. + + // Call when created to init the CSER connection + virtual void InitConnection( void ) = 0; + + // Call periodically to poll steam for a CSER connection + virtual void UpdateConnection( void ) = 0; + + // If user has disabled stats tracking, do nothing + virtual bool IsGameStatsLoggingEnabled() = 0; + + // Gets a non-personally identifiable unique ID for this steam user, used for tracking total gameplay time across + // multiple stats sessions, but isn't trackable back to their Steam account or id. + // Buffer should be 16 bytes, ID will come back as a hexadecimal string version of a GUID + virtual void GetPseudoUniqueId( char *buf, size_t bufsize ) = 0; + + // For determining general % of users running using cyber cafe accounts... + virtual bool IsCyberCafeUser( void ) = 0; + + // Only works in single player + virtual bool IsHDREnabled( void ) = 0; +}; + +#define INTERFACEVERSION_PLUGINHELPERSCHECK "PluginHelpersCheck001" + +//----------------------------------------------------------------------------- +// Purpose: allows the game dll to control which plugin functions can be run +//----------------------------------------------------------------------------- +abstract_class IPluginHelpersCheck +{ +public: + virtual bool CreateMessage( const char *plugin, edict_t *pEntity, DIALOG_TYPE type, KeyValues *data ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface exposed from the client .dll back to the engine for specifying shared .dll IAppSystems (e.g., ISoundEmitterSystem) +//----------------------------------------------------------------------------- +abstract_class IServerDLLSharedAppSystems +{ +public: + virtual int Count() = 0; + virtual char const *GetDllName( int idx ) = 0; + virtual char const *GetInterfaceName( int idx ) = 0; +}; + +#define SERVER_DLL_SHARED_APPSYSTEMS "VServerDllSharedAppSystems001" + +#define INTERFACEVERSION_SERVERGAMETAGS "ServerGameTags001" + +//----------------------------------------------------------------------------- +// Purpose: querying the game dll for Server cvar tags +//----------------------------------------------------------------------------- +abstract_class IServerGameTags +{ +public: + // Get the list of cvars that require tags to show differently in the server browser + virtual void GetTaggedConVarList( KeyValues *pCvarTagList ) = 0; +}; + +#endif // EIFACE_H diff --git a/public/engine/IClientLeafSystem.h b/public/engine/IClientLeafSystem.h new file mode 100644 index 0000000..12e600c --- /dev/null +++ b/public/engine/IClientLeafSystem.h @@ -0,0 +1,54 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +// +// This file contains code to allow us to associate client data with bsp leaves. +// +//=============================================================================// + +#if !defined( ICLIENTLEAFSYSTEM_H ) +#define ICLIENTLEAFSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/platform.h" +#include "client_render_handle.h" +#include "engine/ivmodelinfo.h" + + +#define CLIENTLEAFSYSTEM_INTERFACE_VERSION "ClientLeafSystem002" + + +enum RenderableModelType_t +{ + RENDERABLE_MODEL_UNKNOWN_TYPE = -1, + RENDERABLE_MODEL_ENTITY = 0, + RENDERABLE_MODEL_STUDIOMDL, + RENDERABLE_MODEL_STATIC_PROP, + RENDERABLE_MODEL_BRUSH, +}; + + +//----------------------------------------------------------------------------- +// The client leaf system +//----------------------------------------------------------------------------- +abstract_class IClientLeafSystemEngine +{ +public: + // Adds and removes renderables from the leaf lists + // CreateRenderableHandle stores the handle inside pRenderable. + virtual void CreateRenderableHandle( IClientRenderable* pRenderable, bool bRenderWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType, uint32 nSplitscreenEnabled = 0xFFFFFFFF ) = 0; // = RENDERABLE_MODEL_UNKNOWN_TYPE ) = 0; + virtual void RemoveRenderable( ClientRenderHandle_t handle ) = 0; + virtual void AddRenderableToLeaves( ClientRenderHandle_t renderable, int nLeafCount, unsigned short *pLeaves ) = 0; + virtual void SetTranslucencyType( ClientRenderHandle_t handle, RenderableTranslucencyType_t nType ) = 0; +}; + + +#endif // ICLIENTLEAFSYSTEM_H + + diff --git a/public/engine/ICollideable.h b/public/engine/ICollideable.h new file mode 100644 index 0000000..96dae0f --- /dev/null +++ b/public/engine/ICollideable.h @@ -0,0 +1,82 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ENGINE_ICOLLIDEABLE_H +#define ENGINE_ICOLLIDEABLE_H +#ifdef _WIN32 +#pragma once +#endif + + +enum SolidType_t; +class IHandleEntity; +struct Ray_t; +struct model_t; +class Vector; +class QAngle; +class CGameTrace; +typedef CGameTrace trace_t; +class IClientUnknown; + + +abstract_class ICollideable +{ +public: + // Gets at the entity handle associated with the collideable + virtual IHandleEntity *GetEntityHandle() = 0; + + // These methods return the bounds of an OBB measured in "collision" space + // which can be retreived through the CollisionToWorldTransform or + // GetCollisionOrigin/GetCollisionAngles methods + virtual const Vector& OBBMins( ) const = 0; + virtual const Vector& OBBMaxs( ) const = 0; + + // Returns the bounds of a world-space box used when the collideable is being traced + // against as a trigger. It's only valid to call these methods if the solid flags + // have the FSOLID_USE_TRIGGER_BOUNDS flag set. + virtual void WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const = 0; + + // custom collision test + virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) = 0; + + // Perform hitbox test, returns true *if hitboxes were tested at all*!! + virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) = 0; + + // Returns the BRUSH model index if this is a brush model. Otherwise, returns -1. + virtual int GetCollisionModelIndex() = 0; + + // Return the model, if it's a studio model. + virtual const model_t* GetCollisionModel() = 0; + + // Get angles and origin. + virtual const Vector& GetCollisionOrigin() const = 0; + virtual const QAngle& GetCollisionAngles() const = 0; + virtual const matrix3x4_t& CollisionToWorldTransform() const = 0; + + // Return a SOLID_ define. + virtual SolidType_t GetSolid() const = 0; + virtual int GetSolidFlags() const = 0; + + // Gets at the containing class... + virtual IClientUnknown* GetIClientUnknown() = 0; + + // We can filter out collisions based on collision group + virtual int GetCollisionGroup() const = 0; + + // Returns a world-aligned box guaranteed to surround *everything* in the collision representation + // Note that this will surround hitboxes, trigger bounds, physics. + // It may or may not be a tight-fitting box and its volume may suddenly change + virtual void WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ) = 0; + + virtual bool ShouldTouchTrigger( int triggerSolidFlags ) const = 0; + + // returns NULL unless this collideable has specified FSOLID_ROOT_PARENT_ALIGNED + virtual const matrix3x4_t *GetRootParentToWorldTransform() const = 0; +}; + + +#endif // ENGINE_ICOLLIDEABLE_H diff --git a/public/engine/IEngineSound.h b/public/engine/IEngineSound.h new file mode 100644 index 0000000..bd6cff5 --- /dev/null +++ b/public/engine/IEngineSound.h @@ -0,0 +1,128 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Client-server neutral sound interface +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IENGINESOUND_H +#define IENGINESOUND_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "basetypes.h" +#include "interface.h" +#include "soundflags.h" +#include "irecipientfilter.h" +#include "utlvector.h" +#include "engine/SndInfo.h" + +//----------------------------------------------------------------------------- +// forward declaration +//----------------------------------------------------------------------------- +class Vector; + +// Handy defines for EmitSound +#define SOUND_FROM_LOCAL_PLAYER -1 +#define SOUND_FROM_WORLD 0 + + + +// These are used to feed a soundlevel to the sound system and have it use +// goldsrc-type attenuation. We should use this as little as possible and +// phase it out as soon as possible. + +// Take a regular sndlevel and convert it to compatibility mode. +#define SNDLEVEL_TO_COMPATIBILITY_MODE( x ) ((soundlevel_t)(int)( (x) + 256 )) + +// Take a compatibility-mode sndlevel and get the REAL sndlevel out of it. +#define SNDLEVEL_FROM_COMPATIBILITY_MODE( x ) ((soundlevel_t)(int)( (x) - 256 )) + +// Tells if the given sndlevel is marked as compatibility mode. +#define SNDLEVEL_IS_COMPATIBILITY_MODE( x ) ( (x) >= 256 ) + + + +//----------------------------------------------------------------------------- +// Client-server neutral effects interface +//----------------------------------------------------------------------------- +#define IENGINESOUND_CLIENT_INTERFACE_VERSION "IEngineSoundClient003" +#define IENGINESOUND_SERVER_INTERFACE_VERSION "IEngineSoundServer003" + +abstract_class IEngineSound +{ +public: + // Precache a particular sample + virtual bool PrecacheSound( const char *pSample, bool bPreload = false, bool bIsUISound = false ) = 0; + virtual bool IsSoundPrecached( const char *pSample ) = 0; + virtual void PrefetchSound( const char *pSample ) = 0; + virtual bool IsLoopingSound( const char *pSample ) = 0; + + // Just loads the file header and checks for duration (not hooked up for .mp3's yet) + // Is accessible to server and client though + virtual float GetSoundDuration( const char *pSample ) = 0; + + // Pitch of 100 is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 + // down to 1 is a lower pitch. 150 to 70 is the realistic range. + // EmitSound with pitch != 100 should be used sparingly, as it's not quite as + // fast (the pitchshift mixer is not native coded). + + // NOTE: setting iEntIndex to -1 will cause the sound to be emitted from the local + // player (client-side only) + virtual int EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSoundEntry, unsigned int nSoundEntryHash, const char *pSample, + float flVolume, float flAttenuation, int nSeed, int iFlags = 0, int iPitch = PITCH_NORM, + const Vector *pOrigin = NULL, const Vector *pDirection = NULL, CUtlVector< Vector >* pUtlVecOrigins = NULL, bool bUpdatePositions = true, float soundtime = 0.0f, int speakerentity = -1 ) = 0; + + virtual int EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSoundEntry, unsigned int nSoundEntryHash, const char *pSample, + float flVolume, soundlevel_t iSoundlevel, int nSeed, int iFlags = 0, int iPitch = PITCH_NORM, + const Vector *pOrigin = NULL, const Vector *pDirection = NULL, CUtlVector< Vector >* pUtlVecOrigins = NULL, bool bUpdatePositions = true, float soundtime = 0.0f, int speakerentity = -1 ) = 0; + + virtual void EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex, + float flVolume, soundlevel_t iSoundlevel, int nSeed, int iFlags = 0, int iPitch = PITCH_NORM, + const Vector *pOrigin = NULL, const Vector *pDirection = NULL, CUtlVector< Vector >* pUtlVecOrigins = NULL, bool bUpdatePositions = true, float soundtime = 0.0f, int speakerentity = -1 ) = 0; + + virtual void StopSound( int iEntIndex, int iChannel, const char *pSample, unsigned int nSoundEntryHash ) = 0; + + // stop all active sounds (client only) + virtual void StopAllSounds(bool bClearBuffers) = 0; + + // Set the room type for a player (client only) + virtual void SetRoomType( IRecipientFilter& filter, int roomType ) = 0; + + // Set the dsp preset for a player (client only) + virtual void SetPlayerDSP( IRecipientFilter& filter, int dspType, bool fastReset ) = 0; + + // emit an "ambient" sound that isn't spatialized + // only available on the client, assert on server + virtual int EmitAmbientSound( const char *pSample, float flVolume, int iPitch = PITCH_NORM, int flags = 0, float soundtime = 0.0f ) = 0; + + +// virtual EntChannel_t CreateEntChannel() = 0; + + virtual float GetDistGainFromSoundLevel( soundlevel_t soundlevel, float dist ) = 0; + + // Client .dll only functions + virtual int GetGuidForLastSoundEmitted() = 0; + virtual bool IsSoundStillPlaying( int guid ) = 0; + virtual void StopSoundByGuid( int guid, bool bForceSync ) = 0; + // Set's master volume (0.0->1.0) + virtual void SetVolumeByGuid( int guid, float fvol ) = 0; + + // Retrieves list of all active sounds + virtual void GetActiveSounds( CUtlVector< SndInfo_t >& sndlist ) = 0; + + virtual void PrecacheSentenceGroup( const char *pGroupName ) = 0; + virtual void NotifyBeginMoviePlayback() = 0; + virtual void NotifyEndMoviePlayback() = 0; + + virtual bool GetSoundChannelVolume( const char* sound, float &flVolumeLeft, float &flVolumeRight ) = 0; + + virtual float GetElapsedTimeByGuid( int guid ) = 0; + +}; + + +#endif // IENGINESOUND_H diff --git a/public/engine/IEngineTrace.h b/public/engine/IEngineTrace.h new file mode 100644 index 0000000..1e53d27 --- /dev/null +++ b/public/engine/IEngineTrace.h @@ -0,0 +1,233 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef ENGINE_IENGINETRACE_H +#define ENGINE_IENGINETRACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basehandle.h" +#include "utlvector.h" //need CUtlVector for IEngineTrace::GetBrushesIn*() +#include "mathlib/vector4d.h" +#include "bspflags.h" + +class Vector; +class IHandleEntity; +struct Ray_t; +class CGameTrace; +typedef CGameTrace trace_t; +class ICollideable; +class QAngle; +class ITraceListData; +class CPhysCollide; +struct cplane_t; +struct virtualmeshlist_t; + +//----------------------------------------------------------------------------- +// The standard trace filter... NOTE: Most normal traces inherit from CTraceFilter!!! +//----------------------------------------------------------------------------- +enum TraceType_t +{ + TRACE_EVERYTHING = 0, + TRACE_WORLD_ONLY, // NOTE: This does *not* test static props!!! + TRACE_ENTITIES_ONLY, // NOTE: This version will *not* test static props + TRACE_EVERYTHING_FILTER_PROPS, // NOTE: This version will pass the IHandleEntity for props through the filter, unlike all other filters +}; + +abstract_class ITraceFilter +{ +public: + virtual bool ShouldHitEntity( IHandleEntity *pEntity, int contentsMask ) = 0; + virtual TraceType_t GetTraceType() const = 0; +}; + + +//----------------------------------------------------------------------------- +// Classes are expected to inherit these + implement the ShouldHitEntity method +//----------------------------------------------------------------------------- + +// This is the one most normal traces will inherit from +class CTraceFilter : public ITraceFilter +{ +public: + virtual TraceType_t GetTraceType() const + { + return TRACE_EVERYTHING; + } +}; + +class CTraceFilterEntitiesOnly : public ITraceFilter +{ +public: + virtual TraceType_t GetTraceType() const + { + return TRACE_ENTITIES_ONLY; + } +}; + + +//----------------------------------------------------------------------------- +// Classes need not inherit from these +//----------------------------------------------------------------------------- +class CTraceFilterWorldOnly : public ITraceFilter +{ +public: + bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) + { + return false; + } + virtual TraceType_t GetTraceType() const + { + return TRACE_WORLD_ONLY; + } +}; + +class CTraceFilterWorldAndPropsOnly : public ITraceFilter +{ +public: + bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) + { + return false; + } + virtual TraceType_t GetTraceType() const + { + return TRACE_EVERYTHING; + } +}; + +class CTraceFilterHitAll : public CTraceFilter +{ +public: + virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) + { + return true; + } +}; + + +enum DebugTraceCounterBehavior_t +{ + kTRACE_COUNTER_SET = 0, + kTRACE_COUNTER_INC, +}; + +//----------------------------------------------------------------------------- +// Enumeration interface for EnumerateLinkEntities +//----------------------------------------------------------------------------- +abstract_class IEntityEnumerator +{ +public: + // This gets called with each handle + virtual bool EnumEntity( IHandleEntity *pHandleEntity ) = 0; +}; + + +struct BrushSideInfo_t +{ + Vector4D plane; // The plane of the brush side + unsigned short bevel; // Bevel plane? + unsigned short thin; // Thin? +}; + +//----------------------------------------------------------------------------- +// Interface the engine exposes to the game DLL +//----------------------------------------------------------------------------- +#define INTERFACEVERSION_ENGINETRACE_SERVER "EngineTraceServer004" +#define INTERFACEVERSION_ENGINETRACE_CLIENT "EngineTraceClient004" +abstract_class IEngineTrace +{ +public: + // Returns the contents mask + entity at a particular world-space position + virtual int GetPointContents( const Vector &vecAbsPosition, int contentsMask = MASK_ALL, IHandleEntity** ppEntity = NULL ) = 0; + + // Returns the contents mask of the world only @ the world-space position (static props are ignored) + virtual int GetPointContents_WorldOnly( const Vector &vecAbsPosition, int contentsMask = MASK_ALL ) = 0; + + // Get the point contents, but only test the specific entity. This works + // on static props and brush models. + // + // If the entity isn't a static prop or a brush model, it returns CONTENTS_EMPTY and sets + // bFailed to true if bFailed is non-null. + virtual int GetPointContents_Collideable( ICollideable *pCollide, const Vector &vecAbsPosition ) = 0; + + // Traces a ray against a particular entity + virtual void ClipRayToEntity( const Ray_t &ray, unsigned int fMask, IHandleEntity *pEnt, trace_t *pTrace ) = 0; + + // Traces a ray against a particular entity + virtual void ClipRayToCollideable( const Ray_t &ray, unsigned int fMask, ICollideable *pCollide, trace_t *pTrace ) = 0; + + // A version that simply accepts a ray (can work as a traceline or tracehull) + virtual void TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace ) = 0; + + // A version that sets up the leaf and entity lists and allows you to pass those in for collision. + virtual void SetupLeafAndEntityListRay( const Ray_t &ray, ITraceListData *pTraceData ) = 0; + virtual void SetupLeafAndEntityListBox( const Vector &vecBoxMin, const Vector &vecBoxMax, ITraceListData *pTraceData ) = 0; + virtual void TraceRayAgainstLeafAndEntityList( const Ray_t &ray, ITraceListData *pTraceData, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace ) = 0; + + // A version that sweeps a collideable through the world + // abs start + abs end represents the collision origins you want to sweep the collideable through + // vecAngles represents the collision angles of the collideable during the sweep + virtual void SweepCollideable( ICollideable *pCollide, const Vector &vecAbsStart, const Vector &vecAbsEnd, + const QAngle &vecAngles, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace ) = 0; + + // Enumerates over all entities along a ray + // If triggers == true, it enumerates all triggers along a ray + virtual void EnumerateEntities( const Ray_t &ray, bool triggers, IEntityEnumerator *pEnumerator ) = 0; + + // Same thing, but enumerate entitys within a box + virtual void EnumerateEntities( const Vector &vecAbsMins, const Vector &vecAbsMaxs, IEntityEnumerator *pEnumerator ) = 0; + + // Convert a handle entity to a collideable. Useful inside enumer + virtual ICollideable *GetCollideable( IHandleEntity *pEntity ) = 0; + + // HACKHACK: Temp for performance measurments + virtual int GetStatByIndex( int index, bool bClear ) = 0; + + + //finds brushes in an AABB, prone to some false positives + virtual void GetBrushesInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector *pOutput, int iContentsMask = 0xFFFFFFFF ) = 0; + + //Creates a CPhysCollide out of all displacements wholly or partially contained in the specified AABB + virtual CPhysCollide* GetCollidableFromDisplacementsInAABB( const Vector& vMins, const Vector& vMaxs ) = 0; + + // gets the number of displacements in the world + virtual int GetNumDisplacements( ) = 0; + + // gets a specific diplacement mesh + virtual void GetDisplacementMesh( int nIndex, virtualmeshlist_t *pMeshTriList ) = 0; + + //retrieve brush planes and contents, returns true if data is being returned in the output pointers, false if the brush doesn't exist + virtual bool GetBrushInfo( int iBrush, CUtlVector *pBrushSideInfoOut, int *pContentsOut ) = 0; + + virtual bool PointOutsideWorld( const Vector &ptTest ) = 0; //Tests a point to see if it's outside any playable area + + // Walks bsp to find the leaf containing the specified point + virtual int GetLeafContainingPoint( const Vector &ptTest ) = 0; + + virtual ITraceListData *AllocTraceListData() = 0; + virtual void FreeTraceListData(ITraceListData *) = 0; + + /// Used only in debugging: get/set/clear/increment the trace debug counter. See comment below for details. + virtual int GetSetDebugTraceCounter( int value, DebugTraceCounterBehavior_t behavior ) = 0; +}; + +/// IEngineTrace::GetSetDebugTraceCounter +/// SET to a negative number to disable. SET to a positive number to reset the counter; it'll tick down by +/// one for each trace, and break into the debugger on hitting zero. INC lets you add or subtract from the +/// counter. In each case it will return the value of the counter BEFORE you set or incremented it. INC 0 +/// to query. This end-around approach is necessary for security: because only the engine knows when we +/// are in a trace, and only the server knows when we are in a think, data must somehow be shared between +/// them. Simply returning a pointer to an address inside the engine in a retail build is unacceptable, +/// and in the PC there is no way to distinguish between retail and non-retail builds at compile time +/// (there is no #define for it). +/// This may seem redundant with the VPROF_INCREMENT_COUNTER( "TraceRay" ), but it's not, because while +/// that's readable across DLLs, there's no way to trap on its exceeding a certain value, nor can we reset +/// it for each think. + +#endif // ENGINE_IENGINETRACE_H diff --git a/public/engine/IStaticPropMgr.h b/public/engine/IStaticPropMgr.h new file mode 100644 index 0000000..6aa162f --- /dev/null +++ b/public/engine/IStaticPropMgr.h @@ -0,0 +1,103 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ISTATICPROPMGR_H +#define ISTATICPROPMGR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "mathlib/vector.h" +#include "utlvector.h" +#include "basehandle.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct vcollide_t; +struct Ray_t; +class IClientRenderable; +class CGameTrace; +typedef CGameTrace trace_t; +class IVPhysicsKeyHandler; +class IPhysicsEnvironment; +class ICollideable; +struct RenderableInstance_t; + + +//----------------------------------------------------------------------------- +// Interface versions for static props +//----------------------------------------------------------------------------- +#define INTERFACEVERSION_STATICPROPMGR_CLIENT "StaticPropMgrClient005" +#define INTERFACEVERSION_STATICPROPMGR_SERVER "StaticPropMgrServer002" + + +//----------------------------------------------------------------------------- +// Interface for static props +//----------------------------------------------------------------------------- +abstract_class IStaticPropMgr +{ +public: + // Create physics representations of props + virtual void CreateVPhysicsRepresentations( IPhysicsEnvironment *physenv, IVPhysicsKeyHandler *pDefaults, void *pGameData ) = 0; + + // Purpose: Trace a ray against the specified static Prop. Returns point of intersection in trace_t + virtual void TraceRayAgainstStaticProp( const Ray_t& ray, int staticPropIndex, trace_t& tr ) = 0; + + // Is a base handle a static prop? + virtual bool IsStaticProp( IHandleEntity *pHandleEntity ) const = 0; + virtual bool IsStaticProp( CBaseHandle handle ) const = 0; + + // returns a collideable interface to static props + virtual ICollideable *GetStaticPropByIndex( int propIndex ) = 0; +}; + +abstract_class IStaticPropMgrClient : public IStaticPropMgr +{ +public: + // Adds decals to static props, returns point of decal in trace_t + virtual void AddDecalToStaticProp( const Vector& rayStart, const Vector& rayEnd, + int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr ) = 0; + + // Adds/removes shadows from static props + virtual void AddShadowToStaticProp( unsigned short shadowHandle, IClientRenderable* pRenderable ) = 0; + virtual void RemoveAllShadowsFromStaticProp( IClientRenderable* pRenderable ) = 0; + + // Gets the lighting + material color of a static prop + virtual void GetStaticPropMaterialColorAndLighting( trace_t* pTrace, + int staticPropIndex, Vector& lighting, Vector& matColor ) = 0; + + //Changes made specifically to support the Portal mod (smack Dave Kircher if something breaks) (Added separately to both client and server to not mess with versioning) + //=================================================================== + virtual void GetAllStaticProps( CUtlVector *pOutput ) = 0; //testing function that will eventually be removed + virtual void GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector *pOutput ) = 0; //get all static props that exist wholly or partially in an AABB + virtual void GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector *pOutput ) = 0; //get all static props that exist wholly or partially in an OBB + //=================================================================== + + virtual void DrawStaticProps( IClientRenderable **pProps, const RenderableInstance_t *pInstances, int count, bool bShadowDepth, bool drawVCollideWireframe ) = 0; + + // Returns the lighting origins of a number of static props + virtual void GetLightingOrigins( Vector *pLightingOrigins, int nOriginStride, int nCount, IClientRenderable **ppRenderable, int nRenderableStride ) = 0; +}; + +class IStaticPropMgrServer : public IStaticPropMgr +{ +public: + + + //Changes made specifically to support the Portal mod (smack Dave Kircher if something breaks) (Added separately to both client and server to not mess with versioning) + //=================================================================== + virtual void GetAllStaticProps( CUtlVector *pOutput ) = 0; //testing function that will eventually be removed + virtual void GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector *pOutput ) = 0; //get all static props that exist wholly or partially in an AABB + virtual void GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector *pOutput ) = 0; //get all static props that exist wholly or partially in an OBB + //=================================================================== +}; + + +#endif // IPROPS_H diff --git a/public/engine/SndInfo.h b/public/engine/SndInfo.h new file mode 100644 index 0000000..1f08edb --- /dev/null +++ b/public/engine/SndInfo.h @@ -0,0 +1,66 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef SNDINFO_H +#define SNDINFO_H +#ifdef _WIN32 +#pragma once +#endif + +class Vector; +#include "utlsymbol.h" + +//----------------------------------------------------------------------------- +// Purpose: Client side only +//----------------------------------------------------------------------------- +struct SndInfo_t +{ + // Sound Guid + int m_nGuid; + FileNameHandle_t m_filenameHandle; // filesystem filename handle - call IFilesystem to conver this to a string + int m_nSoundSource; + int m_nChannel; + // If a sound is being played through a speaker entity (e.g., on a monitor,), this is the + // entity upon which to show the lips moving, if the sound has sentence data + int m_nSpeakerEntity; + float m_flVolume; + float m_flLastSpatializedVolume; + // Radius of this sound effect (spatialization is different within the radius) + float m_flRadius; + int m_nPitch; + Vector *m_pOrigin; + Vector *m_pDirection; + + // if true, assume sound source can move and update according to entity + bool m_bUpdatePositions; + // true if playing linked sentence + bool m_bIsSentence; + // if true, bypass all dsp processing for this sound (ie: music) + bool m_bDryMix; + // true if sound is playing through in-game speaker entity. + bool m_bSpeaker; + // for snd_show, networked sounds get colored differently than local sounds + bool m_bFromServer; +}; + +//----------------------------------------------------------------------------- +// Hearing info +//----------------------------------------------------------------------------- +struct AudioState_t +{ + AudioState_t() + { + m_Origin.Init(); + m_Angles.Init(); + m_bIsUnderwater = false; + } + + Vector m_Origin; + QAngle m_Angles; + bool m_bIsUnderwater; +}; + +#endif // SNDINFO_H diff --git a/public/engine/iserverplugin.h b/public/engine/iserverplugin.h new file mode 100644 index 0000000..85f0667 --- /dev/null +++ b/public/engine/iserverplugin.h @@ -0,0 +1,166 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef ISERVERPLUGIN_H +#define ISERVERPLUGIN_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "edict.h" +#include "tier1/interface.h" +#include "tier1/KeyValues.h" + +class CCommand; + +// +// you will also want to listen for game events via IGameEventManager::AddListener() +// + +typedef enum +{ + PLUGIN_CONTINUE = 0, // keep going + PLUGIN_OVERRIDE, // run the game dll function but use our return value instead + PLUGIN_STOP, // don't run the game dll function at all +} PLUGIN_RESULT; + + +typedef enum +{ + eQueryCvarValueStatus_ValueIntact=0, // It got the value fine. + eQueryCvarValueStatus_CvarNotFound=1, + eQueryCvarValueStatus_NotACvar=2, // There's a ConCommand, but it's not a ConVar. + eQueryCvarValueStatus_CvarProtected=3 // The cvar was marked with FCVAR_SERVER_CAN_NOT_QUERY, so the server is not allowed to have its value. +} EQueryCvarValueStatus; + + +typedef int QueryCvarCookie_t; +#define InvalidQueryCvarCookie -1 + + +#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS_VERSION_1 "ISERVERPLUGINCALLBACKS001" +#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS_VERSION_2 "ISERVERPLUGINCALLBACKS002" +#define INTERFACEVERSION_ISERVERPLUGINCALLBACKS "ISERVERPLUGINCALLBACKS003" + +//----------------------------------------------------------------------------- +// Purpose: callbacks the engine exposes to the 3rd party plugins (ala MetaMod) +//----------------------------------------------------------------------------- +abstract_class IServerPluginCallbacks +{ +public: + // Initialize the plugin to run + // Return false if there is an error during startup. + virtual bool Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory ) = 0; + + // Called when the plugin should be shutdown + virtual void Unload( void ) = 0; + + // called when a plugins execution is stopped but the plugin is not unloaded + virtual void Pause( void ) = 0; + + // called when a plugin should start executing again (sometime after a Pause() call) + virtual void UnPause( void ) = 0; + + // Returns string describing current plugin. e.g., Admin-Mod. + virtual const char *GetPluginDescription( void ) = 0; + + // Called any time a new level is started (after GameInit() also on level transitions within a game) + virtual void LevelInit( char const *pMapName ) = 0; + + // The server is about to activate + virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) = 0; + + // The server should run physics/think on all edicts + virtual void GameFrame( bool simulating ) = 0; + + // Called when a level is shutdown (including changing levels) + virtual void LevelShutdown( void ) = 0; + + // Client is going active + virtual void ClientActive( edict_t *pEntity ) = 0; + + // Client is fully connected ( has received initial baseline of entities ) + virtual void ClientFullyConnect( edict_t *pEntity ) = 0; + + // Client is disconnecting from server + virtual void ClientDisconnect( edict_t *pEntity ) = 0; + + // Client is connected and should be put in the game + virtual void ClientPutInServer( edict_t *pEntity, char const *playername ) = 0; + + // Sets the client index for the client who typed the command into their console + virtual void SetCommandClient( int index ) = 0; + + // A player changed one/several replicated cvars (name etc) + virtual void ClientSettingsChanged( edict_t *pEdict ) = 0; + + // Client is connecting to server ( set retVal to false to reject the connection ) + // You can specify a rejection message by writing it into reject + virtual PLUGIN_RESULT ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) = 0; + + // The client has typed a command at the console + virtual PLUGIN_RESULT ClientCommand( edict_t *pEntity, const CCommand &args ) = 0; + + // A user has had their network id setup and validated + virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID ) = 0; + + // This is called when a query from IServerPluginHelpers::StartQueryCvarValue is finished. + // iCookie is the value returned by IServerPluginHelpers::StartQueryCvarValue. + // Added with version 2 of the interface. + virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue ) = 0; + + // added with version 3 of the interface. + virtual void OnEdictAllocated( edict_t *edict ) = 0; + virtual void OnEdictFreed( const edict_t *edict ) = 0; +}; + +#define INTERFACEVERSION_ISERVERPLUGINHELPERS "ISERVERPLUGINHELPERS001" + + +typedef enum +{ + DIALOG_MSG = 0, // just an on screen message !!(DOESN'T WORK IN PORTAL 2!)!! + DIALOG_MENU, // an options menu + DIALOG_TEXT, // a richtext dialog + DIALOG_ENTRY, // an entry box + DIALOG_ASKCONNECT // Ask the client to connect to a specified IP address. Only the "time" and "title" keys are used. +} DIALOG_TYPE; + +//----------------------------------------------------------------------------- +// Purpose: functions that only 3rd party plugins need +//----------------------------------------------------------------------------- +abstract_class IServerPluginHelpers +{ +public: + // creates an onscreen menu with various option buttons + // The keyvalues param can contain these fields: + // "title" - (string) the title to show in the hud and in the title bar + // "msg" - (string) a longer message shown in the GameUI + // "color" - (color) the color to display the message in the hud (white by default) + // "level" - (int) the priority of this message (closer to 0 is higher), only 1 message can be outstanding at a time + // "time" - (int) the time in seconds this message should stay active in the GameUI (min 10 sec, max 200 sec) + // + // For DIALOG_MENU add sub keys for each option with these fields: + // "command" - (string) client command to run if selected + // "msg" - (string) button text for this option + // + virtual void CreateMessage( edict_t *pEntity, DIALOG_TYPE type, KeyValues *data, IServerPluginCallbacks *plugin ) = 0; + virtual void ClientCommand( edict_t *pEntity, const char *cmd ) = 0; + + // Call this to find out the value of a cvar on the client. + // + // It is an asynchronous query, and it will call IServerPluginCallbacks::OnQueryCvarValueFinished when + // the value comes in from the client. + // + // Store the return value if you want to match this specific query to the OnQueryCvarValueFinished call. + // Returns InvalidQueryCvarCookie if the entity is invalid. + virtual QueryCvarCookie_t StartQueryCvarValue( edict_t *pEntity, const char *pName ) = 0; +}; + +#endif //ISERVERPLUGIN_H diff --git a/public/engine/ishadowmgr.h b/public/engine/ishadowmgr.h new file mode 100644 index 0000000..bdfd08f --- /dev/null +++ b/public/engine/ishadowmgr.h @@ -0,0 +1,227 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef ISHADOWMGR_H +#define ISHADOWMGR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "mathlib/vmatrix.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- + +class IMaterial; +class Vector; +class Vector2D; +struct model_t; +typedef unsigned short ModelInstanceHandle_t; +class IClientRenderable; +class ITexture; +struct FlashlightInstance_t; +struct FlashlightState_t; + +// change this when the new version is incompatable with the old +#define ENGINE_SHADOWMGR_INTERFACE_VERSION "VEngineShadowMgr002" + + +//----------------------------------------------------------------------------- +// Flags for the creation method +//----------------------------------------------------------------------------- +enum ShadowFlags_t +{ + SHADOW_FLAGS_FLASHLIGHT = (1 << 0), + SHADOW_FLAGS_SHADOW = (1 << 1), + SHADOW_FLAGS_SIMPLE_PROJECTION = (1 << 2), + + // Update this if you add flags + SHADOW_FLAGS_LAST_FLAG = SHADOW_FLAGS_SIMPLE_PROJECTION +}; + +#define SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ( SHADOW_FLAGS_FLASHLIGHT | SHADOW_FLAGS_SHADOW | SHADOW_FLAGS_SIMPLE_PROJECTION ) + + +//----------------------------------------------------------------------------- +// +// Shadow-related functionality exported by the engine +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// This is a handle to shadows, clients can create as many as they want +//----------------------------------------------------------------------------- +typedef unsigned short ShadowHandle_t; + +enum +{ + SHADOW_HANDLE_INVALID = (ShadowHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Used for the creation Flags field of CreateShadow +//----------------------------------------------------------------------------- +enum ShadowCreateFlags_t +{ + SHADOW_CACHE_VERTS = ( 1 << 0 ), + SHADOW_FLASHLIGHT = ( 1 << 1 ), + SHADOW_SIMPLE_PROJECTION = ( 1 << 2 ), + + SHADOW_LAST_FLAG = SHADOW_SIMPLE_PROJECTION, +}; + + +//----------------------------------------------------------------------------- +// Information about a particular shadow +//----------------------------------------------------------------------------- +struct ShadowInfo_t +{ + // Transforms from world space into texture space of the shadow + VMatrix m_WorldToShadow; + + // The shadow should no longer be drawn once it's further than MaxDist + // along z in shadow texture coordinates. + float m_FalloffOffset; + float m_MaxDist; + float m_FalloffAmount; // how much to lighten the shadow maximally + Vector2D m_TexOrigin; + Vector2D m_TexSize; + unsigned char m_FalloffBias; +}; + +typedef void (*ShadowDrawCallbackFn_t)( void * ); + +typedef void (*ShadowDrawCallbackFn_t)( void * ); + +//----------------------------------------------------------------------------- +// The engine's interface to the shadow manager +//----------------------------------------------------------------------------- +abstract_class IShadowMgr +{ +public: + // Create, destroy shadows (see ShadowCreateFlags_t for creationFlags) + virtual ShadowHandle_t CreateShadow( IMaterial* pMaterial, IMaterial* pModelMaterial, void* pBindProxy, int creationFlags ) = 0; + virtual void DestroyShadow( ShadowHandle_t handle ) = 0; + + // Resets the shadow material (useful for shadow LOD.. doing blobby at distance) + virtual void SetShadowMaterial( ShadowHandle_t handle, IMaterial* pMaterial, IMaterial* pModelMaterial, void* pBindProxy ) = 0; + + // Shadow opacity +// virtual void SetShadowOpacity( ShadowHandle_t handle, float alpha ) = 0; +// virtual float GetShadowOpacity( ShadowHandle_t handle ) const = 0; + + // Project a shadow into the world + // The two points specify the upper left coordinate and the lower-right + // coordinate of the shadow specified in a shadow "viewplane". The + // projection matrix is a shadow viewplane->world transformation, + // and can be orthographic orperspective. + + // I expect that the client DLL will call this method any time the shadow + // changes because the light changes, or because the entity casting the + // shadow moves + + // Note that we can't really control the shadows from the engine because + // the engine only knows about pevs, which don't exist on the client + + // The shadow matrix specifies a world-space transform for the shadow + // the shadow is projected down the z direction, and the origin of the + // shadow matrix is the origin of the projection ray. The size indicates + // the shadow size measured in the space of the shadow matrix; the + // shadow goes from +/- size.x/2 along the x axis of the shadow matrix + // and +/- size.y/2 along the y axis of the shadow matrix. + virtual void ProjectShadow( ShadowHandle_t handle, const Vector &origin, + const Vector& projectionDir, const VMatrix& worldToShadow, const Vector2D& size, + int nLeafCount, const int *pLeafList, + float maxHeight, float falloffOffset, float falloffAmount, const Vector &vecCasterOrigin ) = 0; + + virtual void ProjectFlashlight( ShadowHandle_t handle, const VMatrix &worldToShadow, int nLeafCount, const int *pLeafList ) = 0; + + // Gets at information about a particular shadow + virtual const ShadowInfo_t &GetInfo( ShadowHandle_t handle ) = 0; + + virtual const Frustum_t &GetFlashlightFrustum( ShadowHandle_t handle ) = 0; + + // Methods related to shadows on brush models + virtual void AddShadowToBrushModel( ShadowHandle_t handle, + model_t* pModel, const Vector& origin, const QAngle& angles ) = 0; + + // Removes all shadows from a brush model + virtual void RemoveAllShadowsFromBrushModel( model_t* pModel ) = 0; + + // Sets the texture coordinate range for a shadow... + virtual void SetShadowTexCoord( ShadowHandle_t handle, float x, float y, float w, float h ) = 0; + + // Methods related to shadows on studio models + virtual void AddShadowToModel( ShadowHandle_t shadow, ModelInstanceHandle_t instance ) = 0; + virtual void RemoveAllShadowsFromModel( ModelInstanceHandle_t instance ) = 0; + + // Set extra clip planes related to shadows... + // These are used to prevent pokethru and back-casting + virtual void ClearExtraClipPlanes( ShadowHandle_t shadow ) = 0; + virtual void AddExtraClipPlane( ShadowHandle_t shadow, const Vector& normal, float dist ) = 0; + + // Allows us to disable particular shadows + virtual void EnableShadow( ShadowHandle_t shadow, bool bEnable ) = 0; + + // Set the darkness falloff bias + virtual void SetFalloffBias( ShadowHandle_t shadow, unsigned char ucBias ) = 0; + + // Update the state for a flashlight. + virtual void UpdateFlashlightState( ShadowHandle_t shadowHandle, const FlashlightState_t &lightState ) = 0; + + virtual void DrawFlashlightDepthTexture( ) = 0; + + virtual ShadowHandle_t CreateShadowEx( IMaterial* pMaterial, IMaterial* pModelMaterial, void* pBindProxy, int creationFlags, int nEntIndex ) = 0; + + virtual void SetFlashlightDepthTexture( ShadowHandle_t shadowHandle, ITexture *pFlashlightDepthTexture, unsigned char ucShadowStencilBit ) = 0; + + virtual const FlashlightState_t &GetFlashlightState( ShadowHandle_t handle ) = 0; + + virtual void SetFlashlightRenderState( ShadowHandle_t handle ) = 0; + virtual void EndFlashlightRenderState( ShadowHandle_t handle ) = 0; + + virtual void DrawVolumetrics() = 0; + + virtual int GetNumShadowsOnModel( ModelInstanceHandle_t instance ) = 0; + virtual int GetShadowsOnModel( ModelInstanceHandle_t instance, ShadowHandle_t* pShadowArray, bool bNormalShadows, bool bFlashlightShadows ) = 0; + + virtual void FlashlightDrawCallback( ShadowDrawCallbackFn_t pCallback, void *pData ) = 0; //used to draw each additive flashlight pass. The callback is called once per flashlight state for an additive pass. + + //Way for the client to determine which flashlight to use in single-pass modes. Does not actually enable the flashlight in any way. + virtual void SetSinglePassFlashlightRenderState( ShadowHandle_t handle ) = 0; + + //Enable/Disable the flashlight state set with SetSinglePassFlashlightRenderState. + virtual void PushSinglePassFlashlightStateEnabled( bool bEnable ) = 0; + virtual void PopSinglePassFlashlightStateEnabled( void ) = 0; + + virtual bool SinglePassFlashlightModeEnabled( void ) = 0; + + // Determine a unique list of flashlights which hit at least one of the specified models + // Accepts an instance count and an array of ModelInstanceHandle_ts. + // Returns the number of FlashlightInstance_ts it's found that affect the models. + // Also fills in a mask of which flashlights affect each ModelInstanceHandle_t + // There can be at most MAX_FLASHLIGHTS_PER_INSTANCE_DRAW_CALL pFlashlights, + // and the size of the pModelUsageMask array must be nInstanceCount. + virtual int SetupFlashlightRenderInstanceInfo( ShadowHandle_t *pUniqueFlashlights, uint32 *pModelUsageMask, int nUsageStride, int nInstanceCount, const ModelInstanceHandle_t *pInstance ) = 0; + + // Returns the flashlight state for multiple flashlights + virtual void GetFlashlightRenderInfo( FlashlightInstance_t *pFlashlightState, int nCount, const ShadowHandle_t *pHandles ) = 0; + + virtual void RemoveAllDecalsFromShadow( ShadowHandle_t handle ) = 0; + + virtual void SkipShadowForEntity( int nEntIndex ) = 0; +}; + + +#endif diff --git a/public/engine/ivdebugoverlay.h b/public/engine/ivdebugoverlay.h new file mode 100644 index 0000000..6440831 --- /dev/null +++ b/public/engine/ivdebugoverlay.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// +// cdll_int.h +// +// 4-23-98 +// JOHN: client dll interface declarations +// + +#ifndef IVDEBUGOVERLAY_H +#define IVDEBUGOVERLAY_H + +#ifdef _WIN32 +#pragma once +#endif + +class Vector; + +#define VDEBUG_OVERLAY_INTERFACE_VERSION "VDebugOverlay004" + +// When used as a duration by a server-side NDebugOverlay:: call, +// causes the overlay to persist until the next server update. +#define NDEBUG_PERSIST_TILL_NEXT_SERVER (0.01023f) + +class OverlayText_t; + +abstract_class IVDebugOverlay +{ +public: + virtual void AddEntityTextOverlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0; + virtual void AddBoxOverlay(const Vector& origin, const Vector& mins, const Vector& max, QAngle const& orientation, int r, int g, int b, int a, float duration) = 0; + virtual void AddSphereOverlay(const Vector& vOrigin, float flRadius, int nTheta, int nPhi, int r, int g, int b, int a, float flDuration) = 0; + virtual void AddTriangleOverlay(const Vector& p1, const Vector& p2, const Vector& p3, int r, int g, int b, int a, bool noDepthTest, float duration) = 0; + virtual void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b,bool noDepthTest, float duration) = 0; + virtual void AddTextOverlay(const Vector& origin, float duration, const char *format, ...) FMTFUNCTION( 4, 5 ) = 0; + virtual void AddTextOverlay(const Vector& origin, int line_offset, float duration, const char *format, ...) FMTFUNCTION( 5, 6 ) = 0; + virtual void AddScreenTextOverlay(float flXPos, float flYPos,float flDuration, int r, int g, int b, int a, const char *text) = 0; + virtual void AddSweptBoxOverlay(const Vector& start, const Vector& end, const Vector& mins, const Vector& max, const QAngle & angles, int r, int g, int b, int a, float flDuration) = 0; + virtual void AddGridOverlay(const Vector& origin) = 0; + virtual void AddCoordFrameOverlay(const matrix3x4_t& frame, float flScale, int vColorTable[3][3] = NULL) = 0; + + virtual int ScreenPosition(const Vector& point, Vector& screen) = 0; + virtual int ScreenPosition(float flXPos, float flYPos, Vector& screen) = 0; + + virtual OverlayText_t *GetFirst( void ) = 0; + virtual OverlayText_t *GetNext( OverlayText_t *current ) = 0; + virtual void ClearDeadOverlays( void ) = 0; + virtual void ClearAllOverlays() = 0; + + virtual void AddTextOverlayRGB(const Vector& origin, int line_offset, float duration, float r, float g, float b, float alpha, const char *format, ...) FMTFUNCTION( 9, 10 ) = 0; + virtual void AddTextOverlayRGB(const Vector& origin, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) FMTFUNCTION( 9, 10 ) = 0; + + virtual void AddLineOverlayAlpha(const Vector& origin, const Vector& dest, int r, int g, int b, int a, bool noDepthTest, float duration) = 0; + virtual void AddBoxOverlay2( const Vector& origin, const Vector& mins, const Vector& max, QAngle const& orientation, const Color& faceColor, const Color& edgeColor, float duration ) = 0; + + virtual void PurgeTextOverlays() = 0; + +private: + inline void AddTextOverlay(const Vector& origin, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) {} /* catch improper use of bad interface. Needed because '0' duration can be resolved by compiler to NULL format string (i.e., compiles but calls wrong function) */ +}; + + +#endif // IVDEBUGOVERLAY_H diff --git a/public/engine/ivmodelinfo.h b/public/engine/ivmodelinfo.h new file mode 100644 index 0000000..3549f04 --- /dev/null +++ b/public/engine/ivmodelinfo.h @@ -0,0 +1,149 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IVMODELINFO_H +#define IVMODELINFO_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class KeyValues; +struct vcollide_t; +struct model_t; +class Vector; +class QAngle; +class CGameTrace; +struct cplane_t; +typedef CGameTrace trace_t; +struct studiohdr_t; +struct virtualmodel_t; +typedef unsigned char byte; +struct virtualterrainparams_t; +class CPhysCollide; +typedef unsigned short MDLHandle_t; +class CUtlBuffer; +class IClientRenderable; + + +//----------------------------------------------------------------------------- +// Indicates the type of translucency of an unmodulated renderable +//----------------------------------------------------------------------------- +enum RenderableTranslucencyType_t +{ + RENDERABLE_IS_OPAQUE = 0, + RENDERABLE_IS_TRANSLUCENT, + RENDERABLE_IS_TWO_PASS, // has both translucent and opaque sub-partsa +}; + + +//----------------------------------------------------------------------------- +// Model info interface +//----------------------------------------------------------------------------- + +// change this when the new version is incompatable with the old +#define VMODELINFO_CLIENT_INTERFACE_VERSION "VModelInfoClient004" +#define VMODELINFO_SERVER_INTERFACE_VERSION "VModelInfoServer002" + +class IVModelInfo +{ +public: + virtual ~IVModelInfo( void ) { } + + virtual const model_t *GetModel( int modelindex ) const = 0; + // Returns index of model by name + virtual int GetModelIndex( const char *name ) const = 0; + + // Returns name of model + virtual const char *GetModelName( const model_t *model ) const = 0; + virtual vcollide_t *GetVCollide( const model_t *model ) const = 0; + virtual vcollide_t *GetVCollide( int modelindex ) const = 0; + virtual void GetModelBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; + virtual void GetModelRenderBounds( const model_t *model, Vector& mins, Vector& maxs ) const = 0; + virtual int GetModelFrameCount( const model_t *model ) const = 0; + virtual int GetModelType( const model_t *model ) const = 0; + virtual void *GetModelExtraData( const model_t *model ) = 0; + virtual bool ModelHasMaterialProxy( const model_t *model ) const = 0; + virtual bool IsTranslucent( model_t const* model ) const = 0; + virtual bool IsTranslucentTwoPass( const model_t *model ) const = 0; + virtual void Unused0() {}; + virtual RenderableTranslucencyType_t ComputeTranslucencyType( const model_t *model, int nSkin, int nBody ) = 0; + virtual int GetModelMaterialCount( const model_t* model ) const = 0; + virtual void GetModelMaterials( const model_t *model, int count, IMaterial** ppMaterial ) = 0; + virtual bool IsModelVertexLit( const model_t *model ) const = 0; + virtual const char *GetModelKeyValueText( const model_t *model ) = 0; + virtual bool GetModelKeyValue( const model_t *model, CUtlBuffer &buf ) = 0; // supports keyvalue blocks in submodels + virtual float GetModelRadius( const model_t *model ) = 0; + + virtual const studiohdr_t *FindModel( const studiohdr_t *pStudioHdr, void **cache, const char *modelname ) const = 0; + virtual const studiohdr_t *FindModel( void *cache ) const = 0; + virtual virtualmodel_t *GetVirtualModel( const studiohdr_t *pStudioHdr ) const = 0; + virtual byte *GetAnimBlock( const studiohdr_t *pStudioHdr, int iBlock ) const = 0; + + // Available on client only!!! + virtual void GetModelMaterialColorAndLighting( const model_t *model, Vector const& origin, + QAngle const& angles, trace_t* pTrace, + Vector& lighting, Vector& matColor ) = 0; + virtual void GetIlluminationPoint( const model_t *model, IClientRenderable *pRenderable, Vector const& origin, + QAngle const& angles, Vector* pLightingCenter ) = 0; + + virtual int GetModelContents( int modelIndex ) const = 0; + virtual studiohdr_t *GetStudiomodel( const model_t *mod ) = 0; + virtual int GetModelSpriteWidth( const model_t *model ) const = 0; + virtual int GetModelSpriteHeight( const model_t *model ) const = 0; + + // Sets/gets a map-specified fade range (client only) + virtual void SetLevelScreenFadeRange( float flMinSize, float flMaxSize ) = 0; + virtual void GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const = 0; + + // Sets/gets a map-specified per-view fade range (client only) + virtual void SetViewScreenFadeRange( float flMinSize, float flMaxSize ) = 0; + + // Computes fade alpha based on distance fade + screen fade (client only) + virtual unsigned char ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; + virtual unsigned char ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const = 0; + + // both client and server + virtual int GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const = 0; + + // Gets a virtual terrain collision model (creates if necessary) + // NOTE: This may return NULL if the terrain model cannot be virtualized + virtual CPhysCollide *GetCollideForVirtualTerrain( int index ) = 0; + + virtual bool IsUsingFBTexture( const model_t *model, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable ) const = 0; + + virtual const model_t *FindOrLoadModel( const char *name ) const = 0; + + virtual MDLHandle_t GetCacheHandle( const model_t *model ) const = 0; + + // Returns planes of non-nodraw brush model surfaces + virtual int GetBrushModelPlaneCount( const model_t *model ) const = 0; + virtual void GetBrushModelPlane( const model_t *model, int nIndex, cplane_t &plane, Vector *pOrigin ) const = 0; + virtual int GetSurfacepropsForVirtualTerrain( int index ) = 0; + virtual bool UsesEnvCubemap( const model_t *model ) const = 0; + virtual bool UsesStaticLighting( const model_t *model ) const = 0; +}; + + +class IVModelInfoClient : public IVModelInfo +{ +public: +}; + + +struct virtualterrainparams_t +{ + // UNDONE: Add grouping here, specified in BSP file? (test grouping to see if this is necessary) + int index; +}; + +#endif // IVMODELINFO_H diff --git a/public/engine/ivmodelrender.h b/public/engine/ivmodelrender.h new file mode 100644 index 0000000..d3ddbfe --- /dev/null +++ b/public/engine/ivmodelrender.h @@ -0,0 +1,216 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef IVMODELRENDER_H +#define IVMODELRENDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "mathlib/mathlib.h" +#include "istudiorender.h" +#include "datacache/idatacache.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct mstudioanimdesc_t; +struct mstudioseqdesc_t; +struct model_t; +class IClientRenderable; +class Vector; +struct studiohdr_t; +class IMaterial; +class CStudioHdr; +struct MaterialLightingState_t; + +FORWARD_DECLARE_HANDLE( LightCacheHandle_t ); + + +//----------------------------------------------------------------------------- +// Model rendering state +//----------------------------------------------------------------------------- +struct DrawModelState_t +{ + studiohdr_t* m_pStudioHdr; + studiohwdata_t* m_pStudioHWData; + IClientRenderable* m_pRenderable; + const matrix3x4_t *m_pModelToWorld; + StudioDecalHandle_t m_decals; + int m_drawFlags; + int m_lod; +}; + + +//----------------------------------------------------------------------------- +// Model Rendering + instance data +//----------------------------------------------------------------------------- + +// change this when the new version is incompatable with the old +#define VENGINE_HUDMODEL_INTERFACE_VERSION "VEngineModel016" + +typedef unsigned short ModelInstanceHandle_t; + +enum +{ + MODEL_INSTANCE_INVALID = (ModelInstanceHandle_t)~0 +}; + +struct ModelRenderInfo_t +{ + Vector origin; + QAngle angles; + IClientRenderable *pRenderable; + const model_t *pModel; + const matrix3x4_t *pModelToWorld; + const matrix3x4_t *pLightingOffset; + const Vector *pLightingOrigin; + int flags; + int entity_index; + int skin; + int body; + int hitboxset; + ModelInstanceHandle_t instance; + + ModelRenderInfo_t() + { + pModelToWorld = NULL; + pLightingOffset = NULL; + pLightingOrigin = NULL; + } +}; + +struct StaticPropRenderInfo_t +{ + const matrix3x4_t *pModelToWorld; + const model_t *pModel; + IClientRenderable *pRenderable; + Vector *pLightingOrigin; + ModelInstanceHandle_t instance; + uint8 skin; + uint8 alpha; +}; + +struct LightingQuery_t +{ + Vector m_LightingOrigin; + ModelInstanceHandle_t m_InstanceHandle; + bool m_bAmbientBoost; +}; + +struct StaticLightingQuery_t : public LightingQuery_t +{ + IClientRenderable *m_pRenderable; +}; + + +// UNDONE: Move this to hud export code, subsume previous functions +abstract_class IVModelRender +{ +public: + virtual int DrawModel( int flags, + IClientRenderable *pRenderable, + ModelInstanceHandle_t instance, + int entity_index, + const model_t *model, + Vector const& origin, + QAngle const& angles, + int skin, + int body, + int hitboxset, + const matrix3x4_t *modelToWorld = NULL, + const matrix3x4_t *pLightingOffset = NULL ) = 0; + + // This causes a material to be used when rendering the model instead + // of the materials the model was compiled with + virtual void ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType = OVERRIDE_NORMAL ) = 0; + + virtual void SetViewTarget( const CStudioHdr *pStudioHdr, int nBodyIndex, const Vector& target ) = 0; + + // Creates, destroys instance data to be associated with the model + virtual ModelInstanceHandle_t CreateInstance( IClientRenderable *pRenderable, LightCacheHandle_t *pCache = NULL ) = 0; + virtual void DestroyInstance( ModelInstanceHandle_t handle ) = 0; + + // Associates a particular lighting condition with a model instance handle. + // FIXME: This feature currently only works for static props. To make it work for entities, etc., + // we must clean up the lightcache handles as the model instances are removed. + // At the moment, since only the static prop manager uses this, it cleans up all LightCacheHandles + // at level shutdown. + virtual void SetStaticLighting( ModelInstanceHandle_t handle, LightCacheHandle_t* pHandle ) = 0; + virtual LightCacheHandle_t GetStaticLighting( ModelInstanceHandle_t handle ) = 0; + + // moves an existing InstanceHandle to a nex Renderable to keep decals etc. Models must be the same + virtual bool ChangeInstance( ModelInstanceHandle_t handle, IClientRenderable *pRenderable ) = 0; + + // Creates a decal on a model instance by doing a planar projection + // along the ray. The material is the decal material, the radius is the + // radius of the decal to create. + virtual void AddDecal( ModelInstanceHandle_t handle, Ray_t const& ray, + Vector const& decalUp, int decalIndex, int body, bool noPokeThru = false, int maxLODToDecal = ADDDECAL_TO_ALL_LODS ) = 0; + + // Removes all the decals on a model instance + virtual void RemoveAllDecals( ModelInstanceHandle_t handle ) = 0; + + // Remove all decals from all models + virtual void RemoveAllDecalsFromAllModels() = 0; + + // Shadow rendering, DrawModelShadowSetup returns the address of the bone-to-world array, NULL in case of error + virtual matrix3x4a_t* DrawModelShadowSetup( IClientRenderable *pRenderable, int body, int skin, DrawModelInfo_t *pInfo, matrix3x4a_t *pCustomBoneToWorld = NULL ) = 0; + virtual void DrawModelShadow( IClientRenderable *pRenderable, const DrawModelInfo_t &info, matrix3x4a_t *pCustomBoneToWorld = NULL ) = 0; + + // This gets called when overbright, etc gets changed to recompute static prop lighting. + virtual bool RecomputeStaticLighting( ModelInstanceHandle_t handle ) = 0; + + virtual void ReleaseAllStaticPropColorData( void ) = 0; + virtual void RestoreAllStaticPropColorData( void ) = 0; + + // Extended version of drawmodel + virtual int DrawModelEx( ModelRenderInfo_t &pInfo ) = 0; + + virtual int DrawModelExStaticProp( ModelRenderInfo_t &pInfo ) = 0; + + virtual bool DrawModelSetup( ModelRenderInfo_t &pInfo, DrawModelState_t *pState, matrix3x4_t **ppBoneToWorldOut ) = 0; + virtual void DrawModelExecute( const DrawModelState_t &state, const ModelRenderInfo_t &pInfo, matrix3x4_t *pCustomBoneToWorld = NULL ) = 0; + + // Sets up lighting context for a point in space + virtual void SetupLighting( const Vector &vecCenter ) = 0; + + // doesn't support any debug visualization modes or other model options, but draws static props in the + // fastest way possible + virtual int DrawStaticPropArrayFast( StaticPropRenderInfo_t *pProps, int count, bool bShadowDepth ) = 0; + + // Allow client to override lighting state + virtual void SuppressEngineLighting( bool bSuppress ) = 0; + + virtual void SetupColorMeshes( int nTotalVerts ) = 0; + + // Sets up lighting context for a point in space, with smooth interpolation per model. + // Passing MODEL_INSTANCE_INVALID as a handle is equivalent to calling SetupLighting. + virtual void SetupLightingEx( const Vector &vecCenter, ModelInstanceHandle_t handle ) = 0; + + // Finds the brightest light source illuminating a point. Returns false if there isn't any. + virtual bool GetBrightestShadowingLightSource( const Vector &vecCenter, Vector& lightPos, Vector& lightBrightness, bool bAllowNonTaggedLights ) = 0; + + // Computes lighting state for an array of lighting requests + virtual void ComputeLightingState( int nCount, const LightingQuery_t *pQuery, MaterialLightingState_t *pState, ITexture **ppEnvCubemapTexture ) = 0; + + // Gets an array of decal handles given model instances + virtual void GetModelDecalHandles( StudioDecalHandle_t *pDecals, int nDecalStride, int nCount, const ModelInstanceHandle_t *pHandles ) = 0; + + // Computes lighting state for an array of lighting requests for renderables which use static lighting + virtual void ComputeStaticLightingState( int nCount, const StaticLightingQuery_t *pQuery, MaterialLightingState_t *pState, MaterialLightingState_t *pDecalState, ColorMeshInfo_t **ppStaticLighting, ITexture **ppEnvCubemapTexture, DataCacheHandle_t *pColorMeshHandles ) = 0; + + // Cleans up lighting state. Must be called after the draw call that uses + // the color meshes return from ComputeStaticLightingState has been issued + virtual void CleanupStaticLightingState( int nCount, DataCacheHandle_t *pColorMeshHandles ) = 0; +}; + + +#endif // IVMODELRENDER_H diff --git a/public/engine/thinktracecounter.h b/public/engine/thinktracecounter.h new file mode 100644 index 0000000..647d473 --- /dev/null +++ b/public/engine/thinktracecounter.h @@ -0,0 +1,49 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Some macros for the raytraces-in-think-function-counter. +// They're in a header because they're included in a bunch of +// places, but on some cases they need to define files and in +// others only extern them. +// +//=============================================================================// + +#ifndef THINK_TRACE_COUNTER_H +#define THINK_TRACE_COUNTER_H +#ifdef _WIN32 +#pragma once +#endif + +#define THINK_TRACE_COUNTER_COMPILED 1 // without this, all the code is elided. + + +#ifdef THINK_TRACE_COUNTER_COMPILED + // create a macro that is true if we are allowed to debug traces during thinks, and compiles out to nothing otherwise. + #ifdef _X360 + #define DEBUG_THINK_TRACE_COUNTER_ALLOWED() (!IsCert()) + #else + #ifdef THINK_TRACE_COUNTER_COMPILE_FUNCTIONS_ENGINE + bool DEBUG_THINK_TRACE_COUNTER_ALLOWED() + { + // done as a static var to defer initialization until Steam is ready, + // but also to have the fastest check at runtime (rather than calling through + // the API each time) + static bool bIsPublic = GetSteamUniverse() == k_EUniversePublic; + return !bIsPublic; + } + #elif defined( THINK_TRACE_COUNTER_COMPILE_FUNCTIONS_SERVER ) + bool DEBUG_THINK_TRACE_COUNTER_ALLOWED() + { + // done as a static var to defer initialization until Steam is ready, + // but also to have the fastest check at runtime (rather than calling through + // the API each time) + static bool bIsPublic = steamapicontext->SteamUtils() != NULL && steamapicontext->SteamUtils()->GetConnectedUniverse() == k_EUniversePublic; + return !bIsPublic; + } + #else + extern bool DEBUG_THINK_TRACE_COUNTER_ALLOWED(); + #endif + #endif +#endif + + +#endif // THINK_TRACE_COUNTER_H diff --git a/public/engine/view_sharedv1.h b/public/engine/view_sharedv1.h new file mode 100644 index 0000000..af5d8ab --- /dev/null +++ b/public/engine/view_sharedv1.h @@ -0,0 +1,94 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +//=============================================================================// + +#ifndef VIEW_SHAREDV1_H +#define VIEW_SHAREDV1_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Renderer setup data. +//----------------------------------------------------------------------------- +class CViewSetupV1 +{ +public: + CViewSetupV1() + { + m_bForceAspectRatio1To1 = false; + m_bRenderToSubrectOfLargerScreen = false; + bForceClearWholeRenderTarget = false; + m_bUseRenderTargetAspectRatio = false; + } + +// shared by 2D & 3D views + + // User specified context + int context; + + // left side of view window + int x; + // top side of view window + int y; + // width of view window + int width; + // height of view window + int height; + + // clear the color buffer before rendering this view? + bool clearColor; + // clear the Depth buffer before rendering this view? + bool clearDepth; + // NOTE: This is for a workaround on ATI with building cubemaps. Clearing just the viewport doesn't seem to work properly. + bool bForceClearWholeRenderTarget; + +// the rest are only used by 3D views + + // Orthographic projection? + bool m_bOrtho; + // View-space rectangle for ortho projection. + float m_OrthoLeft; + float m_OrthoTop; + float m_OrthoRight; + float m_OrthoBottom; + + // horizontal FOV in degrees + float fov; + // horizontal FOV in degrees for in-view model + float fovViewmodel; + + // 3D origin of camera + Vector origin; + // Origin gets reflected on the water surface, but things like + // displacement LOD need to be calculated from the viewer's + // real position. + Vector m_vUnreflectedOrigin; + + // heading of camera (pitch, yaw, roll) + QAngle angles; + // local Z coordinate of near plane of camera + float zNear; + // local Z coordinate of far plane of camera + float zFar; + + // local Z coordinate of near plane of camera ( when rendering view model ) + float zNearViewmodel; + // local Z coordinate of far plane of camera ( when rendering view model ) + float zFarViewmodel; + + bool m_bForceAspectRatio1To1; + + // set to true if this is to draw into a subrect of the larger screen + // this really is a hack, but no more than the rest of the way this class is used + bool m_bRenderToSubrectOfLargerScreen; + + // Use this for situations like water where you want to render the aspect ratio of the + // back buffer into a square (or otherwise) render target. + bool m_bUseRenderTargetAspectRatio; +}; + +#endif // VIEW_SHAREDV1_H diff --git a/public/engine_hlds_api.h b/public/engine_hlds_api.h new file mode 100644 index 0000000..26f52f7 --- /dev/null +++ b/public/engine_hlds_api.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ENGINE_HLDS_API_H +#define ENGINE_HLDS_API_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "appframework/IAppSystem.h" + + +#define VENGINE_HLDS_API_VERSION "VENGINE_HLDS_API_VERSION002" + + +struct ModInfo_t +{ + void *m_pInstance; + const char *m_pBaseDirectory; // Executable directory ("c:/program files/half-life 2", for example) + const char *m_pInitialMod; // Mod name ("cstrike", for example) + const char *m_pInitialGame; // Root game name ("hl2", for example, in the case of cstrike) + CAppSystemGroup *m_pParentAppSystemGroup; + bool m_bTextMode; +}; + + +//----------------------------------------------------------------------------- +// Purpose: This is the interface exported by the engine.dll to allow a dedicated server front end +// application to host it. +//----------------------------------------------------------------------------- +class IDedicatedServerAPI : public IAppSystem +{ +// Functions +public: + // Initialize the engine with the specified base directory and interface factories + virtual bool ModInit( ModInfo_t &info ) = 0; + // Shutdown the engine + virtual void ModShutdown( void ) = 0; + // Run a frame + virtual bool RunFrame( void ) = 0; + // Insert text into console + virtual void AddConsoleText( char *text ) = 0; + // Get current status to display in the hlds UI (console window title bar, e.g. ) + virtual void UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen ) = 0; + // Get current Hostname to display in the hlds UI (console window title bar, e.g. ) + virtual void UpdateHostname(char *pszHostname, int maxlen) = 0; + + // for the multi-processed fork() server, set the server instance number (1..) + virtual void SetSubProcessID( int nID, int nSocketHandle ) = 0; + +}; + +#endif // ENGINE_HLDS_API_H diff --git a/public/entitydefs.h b/public/entitydefs.h new file mode 100644 index 0000000..259df5b --- /dev/null +++ b/public/entitydefs.h @@ -0,0 +1,21 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ==== +// +// Entity definitions needed by hammer, compile tools, and the game. +// +//============================================================================= + +#ifndef ENTITYDEFS_H +#define ENTITYDEFS_H +#ifdef _WIN32 +#pragma once +#endif + + +#define MAX_ENTITY_NAME_LEN 256 +#define MAX_IO_NAME_LEN 256 + +#define VMF_IOPARAM_STRING_DELIMITER 0x1b // Use ESC as a delimiter so we can pass commas etc. in I/O parameters + + +#endif // ENTITYDEFS_H + diff --git a/public/event_flags.h b/public/event_flags.h new file mode 100644 index 0000000..727fcd3 --- /dev/null +++ b/public/event_flags.h @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef EVENT_FLAGS_H +#define EVENT_FLAGS_H + +#ifdef _WIN32 +#pragma once +#endif + + +// Skip local host for event send. +#define FEV_NOTHOST (1<<0) + +// Send the event reliably. You must specify the origin and angles and use +// PLAYBACK_EVENT_FULL for this to work correctly on the server for anything +// that depends on the event origin/angles. I.e., the origin/angles are not +// taken from the invoking edict for reliable events. +#define FEV_RELIABLE (1<<1) + +// Don't restrict to PAS/PVS, send this event to _everybody_ on the server ( useful for stopping CHAN_STATIC +// sounds started by client event when client is not in PVS anymore ( hwguy in TFC e.g. ). +#define FEV_GLOBAL (1<<2) + +#endif // EVENT_FLAGS_H diff --git a/public/filesystem.h b/public/filesystem.h new file mode 100644 index 0000000..680306e --- /dev/null +++ b/public/filesystem.h @@ -0,0 +1,871 @@ +//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +#include "tier0/threadtools.h" +#include "tier0/memalloc.h" +#include "tier0/tslist.h" +#include "tier1/interface.h" +#include "tier1/utlsymbol.h" +#include "tier1/utlstring.h" +#include "tier1/functors.h" +#include "tier1/checksum_crc.h" +#include "tier1/utlqueue.h" +#include "appframework/IAppSystem.h" +#include "tier2/tier2.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + +class CUtlBuffer; +class KeyValues; +class IFileList; + +typedef void * FileHandle_t; +typedef int FileFindHandle_t; +typedef void (*FileSystemLoggingFunc_t)( const char *fileName, const char *accessType ); +typedef int WaitForResourcesHandle_t; + +#ifdef _X360 +typedef void* HANDLE; +#endif + +//----------------------------------------------------------------------------- +// Enums used by the interface +//----------------------------------------------------------------------------- + +#define FILESYSTEM_MAX_SEARCH_PATHS 128 + +enum FileSystemSeek_t +{ + FILESYSTEM_SEEK_HEAD = SEEK_SET, + FILESYSTEM_SEEK_CURRENT = SEEK_CUR, + FILESYSTEM_SEEK_TAIL = SEEK_END, +}; + +enum +{ + FILESYSTEM_INVALID_FIND_HANDLE = -1 +}; + +enum FileWarningLevel_t +{ + // A problem! + FILESYSTEM_WARNING = -1, + + // Don't print anything + FILESYSTEM_WARNING_QUIET = 0, + + // On shutdown, report names of files left unclosed + FILESYSTEM_WARNING_REPORTUNCLOSED, + + // Report number of times a file was opened, closed + FILESYSTEM_WARNING_REPORTUSAGE, + + // Report all open/close events to console ( !slow! ) + FILESYSTEM_WARNING_REPORTALLACCESSES, + + // Report all open/close/read events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READ, + + // Report all open/close/read/write events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, + + // Report all open/close/read/write events and all async I/O file events to the console ( !slower(est)! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC, + +}; + +// search path filtering +enum PathTypeFilter_t +{ + FILTER_NONE = 0, // no filtering, all search path types match + FILTER_CULLPACK = 1, // pack based search paths are culled (maps and zips) + FILTER_CULLNONPACK = 2, // non-pack based search paths are culled +}; + +// search path querying (bit flags) +enum +{ + PATH_IS_NORMAL = 0x00, // normal path, not pack based + PATH_IS_PACKFILE = 0x01, // path is a pack file + PATH_IS_MAPPACKFILE = 0x02, // path is a map pack file + PATH_IS_DVDDEV = 0x04, // path is the dvddev cache +}; +typedef uint32 PathTypeQuery_t; + +#define IS_PACKFILE( n ) ( n & ( PATH_IS_PACKFILE | PATH_IS_MAPPACKFILE ) ) +#define IS_DVDDEV( n ) ( n & PATH_IS_DVDDEV ) + +enum DVDMode_t +{ + DVDMODE_OFF = 0, // not using dvd + DVDMODE_STRICT = 1, // dvd device only + DVDMODE_DEV = 2, // dev mode, mutiple devices ok + DVDMODE_DEV_VISTA = 3, // dev mode from a vista host, mutiple devices ok +}; + +// In non-retail builds, enable the file blocking access tracking stuff... +#if defined( TRACK_BLOCKING_IO ) +enum FileBlockingWarning_t +{ + // Report how long synchronous i/o took to complete + FILESYSTEM_BLOCKING_SYNCHRONOUS = 0, + // Report how long async i/o took to complete if AsyncFileFinished caused it to load via "blocking" i/o + FILESYSTEM_BLOCKING_ASYNCHRONOUS_BLOCK, + // Report how long async i/o took to complete + FILESYSTEM_BLOCKING_ASYNCHRONOUS, + // Report how long the async "callback" took + FILESYSTEM_BLOCKING_CALLBACKTIMING, + + FILESYSTEM_BLOCKING_NUMBINS, +}; + +#pragma pack(1) +class FileBlockingItem +{ +public: + enum + { + FB_ACCESS_OPEN = 1, + FB_ACCESS_CLOSE = 2, + FB_ACCESS_READ = 3, + FB_ACCESS_WRITE = 4, + FB_ACCESS_APPEND = 5, + FB_ACCESS_SIZE = 6 + }; + + FileBlockingItem() : + m_ItemType( (FileBlockingWarning_t)0 ), + m_flElapsed( 0.0f ), + m_nAccessType( 0 ) + { + SetFileName( NULL ); + } + + FileBlockingItem( int type, char const *filename, float elapsed, int accessType ) : + m_ItemType( (FileBlockingWarning_t)type ), + m_flElapsed( elapsed ), + m_nAccessType( accessType ) + { + SetFileName( filename ); + } + + void SetFileName( char const *filename ) + { + if ( !filename ) + { + m_szFilename[ 0 ] = 0; + return; + } + + int len = Q_strlen( filename ); + if ( len >= sizeof( m_szFilename ) ) + { + Q_strncpy( m_szFilename, &filename[ len - sizeof( m_szFilename ) + 1 ], sizeof( m_szFilename ) ); + } + else + { + Q_strncpy( m_szFilename, filename, sizeof( m_szFilename ) ); + } + } + + char const *GetFileName() const + { + return m_szFilename; + } + + FileBlockingWarning_t m_ItemType; + float m_flElapsed; + byte m_nAccessType; +private: + + char m_szFilename[ 32 ]; +}; +#pragma pack() + +class IBlockingFileItemList +{ +public: + + // You can't call any of the below calls without locking first + virtual void LockMutex() = 0; + virtual void UnlockMutex() = 0; + + virtual int First() const = 0; + virtual int Next( int i ) const = 0; + virtual int InvalidIndex() const = 0; + + virtual const FileBlockingItem& Get( int index ) const = 0; + + virtual void Reset() = 0; +}; + +#endif // TRACK_BLOCKING_IO + +enum FilesystemMountRetval_t +{ + FILESYSTEM_MOUNT_OK = 0, + FILESYSTEM_MOUNT_FAILED, +}; + +enum SearchPathAdd_t +{ + PATH_ADD_TO_HEAD, // First path searched + PATH_ADD_TO_TAIL, // Last path searched + PATH_ADD_TO_TAIL_ATINDEX, // First path searched +}; + +enum FilesystemOpenExFlags_t +{ + FSOPEN_UNBUFFERED = (1 << 0), + FSOPEN_FORCE_TRACK_CRC = (1 << 1), // This makes it calculate a CRC for the file (if the file came from disk) regardless + // of the IFileList passed to RegisterFileWhitelist. + FSOPEN_NEVERINPACK = (1 << 2), // 360 only, hint to FS that file is not allowed to be in pack file +}; + +#define FILESYSTEM_INVALID_HANDLE ( FileHandle_t )0 + +//----------------------------------------------------------------------------- +// Structures used by the interface +//----------------------------------------------------------------------------- + +struct FileSystemStatistics +{ + CInterlockedUInt nReads, + nWrites, + nBytesRead, + nBytesWritten, + nSeeks; +}; + +//----------------------------------------------------------------------------- +// File system allocation functions. Client must free on failure +//----------------------------------------------------------------------------- +typedef void *(*FSAllocFunc_t)( const char *pszFilename, unsigned nBytes ); + + +//----------------------------------------------------------------------------- +// Used to display dirty disk error functions +//----------------------------------------------------------------------------- +typedef void (*FSDirtyDiskReportFunc_t)(); + + +//----------------------------------------------------------------------------- +// Asynchronous support types +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE(FSAsyncControl_t); +DECLARE_POINTER_HANDLE(FSAsyncFile_t); +const FSAsyncFile_t FS_INVALID_ASYNC_FILE = (FSAsyncFile_t)(0x0000ffff); + + +//--------------------------------------------------------- +// Async file status +//--------------------------------------------------------- +enum FSAsyncStatus_t +{ + FSASYNC_ERR_ALIGNMENT = -6, // read parameters invalid for unbuffered IO + FSASYNC_ERR_FAILURE = -5, // hard subsystem failure + FSASYNC_ERR_READING = -4, // read error on file + FSASYNC_ERR_NOMEMORY = -3, // out of memory for file read + FSASYNC_ERR_UNKNOWNID = -2, // caller's provided id is not recognized + FSASYNC_ERR_FILEOPEN = -1, // filename could not be opened (bad path, not exist, etc) + FSASYNC_OK = 0, // operation is successful + FSASYNC_STATUS_PENDING, // file is properly queued, waiting for service + FSASYNC_STATUS_INPROGRESS, // file is being accessed + FSASYNC_STATUS_ABORTED, // file was aborted by caller + FSASYNC_STATUS_UNSERVICED, // file is not yet queued +}; + +//--------------------------------------------------------- +// Async request flags +//--------------------------------------------------------- +enum FSAsyncFlags_t +{ + FSASYNC_FLAGS_ALLOCNOFREE = ( 1 << 0 ), // do the allocation for dataPtr, but don't free + FSASYNC_FLAGS_FREEDATAPTR = ( 1 << 1 ), // free the memory for the dataPtr post callback + FSASYNC_FLAGS_SYNC = ( 1 << 2 ), // Actually perform the operation synchronously. Used to simplify client code paths + FSASYNC_FLAGS_NULLTERMINATE = ( 1 << 3 ), // allocate an extra byte and null terminate the buffer read in +}; + +//--------------------------------------------------------- +// Return value for CheckFileCRC. +//--------------------------------------------------------- +enum EFileCRCStatus +{ + k_eFileCRCStatus_CantOpenFile, // We don't have this file. + k_eFileCRCStatus_GotCRC +}; + +// Used in CacheFileCRCs. +enum ECacheCRCType +{ + k_eCacheCRCType_SingleFile, + k_eCacheCRCType_Directory, + k_eCacheCRCType_Directory_Recursive +}; + +//--------------------------------------------------------- +// Optional completion callback for each async file serviced (or failed) +// call is not reentrant, async i/o guaranteed suspended until return +// Note: If you change the signature of the callback, you will have to account for it in FileSystemV12 (toml [4/18/2005] ) +//--------------------------------------------------------- +struct FileAsyncRequest_t; +typedef void (*FSAsyncCallbackFunc_t)(const FileAsyncRequest_t &request, int nBytesRead, FSAsyncStatus_t err); + +//----------------------------------------------------------------------------- +// Used to add results from async directory scans +//----------------------------------------------------------------------------- +typedef void (*FSAsyncScanAddFunc_t)( void* pContext, char* pFoundPath, char* pFoundFile ); +typedef void (*FSAsyncScanCompleteFunc_t)( void* pContext, FSAsyncStatus_t err ); + + +//--------------------------------------------------------- +// Description of an async request +//--------------------------------------------------------- +struct FileAsyncRequest_t +{ + FileAsyncRequest_t() { memset( this, 0, sizeof(*this) ); hSpecificAsyncFile = FS_INVALID_ASYNC_FILE; } + const char * pszFilename; // file system name + void * pData; // optional, system will alloc/free if NULL + int nOffset; // optional initial seek_set, 0=beginning + int nBytes; // optional read clamp, -1=exist test, 0=full read + FSAsyncCallbackFunc_t pfnCallback; // optional completion callback + void * pContext; // caller's unique file identifier + int priority; // inter list priority, 0=lowest + unsigned flags; // behavior modifier + const char * pszPathID; // path ID (NOTE: this field is here to remain binary compatible with release HL2 filesystem interface) + FSAsyncFile_t hSpecificAsyncFile; // Optional hint obtained using AsyncBeginRead() + FSAllocFunc_t pfnAlloc; // custom allocator. can be null. not compatible with FSASYNC_FLAGS_FREEDATAPTR +}; + + +class CUnverifiedCRCFile +{ +public: + char m_PathID[MAX_PATH]; + char m_Filename[MAX_PATH]; + CRC32_t m_CRC; +}; + + +// Spew flags for SetWhitelistSpewFlags (set with the fs_whitelist_spew_flags cvar). +// Update the comment for the fs_whitelist_spew_flags cvar if you change these. +#define WHITELIST_SPEW_WHILE_LOADING 0x0001 // list files as they are added to the CRC tracker +#define WHITELIST_SPEW_RELOAD_FILES 0x0002 // show files the filesystem is telling the engine to reload +#define WHITELIST_SPEW_DONT_RELOAD_FILES 0x0004 // show files the filesystem is NOT telling the engine to reload + + + +// DLC license mask flags is 32 publisher defined bits +// MSW 16 bits in 8.8: Type.SubVersion +// LSW 16 bits: Flags + +// return id component +#define DLC_LICENSE_ID( x ) ( ( ( (unsigned int)( x ) ) >> 24 ) & 0x000000FF ) +// returns minor version component (not generally used, i.e. we dont rev dlc's yet) +#define DLC_LICENSE_MINORVERSION( x ) ( ( ( (unsigned int)( x ) ) >> 16 ) & 0x000000FF ) +// returns license flags +#define DLC_LICENSE_FLAGS( x ) ( ( ( (unsigned int)( x ) ) & 0x0000FFFF ) ) + +#define DLCFLAGS_PRESENCE_ONLY 0x0001 // causes no search path loadout + + + +//----------------------------------------------------------------------------- +// Base file system interface +//----------------------------------------------------------------------------- + +// This is the minimal interface that can be implemented to provide access to +// a named set of files. +#define BASEFILESYSTEM_INTERFACE_VERSION "VBaseFileSystem011" + +abstract_class IBaseFileSystem +{ +public: + virtual int Read( void* pOutput, int size, FileHandle_t file ) = 0; + virtual int Write( void const* pInput, int size, FileHandle_t file ) = 0; + + // if pathID is NULL, all paths will be searched for the file + virtual FileHandle_t Open( const char *pFileName, const char *pOptions, const char *pathID = 0 ) = 0; + virtual void Close( FileHandle_t file ) = 0; + + + virtual void Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType ) = 0; + virtual unsigned int Tell( FileHandle_t file ) = 0; + virtual unsigned int Size( FileHandle_t file ) = 0; + virtual unsigned int Size( const char *pFileName, const char *pPathID = 0 ) = 0; + + virtual void Flush( FileHandle_t file ) = 0; + virtual bool Precache( const char *pFileName, const char *pPathID = 0 ) = 0; + + virtual bool FileExists( const char *pFileName, const char *pPathID = 0 ) = 0; + virtual bool IsFileWritable( char const *pFileName, const char *pPathID = 0 ) = 0; + virtual bool SetFileWritable( char const *pFileName, bool writable, const char *pPathID = 0 ) = 0; + + virtual long GetFileTime( const char *pFileName, const char *pPathID = 0 ) = 0; + + //-------------------------------------------------------- + // Reads/writes files to utlbuffers. Use this for optimal read performance when doing open/read/close + //-------------------------------------------------------- + virtual bool ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL ) = 0; + virtual bool WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf ) = 0; + virtual bool UnzipFile( const char *pFileName, const char *pPath, const char *pDestination ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Main file system interface +//----------------------------------------------------------------------------- +abstract_class IFileSystem : public IAppSystem, public IBaseFileSystem +{ +public: + //-------------------------------------------------------- + // Steam operations + //-------------------------------------------------------- + + virtual bool IsSteam() const = 0; + + // Supplying an extra app id will mount this app in addition + // to the one specified in the environment variable "steamappid" + // + // If nExtraAppId is < -1, then it will mount that app ID only. + // (Was needed by the dedicated server b/c the "SteamAppId" env var only gets passed to steam.dll + // at load time, so the dedicated couldn't pass it in that way). + virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 ) = 0; + + //-------------------------------------------------------- + // Search path manipulation + //-------------------------------------------------------- + + // Add paths in priority order (mod dir, game dir, ....) + // If one or more .pak files are in the specified directory, then they are + // added after the file system path + // If the path is the relative path to a .bsp file, then any previous .bsp file + // override is cleared and the current .bsp is searched for an embedded PAK file + // and this file becomes the highest priority search path ( i.e., it's looked at first + // even before the mod's file system path ). + virtual void AddSearchPath( const char *pPath, const char *pathID, SearchPathAdd_t addType = PATH_ADD_TO_TAIL ) = 0; + virtual bool RemoveSearchPath( const char *pPath, const char *pathID = 0 ) = 0; + + // Remove all search paths (including write path?) + virtual void RemoveAllSearchPaths( void ) = 0; + + // Remove search paths associated with a given pathID + virtual void RemoveSearchPaths( const char *szPathID ) = 0; + + // This is for optimization. If you mark a path ID as "by request only", then files inside it + // will only be accessed if the path ID is specifically requested. Otherwise, it will be ignored. + // If there are currently no search paths with the specified path ID, then it will still + // remember it in case you add search paths with this path ID. + virtual void MarkPathIDByRequestOnly( const char *pPathID, bool bRequestOnly ) = 0; + + // converts a partial path into a full path + virtual const char *RelativePathToFullPath( const char *pFileName, const char *pPathID, char *pLocalPath, int localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t *pPathType = NULL ) = 0; + + // Returns the search path, each path is separated by ;s. Returns the length of the string returned + virtual int GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen ) = 0; + + // interface for custom pack files > 4Gb + virtual bool AddPackFile( const char *fullpath, const char *pathID ) = 0; + + //-------------------------------------------------------- + // File manipulation operations + //-------------------------------------------------------- + + // Deletes a file (on the WritePath) + virtual void RemoveFile( char const* pRelativePath, const char *pathID = 0 ) = 0; + + // Renames a file (on the WritePath) + virtual bool RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID = 0 ) = 0; + + // create a local directory structure + virtual void CreateDirHierarchy( const char *path, const char *pathID = 0 ) = 0; + + // File I/O and info + virtual bool IsDirectory( const char *pFileName, const char *pathID = 0 ) = 0; + + virtual void FileTimeToString( char* pStrip, int maxCharsIncludingTerminator, long fileTime ) = 0; + + //-------------------------------------------------------- + // Open file operations + //-------------------------------------------------------- + + virtual void SetBufferSize( FileHandle_t file, unsigned nBytes ) = 0; + + virtual bool IsOk( FileHandle_t file ) = 0; + + virtual bool EndOfFile( FileHandle_t file ) = 0; + + virtual char *ReadLine( char *pOutput, int maxChars, FileHandle_t file ) = 0; + virtual int FPrintf( FileHandle_t file, const char *pFormat, ... ) FMTFUNCTION( 3, 4 ) = 0; + + //-------------------------------------------------------- + // Dynamic library operations + //-------------------------------------------------------- + + // load/unload modules + virtual CSysModule *LoadModule( const char *pFileName, const char *pPathID = 0, bool bValidatedDllOnly = true ) = 0; + virtual void UnloadModule( CSysModule *pModule ) = 0; + + //-------------------------------------------------------- + // File searching operations + //-------------------------------------------------------- + + // FindFirst/FindNext. Also see FindFirstEx. + virtual const char *FindFirst( const char *pWildCard, FileFindHandle_t *pHandle ) = 0; + virtual const char *FindNext( FileFindHandle_t handle ) = 0; + virtual bool FindIsDirectory( FileFindHandle_t handle ) = 0; + virtual void FindClose( FileFindHandle_t handle ) = 0; + + // Same as FindFirst, but you can filter by path ID, which can make it faster. + virtual const char *FindFirstEx( + const char *pWildCard, + const char *pPathID, + FileFindHandle_t *pHandle + ) = 0; + + virtual void FindFileAbsoluteList( + CUtlVector> &output, + const char *pWildCard, + const char *pPathID + ) = 0; + + //-------------------------------------------------------- + // File name and directory operations + //-------------------------------------------------------- + + // FIXME: This method is obsolete! Use RelativePathToFullPath instead! + // converts a partial path into a full path + virtual const char *GetLocalPath( const char *pFileName, char *pLocalPath, int localPathBufferSize ) = 0; + + // Returns true on success ( based on current list of search paths, otherwise false if + // it can't be resolved ) + virtual bool FullPathToRelativePath( const char *pFullpath, char *pRelative, int maxlen ) = 0; + + // Gets the current working directory + virtual bool GetCurrentDirectory( char* pDirectory, int maxlen ) = 0; + + //-------------------------------------------------------- + // Filename dictionary operations + //-------------------------------------------------------- + + virtual FileNameHandle_t FindOrAddFileName( char const *pFileName ) = 0; + virtual bool String( const FileNameHandle_t& handle, char *buf, int buflen ) = 0; + + //-------------------------------------------------------- + // Asynchronous file operations + //-------------------------------------------------------- + + //------------------------------------ + // Global operations + //------------------------------------ + FSAsyncStatus_t AsyncRead( const FileAsyncRequest_t &request, FSAsyncControl_t *phControl = NULL ) { return AsyncReadMultiple( &request, 1, phControl ); } + virtual FSAsyncStatus_t AsyncReadMultiple( const FileAsyncRequest_t *pRequests, int nRequests, FSAsyncControl_t *phControls = NULL ) = 0; + virtual FSAsyncStatus_t AsyncAppend(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl = NULL ) = 0; + virtual FSAsyncStatus_t AsyncAppendFile(const char *pAppendToFileName, const char *pAppendFromFileName, FSAsyncControl_t *pControl = NULL ) = 0; + virtual void AsyncFinishAll( int iToPriority = 0 ) = 0; + virtual void AsyncFinishAllWrites() = 0; + virtual FSAsyncStatus_t AsyncFlush() = 0; + virtual bool AsyncSuspend() = 0; + virtual bool AsyncResume() = 0; + + //------------------------------------ + // Functions to hold a file open if planning on doing mutiple reads. Use is optional, + // and is taken only as a hint + //------------------------------------ + virtual FSAsyncStatus_t AsyncBeginRead( const char *pszFile, FSAsyncFile_t *phFile ) = 0; + virtual FSAsyncStatus_t AsyncEndRead( FSAsyncFile_t hFile ) = 0; + + //------------------------------------ + // Request management + //------------------------------------ + virtual FSAsyncStatus_t AsyncFinish( FSAsyncControl_t hControl, bool wait = true ) = 0; + virtual FSAsyncStatus_t AsyncGetResult( FSAsyncControl_t hControl, void **ppData, int *pSize ) = 0; + virtual FSAsyncStatus_t AsyncAbort( FSAsyncControl_t hControl ) = 0; + virtual FSAsyncStatus_t AsyncStatus( FSAsyncControl_t hControl ) = 0; + // set a new priority for a file already in the queue + virtual FSAsyncStatus_t AsyncSetPriority(FSAsyncControl_t hControl, int newPriority) = 0; + virtual void AsyncAddRef( FSAsyncControl_t hControl ) = 0; + virtual void AsyncRelease( FSAsyncControl_t hControl ) = 0; + + //-------------------------------------------------------- + // Remote resource management + //-------------------------------------------------------- + + // starts waiting for resources to be available + // returns FILESYSTEM_INVALID_HANDLE if there is nothing to wait on + virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist ) = 0; + // get progress on waiting for resources; progress is a float [0, 1], complete is true on the waiting being done + // returns false if no progress is available + // any calls after complete is true or on an invalid handle will return false, 0.0f, true + virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ ) = 0; + // cancels a progress call + virtual void CancelWaitForResources( WaitForResourcesHandle_t handle ) = 0; + + // hints that a set of files will be loaded in near future + // HintResourceNeed() is not to be confused with resource precaching. + virtual int HintResourceNeed( const char *hintlist, int forgetEverything ) = 0; + // returns true if a file is on disk + virtual bool IsFileImmediatelyAvailable(const char *pFileName) = 0; + + // copies file out of pak/bsp/steam cache onto disk (to be accessible by third-party code) + virtual void GetLocalCopy( const char *pFileName ) = 0; + + //-------------------------------------------------------- + // Debugging operations + //-------------------------------------------------------- + + // Dump to printf/OutputDebugString the list of files that have not been closed + virtual void PrintOpenedFiles( void ) = 0; + virtual void PrintSearchPaths( void ) = 0; + + // output + virtual void SetWarningFunc( void (*pfnWarning)( const char *fmt, ... ) ) = 0; + virtual void SetWarningLevel( FileWarningLevel_t level ) = 0; + virtual void AddLoggingFunc( void (*pfnLogFunc)( const char *fileName, const char *accessType ) ) = 0; + virtual void RemoveLoggingFunc( FileSystemLoggingFunc_t logFunc ) = 0; + + // Returns the file system statistics retreived by the implementation. Returns NULL if not supported. + virtual const FileSystemStatistics *GetFilesystemStatistics() = 0; + + //-------------------------------------------------------- + // Start of new functions after Lost Coast release (7/05) + //-------------------------------------------------------- + + virtual FileHandle_t OpenEx( const char *pFileName, const char *pOptions, unsigned flags = 0, const char *pathID = 0, char **ppszResolvedFilename = NULL ) = 0; + + // Extended version of read provides more context to allow for more optimal reading + virtual int ReadEx( void* pOutput, int sizeDest, int size, FileHandle_t file ) = 0; + virtual int ReadFileEx( const char *pFileName, const char *pPath, void **ppBuf, bool bNullTerminate = false, bool bOptimalAlloc = false, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL ) = 0; + + virtual FileNameHandle_t FindFileName( char const *pFileName ) = 0; + +#if defined( TRACK_BLOCKING_IO ) + virtual void EnableBlockingFileAccessTracking( bool state ) = 0; + virtual bool IsBlockingFileAccessEnabled() const = 0; + + virtual IBlockingFileItemList *RetrieveBlockingFileAccessInfo() = 0; +#endif + + virtual void SetupPreloadData() = 0; + virtual void DiscardPreloadData() = 0; + + // Fixme, we could do these via a string embedded into the compiled data, etc... + enum KeyValuesPreloadType_t + { + TYPE_VMT, + TYPE_SOUNDEMITTER, + TYPE_SOUNDSCAPE, + NUM_PRELOAD_TYPES + }; + + // If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of compiled keyvalues loaded during startup. + // Otherwise, it'll just fall through to the regular KeyValues loading routines + virtual KeyValues *LoadKeyValues( KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ) = 0; + virtual bool LoadKeyValues( KeyValues& head, KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ) = 0; + + virtual FSAsyncStatus_t AsyncWrite(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend = false, FSAsyncControl_t *pControl = NULL ) = 0; + virtual FSAsyncStatus_t AsyncWriteFile(const char *pFileName, const CUtlBuffer *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend = false, FSAsyncControl_t *pControl = NULL ) = 0; + // Async read functions with memory blame + FSAsyncStatus_t AsyncReadCreditAlloc( const FileAsyncRequest_t &request, const char *pszFile, int line, FSAsyncControl_t *phControl = NULL ) { return AsyncReadMultipleCreditAlloc( &request, 1, pszFile, line, phControl ); } + virtual FSAsyncStatus_t AsyncReadMultipleCreditAlloc( const FileAsyncRequest_t *pRequests, int nRequests, const char *pszFile, int line, FSAsyncControl_t *phControls = NULL ) = 0; + + virtual FSAsyncStatus_t AsyncDirectoryScan( const char* pSearchSpec, bool recurseFolders, void* pContext, FSAsyncScanAddFunc_t pfnAdd, FSAsyncScanCompleteFunc_t pfnDone, FSAsyncControl_t *pControl = NULL ) = 0; + + virtual bool GetFileTypeForFullPath( char const *pFullPath, wchar_t *buf, size_t bufSizeInBytes ) = 0; + + //-------------------------------------------------------- + //-------------------------------------------------------- + virtual bool ReadToBuffer( FileHandle_t hFile, CUtlBuffer &buf, int nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL ) = 0; + + //-------------------------------------------------------- + // Optimal IO operations + //-------------------------------------------------------- + virtual bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign ) = 0; + inline unsigned GetOptimalReadSize( FileHandle_t hFile, unsigned nLogicalSize ); + virtual void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize = 0, unsigned nOffset = 0 ) = 0; + virtual void FreeOptimalReadBuffer( void * ) = 0; + + //-------------------------------------------------------- + // + //-------------------------------------------------------- + virtual void BeginMapAccess() = 0; + virtual void EndMapAccess() = 0; + + // Returns true on success, otherwise false if it can't be resolved + virtual bool FullPathToRelativePathEx( const char *pFullpath, const char *pPathId, char *pRelative, int maxlen ) = 0; + + virtual int GetPathIndex( const FileNameHandle_t &handle ) = 0; + virtual long GetPathTime( const char *pPath, const char *pPathID ) = 0; + + virtual DVDMode_t GetDVDMode() = 0; + + //-------------------------------------------------------- + // Whitelisting for pure servers. + //-------------------------------------------------------- + + // This should be called ONCE at startup. Multiplayer games (gameinfo.txt does not contain singleplayer_only) + // want to enable this so sv_pure works. + virtual void EnableWhitelistFileTracking( bool bEnable ) = 0; + + // This is called when the client connects to a server using a pure_server_whitelist.txt file. + // + // Files listed in pWantCRCList will have CRCs calculated for them IF they come off disk + // (and those CRCs will come out of GetUnverifiedCRCFiles). + // + // Files listed in pAllowFromDiskList will be allowed to load from disk. All other files will + // be forced to come from Steam. + // + // The filesystem hangs onto the whitelists you pass in here, and it will Release() them when it closes down + // or when you call this function again. + // + // NOTE: The whitelists you pass in here will be accessed from multiple threads, so make sure the + // IsFileInList function is thread safe. + // + // If pFilesToReload is non-null, the filesystem will hand back a list of files that should be reloaded because they + // are now "dirty". For example, if you were on a non-pure server and you loaded a certain model, and then you connected + // to a pure server that said that model had to come from Steam, then pFilesToReload would specify that model + // and the engine should reload it so it can come from Steam. + // + // Be sure to call Release() on pFilesToReload. + virtual void RegisterFileWhitelist( IFileList *pWantCRCList, IFileList *pAllowFromDiskList, IFileList **pFilesToReload ) = 0; + + // Called when the client logs onto a server. Any files that came off disk should be marked as + // unverified because this server may have a different set of files it wants to guarantee. + virtual void MarkAllCRCsUnverified() = 0; + + // As the server loads whitelists when it transitions maps, it calls this to calculate CRCs for any files marked + // with check_crc. Then it calls CheckCachedFileCRC later when it gets client requests to verify CRCs. + virtual void CacheFileCRCs( const char *pPathname, ECacheCRCType eType, IFileList *pFilter ) = 0; + virtual EFileCRCStatus CheckCachedFileCRC( const char *pPathID, const char *pRelativeFilename, CRC32_t *pCRC ) = 0; + + // Fills in the list of files that have been loaded off disk and have not been verified. + // Returns the number of files filled in (between 0 and nMaxFiles). + // + // This also removes any files it's returning from the unverified CRC list, so they won't be + // returned from here again. + // The client sends batches of these to the server to verify. + virtual int GetUnverifiedCRCFiles( CUnverifiedCRCFile *pFiles, int nMaxFiles ) = 0; + + // Control debug message output. + // Pass a combination of WHITELIST_SPEW_ flags. + virtual int GetWhitelistSpewFlags() = 0; + virtual void SetWhitelistSpewFlags( int flags ) = 0; + + // Installs a callback used to display a dirty disk dialog + virtual void InstallDirtyDiskReportFunc( FSDirtyDiskReportFunc_t func ) = 0; + + virtual bool IsLaunchedFromXboxHDD() = 0; + virtual bool IsInstalledToXboxHDDCache() = 0; + virtual bool IsDVDHosted() = 0; + virtual bool IsInstallAllowed() = 0; + + virtual int GetSearchPathID( char *pPath, int nMaxLen ) = 0; + virtual bool FixupSearchPathsAfterInstall() = 0; + + virtual FSDirtyDiskReportFunc_t GetDirtyDiskReportFunc() = 0; + + virtual void AddVPKFile( char const *pszName, SearchPathAdd_t addType = PATH_ADD_TO_TAIL ) = 0; + virtual void RemoveVPKFile( char const *pszName ) = 0; + virtual void GetVPKFileNames( CUtlVector &destVector ) = 0; + virtual void RemoveAllMapSearchPaths() = 0; + virtual void SyncDvdDevCache() = 0; + + virtual bool GetStringFromKVPool( CRC32_t poolKey, unsigned int key, char *pOutBuff, int buflen ) = 0; + + virtual bool DiscoverDLC( int iController ) = 0; + virtual int IsAnyDLCPresent( bool *pbDLCSearchPathMounted = NULL ) = 0; + virtual bool GetAnyDLCInfo( int iDLC, unsigned int *pLicenseMask, wchar_t *pTitleBuff, int nOutTitleSize ) = 0; + virtual int IsAnyCorruptDLC() = 0; + virtual bool GetAnyCorruptDLCInfo( int iCorruptDLC, wchar_t *pTitleBuff, int nOutTitleSize ) = 0; + virtual bool AddDLCSearchPaths() = 0; + virtual bool IsSpecificDLCPresent( unsigned int nDLCPackage ) = 0; + + // call this to look for CPU-hogs during loading processes. When you set this, a breakpoint + // will be issued whenever the indicated # of seconds go by without an i/o request. Passing + // 0.0 will turn off the functionality. + virtual void SetIODelayAlarm( float flThreshhold ) = 0; + +}; + +//----------------------------------------------------------------------------- + +#if defined( _X360 ) && !defined( _CERT ) +extern char g_szXboxProfileLastFileOpened[MAX_PATH]; +#define SetLastProfileFileRead( s ) Q_strncpy( g_szXboxProfileLastFileOpened, sizeof( g_szXboxProfileLastFileOpened), pFileName ) +#define GetLastProfileFileRead() (&g_szXboxProfileLastFileOpened[0]) +#else +#define SetLastProfileFileRead( s ) ((void)0) +#define GetLastProfileFileRead() NULL +#endif + +#if defined( _X360 ) && defined( _BASETSD_H_ ) +class CXboxDiskCacheSetter +{ +public: + CXboxDiskCacheSetter( SIZE_T newSize ) + { + m_oldSize = XGetFileCacheSize(); + XSetFileCacheSize( newSize ); + } + + ~CXboxDiskCacheSetter() + { + XSetFileCacheSize( m_oldSize ); + } +private: + SIZE_T m_oldSize; +}; +#define DISK_INTENSIVE() CXboxDiskCacheSetter cacheSetter( 1024*1024 ) +#else +#define DISK_INTENSIVE() ((void)0) +#endif + +//----------------------------------------------------------------------------- + +inline unsigned IFileSystem::GetOptimalReadSize( FileHandle_t hFile, unsigned nLogicalSize ) +{ + unsigned align; + if ( GetOptimalIOConstraints( hFile, &align, NULL, NULL ) ) + return AlignValue( nLogicalSize, align ); + else + return nLogicalSize; +} + +//----------------------------------------------------------------------------- + +// We include this here so it'll catch compile errors in VMPI early. +#include "filesystem_passthru.h" + +//----------------------------------------------------------------------------- +// Async memory tracking +//----------------------------------------------------------------------------- + +#if (defined(_DEBUG) || defined(USE_MEM_DEBUG)) +#define AsyncRead( a, b ) AsyncReadCreditAlloc( a, __FILE__, __LINE__, b ) +#define AsyncReadMutiple( a, b, c ) AsyncReadMultipleCreditAlloc( a, b, __FILE__, __LINE__, c ) +#endif + +//----------------------------------------------------------------------------- +// Globals Exposed +//----------------------------------------------------------------------------- +DECLARE_TIER2_INTERFACE( IFileSystem, g_pFullFileSystem ); + + + + +#endif // FILESYSTEM_H diff --git a/public/filesystem_helpers.cpp b/public/filesystem_helpers.cpp new file mode 100644 index 0000000..57be43d --- /dev/null +++ b/public/filesystem_helpers.cpp @@ -0,0 +1,138 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#include "platform.h" +#include "filesystem.h" +#include "filesystem_helpers.h" +#include "characterset.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// wordbreak parsing set +static characterset_t g_BreakSet, g_BreakSetIncludingColons; + +static void InitializeCharacterSets() +{ + static bool s_CharacterSetInitialized = false; + if (!s_CharacterSetInitialized) + { + CharacterSetBuild( &g_BreakSet, "{}()'" ); + CharacterSetBuild( &g_BreakSetIncludingColons, "{}()':" ); + s_CharacterSetInitialized = true; + } +} + + +const char* ParseFile( const char* pFileBytes, char* pToken, bool* pWasQuoted, characterset_t *pCharSet ) +{ + if (pWasQuoted) + *pWasQuoted = false; + + if (!pFileBytes) + return 0; + + InitializeCharacterSets(); + + // YWB: Ignore colons as token separators in COM_Parse + static bool com_ignorecolons = false; + characterset_t& breaks = pCharSet ? *pCharSet : (com_ignorecolons ? g_BreakSet : g_BreakSetIncludingColons); + + int c; + int len = 0; + pToken[0] = 0; + +// skip whitespace +skipwhite: + + while ( (c = *pFileBytes) <= ' ') + { + if (c == 0) + return 0; // end of file; + pFileBytes++; + } + +// skip // comments + if (c=='/' && pFileBytes[1] == '/') + { + while (*pFileBytes && *pFileBytes != '\n') + pFileBytes++; + goto skipwhite; + } + +// skip c-style comments + if (c=='/' && pFileBytes[1] == '*' ) + { + // Skip "/*" + pFileBytes += 2; + + while ( *pFileBytes ) + { + if ( *pFileBytes == '*' && + pFileBytes[1] == '/' ) + { + pFileBytes += 2; + break; + } + + pFileBytes++; + } + + goto skipwhite; + } + +// handle quoted strings specially + if (c == '\"') + { + if (pWasQuoted) + *pWasQuoted = true; + + pFileBytes++; + while (1) + { + c = *pFileBytes++; + if (c=='\"' || !c) + { + pToken[len] = 0; + return pFileBytes; + } + pToken[len] = c; + len++; + } + } + +// parse single characters + if ( IN_CHARACTERSET( breaks, c ) ) + { + pToken[len] = c; + len++; + pToken[len] = 0; + return pFileBytes+1; + } + +// parse a regular word + do + { + pToken[len] = c; + pFileBytes++; + len++; + c = *pFileBytes; + if ( IN_CHARACTERSET( breaks, c ) ) + break; + } while (c>32); + + pToken[len] = 0; + return pFileBytes; +} + + +char* ParseFile( char* pFileBytes, char* pToken, bool* pWasQuoted ) +{ + return (char*)ParseFile( (const char*)pFileBytes, pToken, pWasQuoted ); +} + + + diff --git a/public/filesystem_helpers.h b/public/filesystem_helpers.h new file mode 100644 index 0000000..f2637cf --- /dev/null +++ b/public/filesystem_helpers.h @@ -0,0 +1,22 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef FILESYSTEM_HELPERS_H +#define FILESYSTEM_HELPERS_H + +#ifdef _WIN32 +#pragma once +#endif + + +// Call until it returns NULL. Each time you call it, it will parse out a token. +struct characterset_t; +const char* ParseFile( const char* pFileBytes, char* pToken, bool* pWasQuoted, characterset_t *pCharSet = NULL ); +char* ParseFile( char* pFileBytes, char* pToken, bool* pWasQuoted ); // (same exact thing as the const version) + + +#endif // FILESYSTEM_HELPERS_H diff --git a/public/filesystem_init.cpp b/public/filesystem_init.cpp new file mode 100644 index 0000000..d7b7120 --- /dev/null +++ b/public/filesystem_init.cpp @@ -0,0 +1,1564 @@ +//====== Copyright 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#undef PROTECTED_THINGS_ENABLE +#undef PROTECT_FILEIO_FUNCTIONS +#ifndef POSIX +#undef fopen +#endif + +#if defined( _WIN32 ) && !defined( _X360 ) +#include +#include +#include +#include +#elif defined( POSIX ) +#include +#endif +#include +#include +#include "tier1/strtools.h" +#include "filesystem_init.h" +#include "tier0/icommandline.h" +#include "tier0/stacktools.h" +#include "keyvalues.h" +#include "appframework/iappsystemgroup.h" +#include "tier1/smartptr.h" +#if defined( _X360 ) +#include "xbox\xbox_win32stubs.h" +#endif + +#include "tier2/tier2.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#if !defined( _X360 ) +#define GAMEINFO_FILENAME "GameInfo.txt" +#else +// The .xtx file is a TCR requirement, as .txt files cannot live on the DVD. +// The .xtx file only exists outside the zips (same as .txt and is made during the image build) and is read to setup the search paths. +// So all other code should be able to safely expect gameinfo.txt after the zip is mounted as the .txt file exists inside the zips. +// The .xtx concept is private and should only have to occurr here. As a safety measure, if the .xtx file is not found +// a retry is made with the original .txt name +#define GAMEINFO_FILENAME "gameinfo.xtx" +#endif +#define GAMEINFO_FILENAME_ALTERNATE "gameinfo.txt" + +static char g_FileSystemError[256]; +static bool s_bUseVProjectBinDir = false; +static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG; + +// Call this to use a bin directory relative to VPROJECT +void FileSystem_UseVProjectBinDir( bool bEnable ) +{ + s_bUseVProjectBinDir = bEnable; +} + +// This class lets you modify environment variables, and it restores the original value +// when it goes out of scope. +class CTempEnvVar +{ +public: + CTempEnvVar( const char *pVarName ) + { + m_bRestoreOriginalValue = true; + m_pVarName = pVarName; + + const char *pValue = NULL; + +#ifdef _WIN32 + // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes + // to the process environment after the DLL was loaded. + char szBuf[ 4096 ]; + if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0) + { + pValue = szBuf; + } +#else + // LINUX BUG: see above + pValue = getenv( pVarName ); +#endif + + if ( pValue ) + { + m_bExisted = true; + m_OriginalValue.SetSize( strlen( pValue ) + 1 ); + memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() ); + } + else + { + m_bExisted = false; + } + } + + ~CTempEnvVar() + { + if ( m_bRestoreOriginalValue ) + { + // Restore the original value. + if ( m_bExisted ) + { + SetValue( "%s", m_OriginalValue.Base() ); + } + else + { + ClearValue(); + } + } + } + + void SetRestoreOriginalValue( bool bRestore ) + { + m_bRestoreOriginalValue = bRestore; + } + + int GetValue(char *pszBuf, int nBufSize ) + { + if ( !pszBuf || ( nBufSize <= 0 ) ) + return 0; + +#ifdef _WIN32 + // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes + // to the process environment after the DLL was loaded. + return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize ); +#else + // LINUX BUG: see above + const char *pszOut = getenv( m_pVarName ); + if ( !pszOut ) + { + *pszBuf = '\0'; + return 0; + } + + Q_strncpy( pszBuf, pszOut, nBufSize ); + return Q_strlen( pszBuf ); +#endif + } + + void SetValue( const char *pValue, ... ) + { + char valueString[4096]; + va_list marker; + va_start( marker, pValue ); + Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker ); + va_end( marker ); + +#ifdef WIN32 + char str[4096]; + Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString ); + _putenv( str ); +#else + setenv( m_pVarName, valueString, 1 ); +#endif + } + + void ClearValue() + { +#ifdef WIN32 + char str[512]; + Q_snprintf( str, sizeof( str ), "%s=", m_pVarName ); + _putenv( str ); +#else + setenv( m_pVarName, "", 1 ); +#endif + } + +private: + bool m_bRestoreOriginalValue; + const char *m_pVarName; + bool m_bExisted; + CUtlVector m_OriginalValue; +}; + + +class CSteamEnvVars +{ +public: + CSteamEnvVars() : + m_SteamAppId( "SteamAppId" ), + m_SteamUserPassphrase( "SteamUserPassphrase" ), + m_SteamAppUser( "SteamAppUser" ), + m_Path( "path" ) + { + } + + void SetRestoreOriginalValue_ALL( bool bRestore ) + { + m_SteamAppId.SetRestoreOriginalValue( bRestore ); + m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore ); + m_SteamAppUser.SetRestoreOriginalValue( bRestore ); + m_Path.SetRestoreOriginalValue( bRestore ); + } + + CTempEnvVar m_SteamAppId; + CTempEnvVar m_SteamUserPassphrase; + CTempEnvVar m_SteamAppUser; + CTempEnvVar m_Path; +}; + +// ---------------------------------------------------------------------------------------------------- // +// Helpers. +// ---------------------------------------------------------------------------------------------------- // +void Q_getwd( char *out, int outSize ) +{ +#if defined( _WIN32 ) || defined( WIN32 ) + _getcwd( out, outSize ); + Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); +#else + getcwd( out, outSize ); + strcat( out, "/" ); +#endif + Q_FixSlashes( out ); +} + +// ---------------------------------------------------------------------------------------------------- // +// Module interface. +// ---------------------------------------------------------------------------------------------------- // + +CFSSearchPathsInit::CFSSearchPathsInit() +{ + m_pDirectoryName = NULL; + m_pLanguage = NULL; + m_ModPath[0] = 0; +} + + +CFSSteamSetupInfo::CFSSteamSetupInfo() +{ + m_pDirectoryName = NULL; + m_bOnlyUseDirectoryName = false; + m_bSteam = false; + m_bToolsMode = true; + m_bNoGameInfo = false; +} + + +CFSLoadModuleInfo::CFSLoadModuleInfo() +{ + m_pFileSystemDLLName = NULL; + m_pFileSystem = NULL; + m_pModule = NULL; +} + + +CFSMountContentInfo::CFSMountContentInfo() +{ + m_bToolsMode = true; + m_pDirectoryName = NULL; + m_pFileSystem = NULL; +} + + +const char *FileSystem_GetLastErrorString() +{ + return g_FileSystemError; +} + + +void AddLanguageGameDir( IFileSystem *pFileSystem, const char *pLocation, const char *pLanguage ) +{ +#if !defined( DEDICATED ) + char temp[MAX_PATH]; + Q_snprintf( temp, sizeof(temp), "%s_%s", pLocation, pLanguage ); + pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); + + if ( IsPC() && !pFileSystem->IsSteam() ) + { + // also look in "..\localization\" if not running Steam + char baseDir[MAX_PATH]; + char *tempPtr = NULL, *gameDir = NULL; + + Q_strncpy( baseDir, pLocation, sizeof(baseDir) ); + tempPtr = Q_strstr( baseDir, "\\game\\" ); + + if ( tempPtr ) + { + gameDir = tempPtr + Q_strlen( "\\game\\" ); + *tempPtr = 0; + Q_snprintf( temp, sizeof(temp), "%s%clocalization%c%s_%s", baseDir, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, gameDir, pLanguage ); + pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); + } + } +#endif +} + + +void AddGameBinDir( IFileSystem *pFileSystem, const char *pLocation ) +{ + char temp[MAX_PATH]; + Q_snprintf( temp, sizeof(temp), "%s%cbin", pLocation, CORRECT_PATH_SEPARATOR ); + pFileSystem->AddSearchPath( temp, "GAMEBIN", PATH_ADD_TO_TAIL ); +} + +KeyValues* ReadKeyValuesFile( const char *pFilename ) +{ + MEM_ALLOC_CREDIT(); + // Read in the gameinfo.txt file and null-terminate it. + FILE *fp = fopen( pFilename, "rb" ); + if ( !fp ) + return NULL; + CUtlVector buf; + fseek( fp, 0, SEEK_END ); + buf.SetSize( ftell( fp ) + 1 ); + fseek( fp, 0, SEEK_SET ); + fread( buf.Base(), 1, buf.Count()-1, fp ); + fclose( fp ); + buf[buf.Count()-1] = 0; + + KeyValues *kv = new KeyValues( "" ); + if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) ) + { + kv->deleteThis(); + return NULL; + } + + return kv; +} + +static bool Sys_GetExecutableName( char *out, int len ) +{ +#if defined( _WIN32 ) + if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) ) + { + return false; + } +#else + if ( CommandLine()->GetParm(0) ) + { + Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) ); + } + else + { + return false; + } +#endif + + return true; +} + +bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen ) +{ + exedir[0] = 0; + + if ( s_bUseVProjectBinDir ) + { + const char *pProject = GetVProjectCmdLineValue(); + if ( !pProject ) + { + // Check their registry. + pProject = getenv( GAMEDIR_TOKEN ); + } + if ( pProject ) + { + Q_snprintf( exedir, exeDirLen, "%s%c..%cbin", pProject, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR ); + return true; + } + return false; + } + + if ( !Sys_GetExecutableName( exedir, exeDirLen ) ) + return false; + Q_StripFilename( exedir ); + + if ( IsX360() ) + { + // The 360 can have its exe and dlls reside on different volumes + // use the optional basedir as the exe dir + if ( CommandLine()->FindParm( "-basedir" ) ) + { + strcpy( exedir, CommandLine()->ParmValue( "-basedir", "" ) ); + } + } + + Q_FixSlashes( exedir ); + + // Return the bin directory as the executable dir if it's not in there + // because that's really where we're running from... + char ext[MAX_PATH]; + Q_StrRight( exedir, 4, ext, sizeof( ext ) ); + if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 ) + { + Q_strncat( exedir, "\\bin", exeDirLen, COPY_ALL_CHARACTERS ); + Q_FixSlashes( exedir ); + } + + return true; +} + +static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen ) +{ + if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) ) + { + Q_StripFilename( baseDir ); + return true; + } + + return false; +} + +void LaunchVConfig() +{ +#if defined( _WIN32 ) && !defined( _X360 ) + char vconfigExe[MAX_PATH]; + FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) ); + Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) ); + Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS ); + + char *argv[] = + { + vconfigExe, + "-allowdebug", + NULL + }; + + _spawnv( _P_NOWAIT, vconfigExe, argv ); +#elif defined( _X360 ) + Msg( "Launching vconfig.exe not supported\n" ); +#endif +} + +const char* GetVProjectCmdLineValue() +{ + return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) ); +} + +FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... ) +{ + va_list marker; + va_start( marker, pMsg ); + Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker ); + va_end( marker ); + + Warning( "%s\n", g_FileSystemError ); + + // Run vconfig? + // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject. + if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() ) + { + LaunchVConfig(); + } + + if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG ) + { + Error( "%s\n", g_FileSystemError ); + } + + return retVal; +} + +FSReturnCode_t LoadGameInfoFile( + const char *pDirectoryName, + KeyValues *&pMainFile, + KeyValues *&pFileSystemInfo, + KeyValues *&pSearchPaths ) +{ + // If GameInfo.txt exists under pBaseDir, then this is their game directory. + // All the filesystem mappings will be in this file. + char gameinfoFilename[MAX_PATH]; + Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) ); + Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) ); + Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( gameinfoFilename ); + pMainFile = ReadKeyValuesFile( gameinfoFilename ); + if ( IsX360() && !pMainFile ) + { + // try again + Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) ); + Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) ); + Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( gameinfoFilename ); + pMainFile = ReadKeyValuesFile( gameinfoFilename ); + } + if ( !pMainFile ) + { + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename ); + } + + pFileSystemInfo = pMainFile->FindKey( "FileSystem" ); + if ( !pFileSystemInfo ) + { + pMainFile->deleteThis(); + return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); + } + + // Now read in all the search paths. + pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" ); + if ( !pSearchPaths ) + { + pMainFile->deleteThis(); + return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename ); + } + return FS_OK; +} + +// checks the registry for the low violence setting +// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3" +bool IsLowViolenceBuild( void ) +{ +#if defined( _LOWVIOLENCE ) + // a low violence build can not be-undone + return true; +#endif + + // Users can opt into low violence mode on the command-line. + if ( CommandLine()->FindParm( "-lv" ) != 0 ) + return true; + +#if defined(_WIN32) + HKEY hKey; + char szValue[64]; + unsigned long len = sizeof(szValue) - 1; + bool retVal = false; + + if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS ) + { + // User Token 2 + if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) + { + if ( Q_strlen( szValue ) > 0 ) + { + retVal = true; + } + } + + if ( !retVal ) + { + // reset "len" for the next check + len = sizeof(szValue) - 1; + + // User Token 3 + if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) + { + if ( Q_strlen( szValue ) > 0 ) + { + retVal = true; + } + } + } + + RegCloseKey(hKey); + } + + return retVal; +#elif POSIX + return false; +#elif + #error "Fix me" +#endif +} + +static void FileSystem_AddLoadedSearchPath( + CFSSearchPathsInit &initInfo, + const char *pPathID, + bool *bFirstGamePath, + const char *pBaseDir, + const char *pLocation, + bool bLowViolence ) +{ + char fullLocationPath[MAX_PATH]; + Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation, pBaseDir ); + + // Now resolve any ./'s. + V_FixSlashes( fullLocationPath ); + if ( !V_RemoveDotSlashes( fullLocationPath ) ) + Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", fullLocationPath ); + + // Add language, mod, and gamebin search paths automatically. + if ( Q_stricmp( pPathID, "game" ) == 0 ) + { + bool bDoAllPaths = true; +#if defined( _X360 ) && defined( LEFT4DEAD ) + // hl2 is a vestigal mistake due to shaders, xbox needs to prevent any search path bloat + if ( V_stristr( fullLocationPath, "\\hl2" ) ) + { + bDoAllPaths = false; + } +#endif + + // add the language path, needs to be topmost, generally only contains audio + // and the language localized movies (there are 2 version one normal, one LV) + // this trumps the LV english movie as desired for the language + if ( initInfo.m_pLanguage && bDoAllPaths ) + { + AddLanguageGameDir( initInfo.m_pFileSystem, fullLocationPath, initInfo.m_pLanguage ); + } + + // next add the low violence path + if ( bLowViolence && bDoAllPaths ) + { + char szPath[MAX_PATH]; + Q_snprintf( szPath, sizeof(szPath), "%s_lv", fullLocationPath ); + initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); + } + + if ( CommandLine()->FindParm( "-tempcontent" ) != 0 && bDoAllPaths ) + { + char szPath[MAX_PATH]; + Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath ); + initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); + } + + // mark the first "game" dir as the "MOD" dir + if ( *bFirstGamePath ) + { + *bFirstGamePath = false; + initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, "MOD", PATH_ADD_TO_TAIL ); + Q_strncpy( initInfo.m_ModPath, fullLocationPath, sizeof( initInfo.m_ModPath ) ); + } + + if ( bDoAllPaths ) + { + // add the game bin + AddGameBinDir( initInfo.m_pFileSystem, fullLocationPath ); + } + } + + initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL ); +} + + +bool FileSystem_IsHldsUpdateToolDedicatedServer() +{ + // To determine this, we see if the directory our executable was launched from is "orangebox". + // We only are under "orangebox" if we're run from hldsupdatetool. + char baseDir[MAX_PATH]; + if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) + return false; + + V_FixSlashes( baseDir ); + V_StripTrailingSlash( baseDir ); + const char *pLastDir = V_UnqualifiedFileName( baseDir ); + return ( pLastDir && V_stricmp( pLastDir, "orangebox" ) == 0 ); +} + +#ifdef ENGINE_DLL + extern void FileSystem_UpdateAddonSearchPaths( IFileSystem *pFileSystem ); +#endif + +FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) +{ + if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." ); + + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( retVal != FS_OK ) + return retVal; + + // All paths except those marked with |gameinfo_path| are relative to the base dir. + char baseDir[MAX_PATH]; + if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); + + initInfo.m_ModPath[0] = 0; + + #define GAMEINFOPATH_TOKEN "|gameinfo_path|" + #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|" + + bool bLowViolence = IsLowViolenceBuild(); + bool bFirstGamePath = true; + + for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) + { + const char *pPathID = pCur->GetName(); + const char *pLocation = pCur->GetString(); + + if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation ) + { + pLocation += strlen( GAMEINFOPATH_TOKEN ); + FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence ); + } + else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation ) + { + // This is a special identifier that tells it to add the specified path for all source engine versions equal to or prior to this version. + // So in Orange Box, if they specified: + // |all_source_engine_paths|hl2 + // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder. + // + // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places. + // In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots, + // so we can just use the path as-is. + + // In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder). + + pLocation += strlen( BASESOURCEPATHS_TOKEN ); + + // Add the Orange-box path (which also will include whatever the depots mapped in as well if we're + // running a Steam-launched app). + FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); + + if ( FileSystem_IsHldsUpdateToolDedicatedServer() ) + { + // If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too. + char ep1EraPath[MAX_PATH]; + V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation ); + FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence ); + } + } + else + { + FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); + } + } + + pMainFile->deleteThis(); + + // + // Set up search paths for add-ons + // + if ( IsPC() ) + { +#ifdef ENGINE_DLL + FileSystem_UpdateAddonSearchPaths( initInfo.m_pFileSystem ); +#endif + } + + // these specialized tool paths are not used on 360 and cause a costly constant perf tax, so inhibited + if ( IsPC() ) + { + // Create a content search path based on the game search path + const char *pGameRoot = getenv( GAMEROOT_TOKEN ); + const char *pContentRoot = getenv( CONTENTROOT_TOKEN ); + + if ( pGameRoot && pContentRoot ) + { + int nLen = initInfo.m_pFileSystem->GetSearchPath( "GAME", false, NULL, 0 ); + char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); + initInfo.m_pFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen ); + char *pPath = pSearchPath; + while( pPath ) + { + char *pSemiColon = strchr( pPath, ';' ); + if ( pSemiColon ) + { + *pSemiColon = 0; + } + + Q_StripTrailingSlash( pPath ); + Q_FixSlashes( pPath ); + + const char *pCurPath = pPath; + pPath = pSemiColon ? pSemiColon + 1 : NULL; + + char pRelativePath[MAX_PATH]; + char pContentPath[MAX_PATH]; + if ( !Q_MakeRelativePath( pCurPath, pGameRoot, pRelativePath, sizeof(pRelativePath) ) ) + continue; + + Q_ComposeFileName( pContentRoot, pRelativePath, pContentPath, sizeof(pContentPath) ); + initInfo.m_pFileSystem->AddSearchPath( pContentPath, "CONTENT" ); + } + + // Add the "platform" directory as a game searchable path + char pPlatformPath[MAX_PATH]; + Q_ComposeFileName( pGameRoot, "platform", pPlatformPath, sizeof(pPlatformPath) ); + initInfo.m_pFileSystem->AddSearchPath( pPlatformPath, "GAME", PATH_ADD_TO_TAIL ); + + initInfo.m_pFileSystem->AddSearchPath( pContentRoot, "CONTENTROOT" ); + initInfo.m_pFileSystem->AddSearchPath( pGameRoot, "GAMEROOT" ); + } + else + { + // Come up with some reasonable default + int nLen = initInfo.m_pFileSystem->GetSearchPath( "MOD", false, NULL, 0 ); + char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); + initInfo.m_pFileSystem->GetSearchPath( "MOD", false, pSearchPath, nLen ); + char *pSemiColon = strchr( pSearchPath, ';' ); + if ( pSemiColon ) + { + *pSemiColon = 0; + } + + char pGameRootPath[MAX_PATH]; + Q_strncpy( pGameRootPath, pSearchPath, sizeof(pGameRootPath) ); + Q_StripTrailingSlash( pGameRootPath ); + Q_StripFilename( pGameRootPath ); + + char pContentRootPath[MAX_PATH]; + Q_strncpy( pContentRootPath, pGameRootPath, sizeof(pContentRootPath) ); + char *pGame = Q_stristr( pContentRootPath, "game" ); + + if ( pGame ) + { + Q_strcpy( pGame, "content" ); + } + + initInfo.m_pFileSystem->AddSearchPath( pContentRootPath, "CONTENTROOT" ); + initInfo.m_pFileSystem->AddSearchPath( pGameRootPath, "GAMEROOT" ); + } + + // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them + // when people forget to specify a search path. + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "contentroot", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gameroot", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "content", true ); + } + + // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them + // when people forget to specify a search path. + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true ); + + // Add the write path last. + if ( initInfo.m_ModPath[0] != 0 ) + { + initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL ); + } + +#ifdef _DEBUG + initInfo.m_pFileSystem->PrintSearchPaths(); +#endif + +#if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) && !defined( _X360 ) + //copy search paths to stack tools so it can grab pdb's from all over. But only on P4 or Steam Beta builds + if( (CommandLine()->FindParm( "-steam" ) == 0) || //not steam + (CommandLine()->FindParm( "-internalbuild" ) != 0) ) //steam beta is ok + { + char szSearchPaths[4096]; + //int CBaseFileSystem::GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen ) + int iLength1 = initInfo.m_pFileSystem->GetSearchPath( "EXECUTABLE_PATH", false, szSearchPaths, 4096 ); + if( iLength1 == 1 ) + iLength1 = 0; + + int iLength2 = initInfo.m_pFileSystem->GetSearchPath( "GAMEBIN", false, szSearchPaths + iLength1, 4096 - iLength1 ); + if( (iLength2 > 1) && (iLength1 > 1) ) + { + szSearchPaths[iLength1 - 1] = ';'; //replace first null terminator + } + + const char *szAdditionalPath = CommandLine()->ParmValue( "-AdditionalPDBSearchPath" ); + if( szAdditionalPath && szAdditionalPath[0] ) + { + int iLength = iLength1; + if( iLength2 > 1 ) + iLength += iLength2; + + if( iLength != 0 ) + { + szSearchPaths[iLength - 1] = ';'; //replaces null terminator + } + V_strncpy( &szSearchPaths[iLength], szAdditionalPath, 4096 - iLength ); + } + + //Append the perforce symbol server last. Documentation says that "srv*\\perforce\symbols" should work, but it doesn't. + //"symsrv*symsrv.dll*\\perforce\symbols" which the docs say is the same statement, works. + { + V_strncat( szSearchPaths, ";symsrv*symsrv.dll*\\\\perforce\\symbols", 4096 ); + } + + SetStackTranslationSymbolSearchPath( szSearchPaths ); + //MessageBox( NULL, szSearchPaths, "Search Paths", 0 ); + } +#endif + + return FS_OK; +} + +bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename ) +{ + char filename[MAX_PATH]; + + Q_strncpy( filename, pDirectoryName, sizeof( filename ) ); + Q_AppendSlash( filename, sizeof( filename ) ); + Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( filename ); + bool bExist = ( _access( filename, 0 ) == 0 ); + + return ( bExist ); +} + +namespace +{ + SuggestGameInfoDirFn_t & GetSuggestGameInfoDirFn( void ) + { + static SuggestGameInfoDirFn_t s_pfnSuggestGameInfoDir = NULL; + return s_pfnSuggestGameInfoDir; + } +}; // `anonymous` namespace + +SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn ) +{ + SuggestGameInfoDirFn_t &rfn = GetSuggestGameInfoDirFn(); + SuggestGameInfoDirFn_t pfnOldFn = rfn; + rfn = pfnNewFn; + return pfnOldFn; +} + +static FSReturnCode_t TryLocateGameInfoFile( char *pOutDir, int outDirLen, bool bBubbleDir ) +{ + // Retain a copy of suggested path for further attempts + CArrayAutoPtr < char > spchCopyNameBuffer( new char [ outDirLen ] ); + Q_strncpy( spchCopyNameBuffer.Get(), pOutDir, outDirLen ); + spchCopyNameBuffer[ outDirLen - 1 ] = 0; + + // Make appropriate slashes ('/' - Linux style) + for ( char *pchFix = spchCopyNameBuffer.Get(), + *pchEnd = pchFix + outDirLen; + pchFix < pchEnd; ++ pchFix ) + { + if ( '\\' == *pchFix ) + { + *pchFix = '/'; + } + } + + // Have a look in supplied path + do + { + if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) + { + return FS_OK; + } + if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) ) + { + return FS_OK; + } + } + while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) ); + + // Make an attempt to resolve from "content -> game" directory + Q_strncpy( pOutDir, spchCopyNameBuffer.Get(), outDirLen ); + pOutDir[ outDirLen - 1 ] = 0; + if ( char *pchContentFix = Q_stristr( pOutDir, "/content/" ) ) + { + sprintf( pchContentFix, "/game/" ); + memmove( pchContentFix + 6, pchContentFix + 9, pOutDir + outDirLen - (pchContentFix + 9) ); + + // Try in the mapped "game" directory + do + { + if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) ) + { + return FS_OK; + } + if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) ) + { + return FS_OK; + } + } + while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) ); + } + + // Could not find it here + return FS_MISSING_GAMEINFO_FILE; +} + +FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen ) +{ + // Engine and Hammer don't want to search around for it. + if ( fsInfo.m_bOnlyUseDirectoryName ) + { + if ( !fsInfo.m_pDirectoryName ) + return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." ); + + bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME ); + if ( IsX360() && !bExists ) + { + bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE ); + } + if ( !bExists ) + { + if ( IsX360() && CommandLine()->FindParm( "-basedir" ) ) + { + char basePath[MAX_PATH]; + strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) ); + Q_AppendSlash( basePath, sizeof( basePath ) ); + Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS ); + if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) ) + { + Q_strncpy( pOutDir, basePath, outDirLen ); + return FS_OK; + } + if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) ) + { + Q_strncpy( pOutDir, basePath, outDirLen ); + return FS_OK; + } + } + + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "Setup file '%s' doesn't exist in subdirectory '%s'.\nCheck your -game parameter or VCONFIG setting.", GAMEINFO_FILENAME, fsInfo.m_pDirectoryName ); + } + + Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen ); + return FS_OK; + } + + // First, check for overrides on the command line or environment variables. + const char *pProject = GetVProjectCmdLineValue(); + + if ( pProject ) + { + if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) ) + { + Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ); + return FS_OK; + } + if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) ) + { + Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ); + return FS_OK; + } + + if ( IsX360() && CommandLine()->FindParm( "-basedir" ) ) + { + char basePath[MAX_PATH]; + strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) ); + Q_AppendSlash( basePath, sizeof( basePath ) ); + Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS ); + if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) ) + { + Q_strncpy( pOutDir, basePath, outDirLen ); + return FS_OK; + } + if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) ) + { + Q_strncpy( pOutDir, basePath, outDirLen ); + return FS_OK; + } + } + + if ( fsInfo.m_bNoGameInfo ) + { + // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use. + // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if + // -game is supplied on the command line. + Q_strncpy( pOutDir, "", outDirLen ); + return FS_OK; + } + else + { + // They either specified vproject on the command line or it's in their registry. Either way, + // we don't want to continue if they've specified it but it's not valid. + goto ShowError; + } + } + + if ( fsInfo.m_bNoGameInfo ) + { + Q_strncpy( pOutDir, "", outDirLen ); + return FS_OK; + } + + // Ask the application if it can provide us with a game info directory + { + bool bBubbleDir = true; + SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn(); + if ( pfnSuggestGameInfoDirFn && + ( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) && + FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) ) + return FS_OK; + } + + // Try to use the environment variable / registry + if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL && + ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) && + FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) ) + return FS_OK; + + if ( IsPC() ) + { + Warning( "Warning: falling back to auto detection of vproject directory.\n" ); + + // Now look for it in the directory they passed in. + if ( fsInfo.m_pDirectoryName ) + Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName ); + else + Q_MakeAbsolutePath( pOutDir, outDirLen, "." ); + + if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) ) + return FS_OK; + + // Use the CWD + Q_getwd( pOutDir, outDirLen ); + if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) ) + return FS_OK; + } + +ShowError: + return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, + "Unable to find %s. Solutions:\n\n" + "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n" + "2. Run vconfig to specify which game you're working on.\n" + "3. Add -game on the command line where is the directory that %s is in.\n", + GAMEINFO_FILENAME, GAMEINFO_FILENAME ); +} + +bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath ) +{ + // Fix the slashes in the input arguments. + char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH]; + Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) ); + Q_FixSlashes( correctedPathEnvVar ); + pPathEnvVar = correctedPathEnvVar; + + Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) ); + Q_FixSlashes( correctedTestPath ); + if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) ) + correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0; + + pTestPath = correctedTestPath; + + const char *pCurPos = pPathEnvVar; + while ( 1 ) + { + const char *pTestPos = Q_stristr( pCurPos, pTestPath ); + if ( !pTestPos ) + return false; + + // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon. + pTestPos += strlen( pTestPath ); + if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') ) + return true; + + // Advance our marker.. + pCurPos = pTestPos; + } +} + +FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings ) +{ + if ( IsConsole() ) + { + // consoles don't use steam + return FS_MISSING_STEAM_DLL; + } + + if ( IsPosix() ) + return FS_OK; // under posix the content does not live with steam.dll up the path, rely on the environment already being set by steam + + // Start at our bin directory and move up until we find a directory with steam.dll in it. + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + { + if ( bErrorsAsWarnings ) + { + Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" ); + return FS_INVALID_PARAMETERS; + } + else + { + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + } + } + + Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen ); +#ifdef WIN32 + const char *pchSteamDLL = "steam.dll"; +#elif defined(OSX) + // under osx the bin lives in the bin/ folder, so step back one + Q_StripLastDir( steamInstallPath, steamInstallPathLen ); + const char *pchSteamDLL = "libsteam.dylib"; +#elif defined(LINUX) + // under linux the bin lives in the bin/ folder, so step back one + Q_StripLastDir( steamInstallPath, steamInstallPathLen ); + const char *pchSteamDLL = "libsteam.so"; +#else +#error +#endif + while ( 1 ) + { + // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username. + // find + if ( DoesFileExistIn( steamInstallPath, pchSteamDLL ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) ) + break; + + if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) ) + { + if ( bErrorsAsWarnings ) + { + Warning( "Can't find %s relative to executable path: %s.\n", pchSteamDLL, executablePath ); + return FS_MISSING_STEAM_DLL; + } + else + { + return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find %s relative to executable path: %s.", pchSteamDLL, executablePath ); + } + } + } + + // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll. + char szPath[ 8192 ]; + steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) ); + if ( !DoesPathExistAlready( szPath, steamInstallPath ) ) + { +#ifdef WIN32 +#define PATH_SEP ";" +#else +#define PATH_SEP ":" +#endif + steamEnvVars.m_Path.SetValue( "%s%s%s", szPath, PATH_SEP, steamInstallPath ); + } + return FS_OK; +} + +FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ) +{ + steamCfgPath[0] = 0; + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + { + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + } + Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen ); + while ( 1 ) + { + if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) ) + break; + + if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) ) + { + // the file isnt found, thats ok, its not mandatory + return FS_OK; + } + } + Q_AppendSlash( steamCfgPath, steamCfgPathLen ); + Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS ); + + return FS_OK; +} + +void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars ) +{ + // Always inherit the Steam user if it's already set, since it probably means we (or the + // the app that launched us) were launched from Steam. + char appUser[MAX_PATH]; + if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) ) + return; + + const char *pTempAppUser = NULL; + if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL ) + { + Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); + } + else + { + // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user + // by looking in \config\SteamAppData.vdf. + char fullFilename[MAX_PATH]; + Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) ); + Q_AppendSlash( fullFilename, sizeof( fullFilename ) ); + Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS ); + + KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename ); + if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL ) + { + Error( "Can't find steam app user info." ); + } + Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) ); + + pSteamAppData->deleteThis(); + } + + Q_strlower( appUser ); + steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser ); +} + +void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars ) +{ + // Always inherit the passphrase if it's already set, since it probably means we (or the + // the app that launched us) were launched from Steam. + char szPassPhrase[ MAX_PATH ]; + if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) ) + return; + + // SteamUserPassphrase. + const char *pStr; + if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL ) + { + steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr ); + } +} + +void SetSteamAppId( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) +{ + // SteamAppId is in gameinfo.txt->FileSystem->FileSystemInfo_Steam->SteamAppId. + int iAppId = pFileSystemInfo->GetInt( "SteamAppId", -1 ); + if ( iAppId == -1 ) + Error( "Missing SteamAppId in %s\\%s.", pGameInfoDirectory, GAMEINFO_FILENAME ); + + steamEnvVars.m_SteamAppId.SetValue( "%d", iAppId ); +} + +FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) +{ + // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can. + char steamInfoFile[MAX_PATH]; + Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) ); + Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) ); + Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS ); + KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile ); + + char steamInstallPath[MAX_PATH]; + FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false ); + if ( ret != FS_OK ) + return ret; + + SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars ); + SetSteamUserPassphrase( pSteamInfo, steamEnvVars ); + SetSteamAppId( pFileSystemInfo, pGameInfoDirectory, steamEnvVars ); + + if ( pSteamInfo ) + pSteamInfo->deleteThis(); + + return FS_OK; +} + +FSReturnCode_t GetSteamExtraAppId( const char *pDirectoryName, int *nExtraAppId ) +{ + // Now, load gameinfo.txt (to make sure it's there) + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + FSReturnCode_t ret = LoadGameInfoFile( pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( ret != FS_OK ) + return ret; + + *nExtraAppId = pFileSystemInfo->GetInt( "ToolsAppId", -1 ); + pMainFile->deleteThis(); + return FS_OK; +} + +FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ) +{ + pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" ); + + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + + pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" ); + return FS_OK; +} + +//----------------------------------------------------------------------------- +// Returns the name of the file system DLL to use +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam ) +{ + bSteam = false; + + // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam + // is in this same directory with us. + char executablePath[MAX_PATH]; + if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); + +#if defined( _WIN32 ) + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR ); +#elif defined( POSIX ) + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio", executablePath, CORRECT_PATH_SEPARATOR ); + struct stat statBuf; + if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || stat( pFileSystemDLL, &statBuf ) != 0 ) + { + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dylib", executablePath, CORRECT_PATH_SEPARATOR ); + bSteam = true; + } + +#else + #error "define a filesystem dll name" +#endif + + return FS_OK; +} + +//----------------------------------------------------------------------------- +// Sets up the steam.dll install path in our PATH env var (so you can then just +// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special ) +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_SetupSteamInstallPath() +{ + CSteamEnvVars steamEnvVars; + char steamInstallPath[MAX_PATH]; + FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); + steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + return ret; +} + +//----------------------------------------------------------------------------- +// Sets up the steam environment + gets back the gameinfo.txt path +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo ) +{ + // First, locate the directory with gameinfo.txt. + FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) ); + if ( ret != FS_OK ) + return ret; + + // This is so that processes spawned by this application will have the same VPROJECT +#ifdef WIN32 + char pEnvBuf[MAX_PATH+32]; + Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath ); + _putenv( pEnvBuf ); +#else + setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 ); +#endif + + CSteamEnvVars steamEnvVars; + if ( fsInfo.m_bSteam ) + { + if ( fsInfo.m_bToolsMode ) + { + // Now, load gameinfo.txt (to make sure it's there) + KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; + ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths ); + if ( ret != FS_OK ) + return ret; + + + // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam. + ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars ); + if ( ret != FS_OK ) + return ret; + + steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + + // We're done with main file + pMainFile->deleteThis(); + } + else if ( fsInfo.m_bSetSteamDLLPath ) + { + // This is used by the engine to automatically set the path to their steam.dll when running the engine, + // so they can debug it without having to copy steam.dll up into their hl2.exe folder. + char steamInstallPath[MAX_PATH]; + ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); + steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. + } + } + + return FS_OK; +} + + +//----------------------------------------------------------------------------- +// Loads the file system module +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo ) +{ + // First, locate the directory with gameinfo.txt. + FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo ); + if ( ret != FS_OK ) + return ret; + + // Now that the environment is setup, load the filesystem module. + if ( !Sys_LoadInterface( + fsInfo.m_pFileSystemDLLName, + FILESYSTEM_INTERFACE_VERSION, + &fsInfo.m_pModule, + (void**)&fsInfo.m_pFileSystem ) ) + { + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName ); + } + + if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) ) + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName ); + + if ( fsInfo.m_pFileSystem->Init() != INIT_OK ) + return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName ); + + return FS_OK; +} + + +//----------------------------------------------------------------------------- +// Mounds a particular steam cache +//----------------------------------------------------------------------------- +FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo ) +{ + // This part is Steam-only. + if ( mountContentInfo.m_pFileSystem->IsSteam() ) + { + // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem + // like HL2, then mount the SDK content (tools materials and models, etc) in addition. + int nExtraAppId = -1; + if ( mountContentInfo.m_bToolsMode ) + { + FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId ); + if ( ret != FS_OK ) + return ret; + } + + // Set our working directory temporarily so Steam can remember it. + // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk + // to get to the relative part of the path. + char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH]; + if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); + + Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) ); + _chdir( baseDir ); + + // Filesystem_tools needs to add dependencies in here beforehand. + FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId ); + + _chdir( oldWorkingDir ); + + if ( retVal != FILESYSTEM_MOUNT_OK ) + return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" ); + } + + return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem ); +} + +void FileSystem_SetErrorMode( FSErrorMode_t errorMode ) +{ + g_FileSystemErrorMode = errorMode; +} + +void FileSystem_ClearSteamEnvVars() +{ + CSteamEnvVars envVars; + + // Change the values and don't restore the originals. + envVars.m_SteamAppId.SetValue( "" ); + envVars.m_SteamUserPassphrase.SetValue( "" ); + envVars.m_SteamAppUser.SetValue( "" ); + + envVars.SetRestoreOriginalValue_ALL( false ); +} + +//----------------------------------------------------------------------------- +// Adds the platform folder to the search path. +//----------------------------------------------------------------------------- +void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath ) +{ + char platform[MAX_PATH]; + if ( pFileSystem->IsSteam() ) + { + // Steam doesn't support relative paths + Q_strncpy( platform, "platform", MAX_PATH ); + } + else + { + if ( !Sys_GetExecutableName( platform, sizeof( platform ) ) ) + { + // fall back to old method if we can't get the executable name + Q_strncpy( platform, szGameInfoPath, MAX_PATH ); + Q_StripTrailingSlash( platform ); + Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH ); + } + else + { + Q_StripFilename( platform ); + Q_StripTrailingSlash( platform ); + Q_FixSlashes( platform ); + + // remove bin folder if necessary + int nLen = Q_strlen( platform ); + if ( ( nLen > 4 ) + && platform[ nLen - 4 ] == CORRECT_PATH_SEPARATOR + && !Q_stricmp( "bin", platform + ( nLen - 3 ) ) + ) + { + Q_StripLastDir( platform, sizeof( platform ) ); + Q_StripTrailingSlash( platform ); + } + // go into platform folder + Q_strncat( platform, "/platform", MAX_PATH, MAX_PATH ); + } + } + + pFileSystem->AddSearchPath( platform, "PLATFORM" ); +} diff --git a/public/filesystem_init.h b/public/filesystem_init.h new file mode 100644 index 0000000..58c7152 --- /dev/null +++ b/public/filesystem_init.h @@ -0,0 +1,223 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FILESYSTEM_INIT_H +#define FILESYSTEM_INIT_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "filesystem.h" + + +// If this option is on the command line, then filesystem_init won't bring up the vconfig +// dialog even if FS_ERRORMODE_VCONFIG is used. +#define CMDLINEOPTION_NOVCONFIG "-NoVConfig" + +#define GAMEDIR_TOKEN "VProject" +#define GAMEROOT_TOKEN "VGame" +#define CONTENTROOT_TOKEN "VContent" + + +#if defined( _WIN32 ) || defined( WIN32 ) +#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') +#else //_WIN32 +#define PATHSEPARATOR(c) ((c) == '/') +#endif //_WIN32 + + +enum FSReturnCode_t +{ + FS_OK, + FS_MISSING_GAMEINFO_FILE, + FS_INVALID_GAMEINFO_FILE, + FS_INVALID_PARAMETERS, + FS_UNABLE_TO_INIT, + FS_MISSING_STEAM_DLL +}; + + +enum FSErrorMode_t +{ + FS_ERRORMODE_AUTO, // Call Error() in case of an error. + FS_ERRORMODE_VCONFIG, // Call Error() for errors and run vconfig when appropriate. + FS_ERRORMODE_NONE, // Just return FSReturnCode values and setup the string for FileSystem_GetLastErrorString. +}; + + +class CFSSteamSetupInfo +{ +public: + CFSSteamSetupInfo(); + +// Inputs. +public: + // If this is set, then the init code will look in this directory up to the root for gameinfo.txt. + // It must be set for FileSystem_LoadSearchPaths to work. + // + // (default: null) + const char *m_pDirectoryName; + + // If this is true, then it won't look at -vproject, -game, or the vproject environment variable + // to find gameinfo.txt. If this is true, then m_pDirectoryName must be set. + // + // (default: false) + bool m_bOnlyUseDirectoryName; + + // If this is true, then: + // 1. It will set the environment variables that steam.dll looks at for startup info. + // 2. It will look for ToolsAppId in the gameinfo.txt file and load the + // steam caches associated with that cache if it's there. This is so apps like Hammer and hlmv + // can load the main steam caches (like for Counter-Strike or Half-Life 2), and also load the + // caches that include tools-specific materials (materials\editor, materials\debug, etc). + // + // (default: true - should be FALSE for the engine) + bool m_bToolsMode; + + // If this is true, and m_bToolsMode is false, then it will append the path to steam.dll to the + // PATH environment variable. This makes it so you can run the engine under Steam without + // having to copy steam.dll up into your hl2.exe folder. + // (default: false) + bool m_bSetSteamDLLPath; + + // Are we loading the Steam filesystem? This should be the same value that + // FileSystem_GetFileSystemDLLName gave you. + bool m_bSteam; + + // If this is true, then it won't look for a gameinfo.txt. + // + // (default: false) + bool m_bNoGameInfo; + +// Outputs (if it returns FS_OK). +public: + char m_GameInfoPath[512]; // The directory that gameinfo.txt lives in. +}; + + +class CFSLoadModuleInfo : public CFSSteamSetupInfo +{ +public: + CFSLoadModuleInfo(); + +// Inputs. +public: + // Full path to the file system DLL (gotten from FileSystem_GetFileSystemDLLName). + const char *m_pFileSystemDLLName; + + // Passed to IFileSystem::Connect. + CreateInterfaceFn m_ConnectFactory; + +// Outputs (if it returns FS_OK). +public: + // The filesystem you got from FileSystem_LoadFileSystemModule. + IFileSystem *m_pFileSystem; + CSysModule *m_pModule; +}; + + +class CFSMountContentInfo +{ +public: + CFSMountContentInfo(); + +// Inputs. +public: + + // See CFSLoadModuleInfo::m_bToolsMode (this valid should always be the same as you passed to CFSLoadModuleInfo::m_bToolsMode). + bool m_bToolsMode; + + // This specifies the directory where gameinfo.txt is. This must be set. + // It can come from CFSLoadModuleInfo::m_GameInfoPath. + const char *m_pDirectoryName; + + // Gotten from CFSLoadModuleInfo::m_pFileSystem. + IFileSystem *m_pFileSystem; +}; + + +class CFSSearchPathsInit +{ +public: + CFSSearchPathsInit(); + +// Inputs. +public: + // This specifies the directory where gameinfo.txt is. This must be set. + const char *m_pDirectoryName; + + // If this is set, then it will add a search path with _ appended to the pathname + // for each search path with a path ID of "game". + // (default: null) + const char *m_pLanguage; + + // This is the filesystem FileSystem_LoadSearchPaths is talking to. + IFileSystem *m_pFileSystem; + +// Outputs. +public: + // This is the location of the first search path called "game", which also becomes your "mod" search path. + char m_ModPath[512]; +}; + + +const char *GetVProjectCmdLineValue(); + + +// Call this to use a bin directory relative to VPROJECT +void FileSystem_UseVProjectBinDir( bool bEnable ); + +// This is used by all things that use the application framework: +// Note that the application framework automatically takes care of step 1 if you use CSteamApplication. +// Step 1: Ask filesystem_init for the name of the filesystem DLL to load +FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam ); + +// Step 2: Use filesystem framework to load/connect/init that filesystem DLL +// -or- just set up the steam environment and get back the gameinfo.txt path +// The second method is used by the application framework, which wants to connect/init the filesystem itself +FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &info ); +FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &info ); + +// Step 3: Ask filesystem_init to set up the executable search path, and mount the steam content based on the mod gameinfo.txt file +FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &fsInfo ); + +// Step 4: Load the search paths out of pGameDirectory\gameinfo.txt. +FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ); + +// This is automatically done during step 3, but if you want to redo all the search +// paths (like Hammer does), you can call this to reset executable_path. +FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ); + +// Utility function to add the PLATFORM search path. +void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath ); + +// See FSErrorMode_t. If you don't specify one here, then the default is FS_ERRORMODE_VCONFIG. +void FileSystem_SetErrorMode( FSErrorMode_t errorMode = FS_ERRORMODE_VCONFIG ); + +bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen ); + +// Clear SteamAppUser, SteamUserPassphrase, and SteamAppId from this process's environment. +// TODO: always do this after LoadFileSysteModule.. there's no reason it should be +// in the environment. +void FileSystem_ClearSteamEnvVars(); + +// Find the steam.cfg above you for optional stuff +FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ); + +// Setup the Steam.dll path without needing all the extra gameinfo stuff first +// used by the CSteamApplication::Create() code to LoadModule() on the filesystem +// before the underlying apps know specific details about the environment to load +FSReturnCode_t FileSystem_SetupSteamInstallPath(); + +// Returns the last error. +const char *FileSystem_GetLastErrorString(); + +void Q_getwd( char *out, int outSize ); + +KeyValues* ReadKeyValuesFile( const char *pFilename ); + +#endif // FILESYSTEM_INIT_H diff --git a/public/filesystem_passthru.h b/public/filesystem_passthru.h new file mode 100644 index 0000000..8403849 --- /dev/null +++ b/public/filesystem_passthru.h @@ -0,0 +1,280 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FILESYSTEM_PASSTHRU_H +#define FILESYSTEM_PASSTHRU_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "filesystem.h" +#include +#include + +#ifdef AsyncRead +#undef AsyncRead +#undef AsyncReadMutiple +#endif + +// +// These classes pass all filesystem interface calls through to another filesystem +// interface. They can be used anytime you want to override a couple things in +// a filesystem. VMPI uses this to override the base filesystem calls while +// allowing the rest of the filesystem functionality to work on the master. +// + +template +class CInternalFileSystemPassThru : public Base +{ +public: + CInternalFileSystemPassThru() + { + m_pBaseFileSystemPassThru = NULL; + } + virtual void InitPassThru( IBaseFileSystem *pBaseFileSystemPassThru ) + { + m_pBaseFileSystemPassThru = pBaseFileSystemPassThru; + } + virtual int Read( void* pOutput, int size, FileHandle_t file ) { return m_pBaseFileSystemPassThru->Read( pOutput, size, file ); } + virtual int Write( void const* pInput, int size, FileHandle_t file ) { return m_pBaseFileSystemPassThru->Write( pInput, size, file ); } + virtual FileHandle_t Open( const char *pFileName, const char *pOptions, const char *pathID ) { return m_pBaseFileSystemPassThru->Open( pFileName, pOptions, pathID ); } + virtual void Close( FileHandle_t file ) { m_pBaseFileSystemPassThru->Close( file ); } + virtual void Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType ) { m_pBaseFileSystemPassThru->Seek( file, pos, seekType ); } + virtual unsigned int Tell( FileHandle_t file ) { return m_pBaseFileSystemPassThru->Tell( file ); } + virtual unsigned int Size( FileHandle_t file ) { return m_pBaseFileSystemPassThru->Size( file ); } + virtual unsigned int Size( const char *pFileName, const char *pPathID ) { return m_pBaseFileSystemPassThru->Size( pFileName, pPathID ); } + virtual void Flush( FileHandle_t file ) { m_pBaseFileSystemPassThru->Flush( file ); } + virtual bool Precache( const char *pFileName, const char *pPathID ) { return m_pBaseFileSystemPassThru->Precache( pFileName, pPathID ); } + virtual bool FileExists( const char *pFileName, const char *pPathID ) { return m_pBaseFileSystemPassThru->FileExists( pFileName, pPathID ); } + virtual bool IsFileWritable( char const *pFileName, const char *pPathID ) { return m_pBaseFileSystemPassThru->IsFileWritable( pFileName, pPathID ); } + virtual bool SetFileWritable( char const *pFileName, bool writable, const char *pPathID ) { return m_pBaseFileSystemPassThru->SetFileWritable( pFileName, writable, pPathID ); } + virtual long GetFileTime( const char *pFileName, const char *pPathID ) { return m_pBaseFileSystemPassThru->GetFileTime( pFileName, pPathID ); } + virtual bool ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL ) { return m_pBaseFileSystemPassThru->ReadFile( pFileName, pPath, buf, nMaxBytes, nStartingByte, pfnAlloc ); } + virtual bool WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf ) { return m_pBaseFileSystemPassThru->WriteFile( pFileName, pPath, buf ); } + virtual bool UnzipFile( const char *pFileName, const char *pPath, const char *pDestination ) { return m_pBaseFileSystemPassThru->UnzipFile( pFileName, pPath, pDestination ); } + +protected: + IBaseFileSystem *m_pBaseFileSystemPassThru; +}; + + +class CBaseFileSystemPassThru : public CInternalFileSystemPassThru +{ +public: +}; + + +class CFileSystemPassThru : public CInternalFileSystemPassThru +{ +public: + typedef CInternalFileSystemPassThru BaseClass; + + CFileSystemPassThru() + { + m_pFileSystemPassThru = NULL; + } + virtual void InitPassThru( IFileSystem *pFileSystemPassThru, bool bBaseOnly ) + { + if ( !bBaseOnly ) + m_pFileSystemPassThru = pFileSystemPassThru; + + BaseClass::InitPassThru( pFileSystemPassThru ); + } + + // IAppSystem stuff. + // Here's where the app systems get to learn about each other + virtual bool Connect( CreateInterfaceFn factory ) { return m_pFileSystemPassThru->Connect( factory ); } + virtual void Disconnect() { m_pFileSystemPassThru->Disconnect(); } + virtual void *QueryInterface( const char *pInterfaceName ) { return m_pFileSystemPassThru->QueryInterface( pInterfaceName ); } + virtual InitReturnVal_t Init() { return m_pFileSystemPassThru->Init(); } + virtual void Shutdown() { m_pFileSystemPassThru->Shutdown(); } + virtual const AppSystemInfo_t* GetDependencies() { return m_pFileSystemPassThru->GetDependencies(); } + virtual AppSystemTier_t GetTier() { return m_pFileSystemPassThru->GetTier(); } + virtual void Reconnect( CreateInterfaceFn factory, const char *pInterfaceName ) { m_pFileSystemPassThru->Reconnect( factory, pInterfaceName ); } + + virtual void RemoveAllSearchPaths( void ) { m_pFileSystemPassThru->RemoveAllSearchPaths(); } + virtual void AddSearchPath( const char *pPath, const char *pathID, SearchPathAdd_t addType ) { m_pFileSystemPassThru->AddSearchPath( pPath, pathID, addType ); } + virtual bool RemoveSearchPath( const char *pPath, const char *pathID ) { return m_pFileSystemPassThru->RemoveSearchPath( pPath, pathID ); } + virtual void RemoveFile( char const* pRelativePath, const char *pathID ) { m_pFileSystemPassThru->RemoveFile( pRelativePath, pathID ); } + virtual bool RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID ) { return m_pFileSystemPassThru->RenameFile( pOldPath, pNewPath, pathID ); } + virtual void CreateDirHierarchy( const char *path, const char *pathID ) { m_pFileSystemPassThru->CreateDirHierarchy( path, pathID ); } + virtual bool IsDirectory( const char *pFileName, const char *pathID ) { return m_pFileSystemPassThru->IsDirectory( pFileName, pathID ); } + virtual void FileTimeToString( char* pStrip, int maxCharsIncludingTerminator, long fileTime ) { m_pFileSystemPassThru->FileTimeToString( pStrip, maxCharsIncludingTerminator, fileTime ); } + virtual void SetBufferSize( FileHandle_t file, unsigned nBytes ) { m_pFileSystemPassThru->SetBufferSize( file, nBytes ); } + virtual bool IsOk( FileHandle_t file ) { return m_pFileSystemPassThru->IsOk( file ); } + virtual bool EndOfFile( FileHandle_t file ) { return m_pFileSystemPassThru->EndOfFile( file ); } + virtual char *ReadLine( char *pOutput, int maxChars, FileHandle_t file ) { return m_pFileSystemPassThru->ReadLine( pOutput, maxChars, file ); } + virtual int FPrintf( FileHandle_t file, const char *pFormat, ... ) + { + char str[8192]; + va_list marker; + va_start( marker, pFormat ); + _vsnprintf( str, sizeof( str ), pFormat, marker ); + va_end( marker ); + return m_pFileSystemPassThru->FPrintf( file, "%s", str ); + } + virtual CSysModule *LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly ) { return m_pFileSystemPassThru->LoadModule( pFileName, pPathID, bValidatedDllOnly ); } + virtual void UnloadModule( CSysModule *pModule ) { m_pFileSystemPassThru->UnloadModule( pModule ); } + virtual const char *FindFirst( const char *pWildCard, FileFindHandle_t *pHandle ) { return m_pFileSystemPassThru->FindFirst( pWildCard, pHandle ); } + virtual const char *FindNext( FileFindHandle_t handle ) { return m_pFileSystemPassThru->FindNext( handle ); } + virtual bool FindIsDirectory( FileFindHandle_t handle ) { return m_pFileSystemPassThru->FindIsDirectory( handle ); } + virtual void FindClose( FileFindHandle_t handle ) { m_pFileSystemPassThru->FindClose( handle ); } + virtual const char *GetLocalPath( const char *pFileName, char *pLocalPath, int localPathBufferSize ) { return m_pFileSystemPassThru->GetLocalPath( pFileName, pLocalPath, localPathBufferSize ); } + virtual bool FullPathToRelativePath( const char *pFullpath, char *pRelative, int maxlen ) { return m_pFileSystemPassThru->FullPathToRelativePath( pFullpath, pRelative, maxlen ); } + virtual bool GetCurrentDirectory( char* pDirectory, int maxlen ) { return m_pFileSystemPassThru->GetCurrentDirectory( pDirectory, maxlen ); } + virtual void PrintOpenedFiles( void ) { m_pFileSystemPassThru->PrintOpenedFiles(); } + virtual void PrintSearchPaths( void ) { m_pFileSystemPassThru->PrintSearchPaths(); } + virtual void SetWarningFunc( void (*pfnWarning)( const char *fmt, ... ) ) { m_pFileSystemPassThru->SetWarningFunc( pfnWarning ); } + virtual void SetWarningLevel( FileWarningLevel_t level ) { m_pFileSystemPassThru->SetWarningLevel( level ); } + virtual void AddLoggingFunc( void (*pfnLogFunc)( const char *fileName, const char *accessType ) ){ m_pFileSystemPassThru->AddLoggingFunc( pfnLogFunc ); } + virtual void RemoveLoggingFunc( FileSystemLoggingFunc_t logFunc ) { m_pFileSystemPassThru->RemoveLoggingFunc( logFunc ); } + virtual FSAsyncStatus_t AsyncReadMultiple( const FileAsyncRequest_t *pRequests, int nRequests, FSAsyncControl_t *pControls ) { return m_pFileSystemPassThru->AsyncReadMultiple( pRequests, nRequests, pControls ); } + virtual FSAsyncStatus_t AsyncReadMultipleCreditAlloc( const FileAsyncRequest_t *pRequests, int nRequests, const char *pszFile, int line, FSAsyncControl_t *pControls ) { return m_pFileSystemPassThru->AsyncReadMultipleCreditAlloc( pRequests, nRequests, pszFile, line, pControls ); } + virtual FSAsyncStatus_t AsyncDirectoryScan( const char* pSearchSpec, bool recurseFolders, void* pContext, FSAsyncScanAddFunc_t pfnAdd, FSAsyncScanCompleteFunc_t pfnDone, FSAsyncControl_t *pControl = NULL ) { return m_pFileSystemPassThru->AsyncDirectoryScan( pSearchSpec, recurseFolders, pContext, pfnAdd, pfnDone, pControl ); } + virtual FSAsyncStatus_t AsyncFinish(FSAsyncControl_t hControl, bool wait) { return m_pFileSystemPassThru->AsyncFinish( hControl, wait ); } + virtual FSAsyncStatus_t AsyncGetResult( FSAsyncControl_t hControl, void **ppData, int *pSize ) { return m_pFileSystemPassThru->AsyncGetResult( hControl, ppData, pSize ); } + virtual FSAsyncStatus_t AsyncAbort(FSAsyncControl_t hControl) { return m_pFileSystemPassThru->AsyncAbort( hControl ); } + virtual FSAsyncStatus_t AsyncStatus(FSAsyncControl_t hControl) { return m_pFileSystemPassThru->AsyncStatus( hControl ); } + virtual FSAsyncStatus_t AsyncFlush() { return m_pFileSystemPassThru->AsyncFlush(); } + virtual void AsyncAddRef( FSAsyncControl_t hControl ) { m_pFileSystemPassThru->AsyncAddRef( hControl ); } + virtual void AsyncRelease( FSAsyncControl_t hControl ) { m_pFileSystemPassThru->AsyncRelease( hControl ); } + virtual FSAsyncStatus_t AsyncBeginRead( const char *pszFile, FSAsyncFile_t *phFile ) { return m_pFileSystemPassThru->AsyncBeginRead( pszFile, phFile ); } + virtual FSAsyncStatus_t AsyncEndRead( FSAsyncFile_t hFile ) { return m_pFileSystemPassThru->AsyncEndRead( hFile ); } + virtual const FileSystemStatistics *GetFilesystemStatistics() { return m_pFileSystemPassThru->GetFilesystemStatistics(); } + virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist ) { return m_pFileSystemPassThru->WaitForResources( resourcelist ); } + virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, + float *progress, bool *complete ) { return m_pFileSystemPassThru->GetWaitForResourcesProgress( handle, progress, complete ); } + virtual void CancelWaitForResources( WaitForResourcesHandle_t handle ) { m_pFileSystemPassThru->CancelWaitForResources( handle ); } + virtual int HintResourceNeed( const char *hintlist, int forgetEverything ) { return m_pFileSystemPassThru->HintResourceNeed( hintlist, forgetEverything ); } + virtual bool IsFileImmediatelyAvailable(const char *pFileName) { return m_pFileSystemPassThru->IsFileImmediatelyAvailable( pFileName ); } + virtual void GetLocalCopy( const char *pFileName ) { m_pFileSystemPassThru->GetLocalCopy( pFileName ); } + virtual FileNameHandle_t FindOrAddFileName( char const *pFileName ) { return m_pFileSystemPassThru->FindOrAddFileName( pFileName ); } + virtual FileNameHandle_t FindFileName( char const *pFileName ) { return m_pFileSystemPassThru->FindFileName( pFileName ); } + virtual bool String( const FileNameHandle_t& handle, char *buf, int buflen ) { return m_pFileSystemPassThru->String( handle, buf, buflen ); } + virtual bool IsOk2( FileHandle_t file ) { return IsOk(file); } + virtual void RemoveSearchPaths( const char *szPathID ) { m_pFileSystemPassThru->RemoveSearchPaths( szPathID ); } + virtual bool IsSteam() const { return m_pFileSystemPassThru->IsSteam(); } + virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 ) { return m_pFileSystemPassThru->MountSteamContent( nExtraAppId ); } + + virtual const char *FindFirstEx( + const char *pWildCard, + const char *pPathID, + FileFindHandle_t *pHandle + ) { return m_pFileSystemPassThru->FindFirstEx( pWildCard, pPathID, pHandle ); } + virtual void FindFileAbsoluteList( + CUtlVector> &output, + const char *pWildCard, + const char *pPathID + ) { m_pFileSystemPassThru->FindFileAbsoluteList( output, pWildCard, pPathID ); } + virtual void MarkPathIDByRequestOnly( const char *pPathID, bool bRequestOnly ) { m_pFileSystemPassThru->MarkPathIDByRequestOnly( pPathID, bRequestOnly ); } + virtual bool AddPackFile( const char *fullpath, const char *pathID ) { return m_pFileSystemPassThru->AddPackFile( fullpath, pathID ); } + virtual FSAsyncStatus_t AsyncAppend(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl ) { return m_pFileSystemPassThru->AsyncAppend( pFileName, pSrc, nSrcBytes, bFreeMemory, pControl); } + virtual FSAsyncStatus_t AsyncWrite(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl ) { return m_pFileSystemPassThru->AsyncWrite( pFileName, pSrc, nSrcBytes, bFreeMemory, bAppend, pControl); } + virtual FSAsyncStatus_t AsyncWriteFile(const char *pFileName, const CUtlBuffer *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl ) { return m_pFileSystemPassThru->AsyncWriteFile( pFileName, pSrc, nSrcBytes, bFreeMemory, bAppend, pControl); } + virtual FSAsyncStatus_t AsyncAppendFile(const char *pDestFileName, const char *pSrcFileName, FSAsyncControl_t *pControl ) { return m_pFileSystemPassThru->AsyncAppendFile(pDestFileName, pSrcFileName, pControl); } + virtual void AsyncFinishAll( int iToPriority ) { m_pFileSystemPassThru->AsyncFinishAll(iToPriority); } + virtual void AsyncFinishAllWrites() { m_pFileSystemPassThru->AsyncFinishAllWrites(); } + virtual FSAsyncStatus_t AsyncSetPriority(FSAsyncControl_t hControl, int newPriority) { return m_pFileSystemPassThru->AsyncSetPriority(hControl, newPriority); } + virtual bool AsyncSuspend() { return m_pFileSystemPassThru->AsyncSuspend(); } + virtual bool AsyncResume() { return m_pFileSystemPassThru->AsyncResume(); } + virtual const char *RelativePathToFullPath( const char *pFileName, const char *pPathID, char *pLocalPath, int localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t *pPathType = NULL ) { return m_pFileSystemPassThru->RelativePathToFullPath( pFileName, pPathID, pLocalPath, localPathBufferSize, pathFilter, pPathType ); } + virtual int GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen ) { return m_pFileSystemPassThru->GetSearchPath( pathID, bGetPackFiles, pPath, nMaxLen ); } + + virtual FileHandle_t OpenEx( const char *pFileName, const char *pOptions, unsigned flags = 0, const char *pathID = 0, char **ppszResolvedFilename = NULL ) { return m_pFileSystemPassThru->OpenEx( pFileName, pOptions, flags, pathID, ppszResolvedFilename );} + virtual int ReadEx( void* pOutput, int destSize, int size, FileHandle_t file ) { return m_pFileSystemPassThru->ReadEx( pOutput, destSize, size, file ); } + virtual int ReadFileEx( const char *pFileName, const char *pPath, void **ppBuf, bool bNullTerminate, bool bOptimalAlloc, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL ) { return m_pFileSystemPassThru->ReadFileEx( pFileName, pPath, ppBuf, bNullTerminate, bOptimalAlloc, nMaxBytes, nStartingByte, pfnAlloc ); } + +#if defined( TRACK_BLOCKING_IO ) + virtual void EnableBlockingFileAccessTracking( bool state ) { m_pFileSystemPassThru->EnableBlockingFileAccessTracking( state ); } + virtual bool IsBlockingFileAccessEnabled() const { return m_pFileSystemPassThru->IsBlockingFileAccessEnabled(); } + + virtual IBlockingFileItemList *RetrieveBlockingFileAccessInfo() { return m_pFileSystemPassThru->RetrieveBlockingFileAccessInfo(); } +#endif + virtual void SetupPreloadData() {} + virtual void DiscardPreloadData() {} + + // If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of compiled keyvalues loaded during startup. + // Otherwise, it'll just fall through to the regular KeyValues loading routines + virtual KeyValues *LoadKeyValues( KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ) { return m_pFileSystemPassThru->LoadKeyValues( type, filename, pPathID ); } + virtual bool LoadKeyValues( KeyValues& head, KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ) { return m_pFileSystemPassThru->LoadKeyValues( head, type, filename, pPathID ); } + + virtual bool GetFileTypeForFullPath( char const *pFullPath, wchar_t *buf, size_t bufSizeInBytes ) { return m_pFileSystemPassThru->GetFileTypeForFullPath( pFullPath, buf, bufSizeInBytes ); } + + virtual bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign ) { return m_pFileSystemPassThru->GetOptimalIOConstraints( hFile, pOffsetAlign, pSizeAlign, pBufferAlign ); } + virtual void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset ) { return m_pFileSystemPassThru->AllocOptimalReadBuffer( hFile, nOffset, nSize ); } + virtual void FreeOptimalReadBuffer( void *p ) { m_pFileSystemPassThru->FreeOptimalReadBuffer( p ); } + + virtual void BeginMapAccess() { m_pFileSystemPassThru->BeginMapAccess(); } + virtual void EndMapAccess() { m_pFileSystemPassThru->EndMapAccess(); } + + virtual bool ReadToBuffer( FileHandle_t hFile, CUtlBuffer &buf, int nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL ) { return m_pFileSystemPassThru->ReadToBuffer( hFile, buf, nMaxBytes, pfnAlloc ); } + virtual bool FullPathToRelativePathEx( const char *pFullPath, const char *pPathId, char *pRelative, int nMaxLen ) { return m_pFileSystemPassThru->FullPathToRelativePathEx( pFullPath, pPathId, pRelative, nMaxLen ); } + virtual int GetPathIndex( const FileNameHandle_t &handle ) { return m_pFileSystemPassThru->GetPathIndex( handle ); } + virtual long GetPathTime( const char *pPath, const char *pPathID ) { return m_pFileSystemPassThru->GetPathTime( pPath, pPathID ); } + + virtual DVDMode_t GetDVDMode() { return m_pFileSystemPassThru->GetDVDMode(); } + + virtual void EnableWhitelistFileTracking( bool bEnable ) + { m_pFileSystemPassThru->EnableWhitelistFileTracking( bEnable ); } + virtual void RegisterFileWhitelist( IFileList *pForceMatchList, IFileList *pAllowFromDiskList, IFileList **pFilesToReload ) + { m_pFileSystemPassThru->RegisterFileWhitelist( pForceMatchList, pAllowFromDiskList, pFilesToReload ); } + virtual void MarkAllCRCsUnverified() + { m_pFileSystemPassThru->MarkAllCRCsUnverified(); } + virtual void CacheFileCRCs( const char *pPathname, ECacheCRCType eType, IFileList *pFilter ) + { return m_pFileSystemPassThru->CacheFileCRCs( pPathname, eType, pFilter ); } + virtual EFileCRCStatus CheckCachedFileCRC( const char *pPathID, const char *pRelativeFilename, CRC32_t *pCRC ) + { return m_pFileSystemPassThru->CheckCachedFileCRC( pPathID, pRelativeFilename, pCRC ); } + virtual int GetUnverifiedCRCFiles( CUnverifiedCRCFile *pFiles, int nMaxFiles ) + { return m_pFileSystemPassThru->GetUnverifiedCRCFiles( pFiles, nMaxFiles ); } + virtual int GetWhitelistSpewFlags() + { return m_pFileSystemPassThru->GetWhitelistSpewFlags(); } + virtual void SetWhitelistSpewFlags( int spewFlags ) + { m_pFileSystemPassThru->SetWhitelistSpewFlags( spewFlags ); } + virtual void InstallDirtyDiskReportFunc( FSDirtyDiskReportFunc_t func ) { m_pFileSystemPassThru->InstallDirtyDiskReportFunc( func ); } + + virtual bool IsLaunchedFromXboxHDD() { return m_pFileSystemPassThru->IsLaunchedFromXboxHDD(); } + virtual bool IsInstalledToXboxHDDCache() { return m_pFileSystemPassThru->IsInstalledToXboxHDDCache(); } + virtual bool IsDVDHosted() { return m_pFileSystemPassThru->IsDVDHosted(); } + virtual bool IsInstallAllowed() { return m_pFileSystemPassThru->IsInstallAllowed(); } + virtual int GetSearchPathID( char *pPath, int nMaxLen ) { return m_pFileSystemPassThru->GetSearchPathID( pPath, nMaxLen ); } + virtual bool FixupSearchPathsAfterInstall() { return m_pFileSystemPassThru->FixupSearchPathsAfterInstall(); } + + virtual FSDirtyDiskReportFunc_t GetDirtyDiskReportFunc() { return m_pFileSystemPassThru->GetDirtyDiskReportFunc(); } + + virtual void AddVPKFile( char const *pPkName, SearchPathAdd_t addType = PATH_ADD_TO_TAIL ) { m_pFileSystemPassThru->AddVPKFile( pPkName, addType ); } + virtual void RemoveVPKFile( char const *pPkName ) { m_pFileSystemPassThru->RemoveVPKFile( pPkName ); } + virtual void GetVPKFileNames( CUtlVector &destVector ) { m_pFileSystemPassThru->GetVPKFileNames( destVector ); } + + virtual void RemoveAllMapSearchPaths( void ) { m_pFileSystemPassThru->RemoveAllMapSearchPaths(); } + + virtual void SyncDvdDevCache( void ) { m_pFileSystemPassThru->SyncDvdDevCache(); } + + virtual bool GetStringFromKVPool( CRC32_t poolKey, unsigned int key, char *pOutBuff, int buflen ) { return m_pFileSystemPassThru->GetStringFromKVPool( poolKey, key, pOutBuff, buflen ); } + + virtual bool DiscoverDLC( int iController ) { return m_pFileSystemPassThru->DiscoverDLC( iController ); } + virtual int IsAnyDLCPresent( bool *pbDLCSearchPathMounted = NULL ) { return m_pFileSystemPassThru->IsAnyDLCPresent( pbDLCSearchPathMounted ); } + virtual bool GetAnyDLCInfo( int iDLC, unsigned int *pLicenseMask, wchar_t *pTitleBuff, int nOutTitleSize ) { return m_pFileSystemPassThru->GetAnyDLCInfo( iDLC, pLicenseMask, pTitleBuff, nOutTitleSize ); } + virtual int IsAnyCorruptDLC() { return m_pFileSystemPassThru->IsAnyCorruptDLC(); } + virtual bool GetAnyCorruptDLCInfo( int iCorruptDLC, wchar_t *pTitleBuff, int nOutTitleSize ) { return m_pFileSystemPassThru->GetAnyCorruptDLCInfo( iCorruptDLC, pTitleBuff, nOutTitleSize ); } + virtual bool AddDLCSearchPaths() { return m_pFileSystemPassThru->AddDLCSearchPaths(); } + virtual bool IsSpecificDLCPresent( unsigned int nDLCPackage ) { return m_pFileSystemPassThru->IsSpecificDLCPresent( nDLCPackage ); } + virtual void SetIODelayAlarm( float flThreshhold ) { m_pFileSystemPassThru->SetIODelayAlarm( flThreshhold ); } + +protected: + IFileSystem *m_pFileSystemPassThru; +}; + + +// This is so people who change the filesystem interface are forced to add the passthru wrapper into CFileSystemPassThru, +// so they don't break VMPI. +inline void GiveMeACompileError() +{ + CFileSystemPassThru asdf; +} + + +#endif // FILESYSTEM_PASSTHRU_H diff --git a/public/game/client/IGameClientExports.h b/public/game/client/IGameClientExports.h new file mode 100644 index 0000000..75673bc --- /dev/null +++ b/public/game/client/IGameClientExports.h @@ -0,0 +1,38 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IGAMECLIENTEXPORTS_H +#define IGAMECLIENTEXPORTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +//----------------------------------------------------------------------------- +// Purpose: Exports a set of functions for the GameUI interface to interact with the game client +//----------------------------------------------------------------------------- +abstract_class IGameClientExports : public IBaseInterface +{ +public: + // ingame voice manipulation + virtual bool IsPlayerGameVoiceMuted(int playerIndex) = 0; + virtual void MutePlayerGameVoice(int playerIndex) = 0; + virtual void UnmutePlayerGameVoice(int playerIndex) = 0; + + // notification of gameui state changes + virtual void OnGameUIActivated() = 0; + virtual void OnGameUIHidden() = 0; + + // if true, the gameui applies the blur effect + virtual bool ClientWantsBlurEffect( void ) = 0; +}; + +#define GAMECLIENTEXPORTS_INTERFACE_VERSION "GameClientExports001" + + +#endif // IGAMECLIENTEXPORTS_H diff --git a/public/game/client/iclientrendertargets.h b/public/game/client/iclientrendertargets.h new file mode 100644 index 0000000..5b71a56 --- /dev/null +++ b/public/game/client/iclientrendertargets.h @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Exposes interfaces to the engine which allow the client to setup their own render targets +// during the proper period of material system's init. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICLIENTRENDERTARGETS_H +#define ICLIENTRENDERTARGETS_H +#ifdef _WIN32 + #pragma once +#endif + +#include "interface.h" // For base interface + +class IMaterialSystem; +class IMaterialSystemHardwareConfig; + +//--------------------------------------------------------------------------------------------------- +// Purpose: Exposes interfaces to the engine which allow the client to setup their own render targets +// during the proper period of material system's init. +//--------------------------------------------------------------------------------------------------- +abstract_class IClientRenderTargets +{ +public: + // Pass the material system interface to the client-- Their Material System singleton has not been created + // at the time they receive this call. + virtual void InitClientRenderTargets( IMaterialSystem* pMaterialSystem, IMaterialSystemHardwareConfig* pHardwareConfig ) = 0; + + // Call shutdown on every created refrence-- Clients keep track of this themselves + // and should add shutdown code to this function whenever they add a new render target. + virtual void ShutdownClientRenderTargets( void ) = 0; + +}; + +#define CLIENTRENDERTARGETS_INTERFACE_VERSION "ClientRenderTargets001" + +extern IClientRenderTargets * g_pClientRenderTargets; + +#endif // ICLIENTRENDERTARGETS_H diff --git a/public/game/client/iviewport.h b/public/game/client/iviewport.h new file mode 100644 index 0000000..aeda5ac --- /dev/null +++ b/public/game/client/iviewport.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( IVIEWPORT_H ) +#define IVIEWPORT_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +#include "viewport_panel_names.h" + +class KeyValues; + +abstract_class IViewPortPanel +{ + +public: + virtual ~IViewPortPanel() {}; + + virtual const char *GetName( void ) = 0;// return identifer name + virtual void SetData(KeyValues *data) = 0; // set ViewPortPanel data + virtual void Reset( void ) = 0; // clears internal state, deactivates it + virtual void Update( void ) = 0; // updates all (size, position, content, etc) + virtual bool NeedsUpdate( void ) = 0; // query panel if content needs to be updated + virtual bool HasInputElements( void ) = 0; // true if panel contains elments which accepts input + virtual void ReloadScheme( void ) {} + virtual bool CanReplace( const char *panelName ) const { return true; } // returns true if this panel can appear on top of the given panel + virtual bool CanBeReopened( void ) const { return true; } // returns true if this panel can be re-opened after being hidden by another panel + + virtual void ShowPanel( bool state ) = 0; // activate VGUI Frame + + // VGUI functions: + virtual vgui::VPANEL GetVPanel( void ) = 0; // returns VGUI panel handle + virtual bool IsVisible() = 0; // true if panel is visible + virtual void SetParent( vgui::VPANEL parent ) = 0; + + virtual bool WantsBackgroundBlurred( void ) = 0; +}; + +abstract_class IViewPort +{ +public: + virtual void UpdateAllPanels( void ) = 0; + virtual void ShowPanel( const char *pName, bool state, KeyValues *data, bool autoDeleteData = true ) = 0; + virtual void ShowPanel( const char *pName, bool state ) = 0; + virtual void ShowPanel( IViewPortPanel* pPanel, bool state ) = 0; + virtual void ShowBackGround(bool bShow) = 0; + virtual IViewPortPanel* FindPanelByName(const char *szPanelName) = 0; + virtual IViewPortPanel* GetActivePanel( void ) = 0; + virtual void RecreatePanel( const char *szPanelName ) = 0; + virtual void PostMessageToPanel( const char *pName, KeyValues *pKeyValues ) = 0; +}; + +extern IViewPort *GetViewPortInterface(); +extern IViewPort *GetFullscreenViewPortInterface(); + +#endif // IVIEWPORT_H \ No newline at end of file diff --git a/public/game/server/ientityinfo.h b/public/game/server/ientityinfo.h new file mode 100644 index 0000000..67ac4f8 --- /dev/null +++ b/public/game/server/ientityinfo.h @@ -0,0 +1,150 @@ +//============ Copyright � 1996-2008, Valve Corporation, All rights reserved. ===================// +// +// Purpose: provides an interface for plugins to query information about entities from the game dll +// +//===============================================================================================// +#ifndef IENTITYINFO_H +#define IENTITYINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" +#include "pluginvariant.h" + +abstract_class IEntityInfo +{ +public: + // returns true if entity is a player + virtual const int EntityIndex() = 0; + virtual const char *GetEntityName() = 0; + virtual const char *GetClassname() = 0; + virtual const char *GetModelName() = 0; +//TODO + virtual const char *GetTargetName() = 0; + virtual void SetModel(const char *modelName) = 0; + virtual bool IsPlayer() = 0; + virtual bool IsNPC() = 0; + virtual bool IsDead() = 0; + virtual bool IsAlive() = 0; + virtual bool IsInWorld() = 0; + virtual bool IsTemplate() = 0; + virtual int GetEFlags() = 0; + virtual void SetEFlags( int iEFlags ) = 0; + virtual void AddEFlags( int nEFlagMask ) = 0; + virtual bool IsEFlagSet( int EFlagMask ) = 0; + + virtual const int GetEffects( void ) = 0; + virtual void AddEffects( int nEffects ) = 0; + virtual void RemoveEffects( int nEffects ) = 0; + virtual void ClearEffects( void ) = 0; + virtual void SetEffects( int nEffects ) = 0; + virtual bool IsEffectActive( int nEffects ) = 0; + virtual int GetRenderMode() = 0; + virtual void SetRenderMode( int nRenderMode ) = 0; + + virtual void SetBlocksLOS( bool bBlocksLOS ) = 0; + virtual bool BlocksLOS( void ) = 0; + + virtual const int GetHealth() = 0; + virtual const int GetMaxHealth() = 0; + virtual void SetHealth( int iHealth ) = 0; + virtual void SetMaxHealth( int iMaxHealth ) = 0; + + // returns the team the entity is on + virtual int GetTeamIndex() = 0; + // changes the entity to a new team (if the game dll logic allows it) + virtual void ChangeTeam( int iTeamNum ) = 0; + + // positioning and sizes + virtual const Vector GetAbsOrigin() = 0; + virtual void SetAbsOrigin( Vector & vec ) = 0; + virtual const QAngle GetAbsAngles() = 0; + virtual void SetAbsAngles( QAngle & ang ) = 0; + virtual const Vector GetLocalOrigin() = 0; + virtual void SetLocalOrigin( const Vector& origin ) = 0; + virtual const QAngle GetLocalAngles() = 0; + virtual void SetLocalAngles( const QAngle& angles ) = 0; + virtual const Vector GetAbsVelocity() = 0; + virtual const Vector GetLocalVelocity() = 0; + virtual const QAngle GetLocalAngularVelocity() = 0; + virtual void EntityToWorldSpace( const Vector &in, Vector *pOut ) = 0; + virtual void WorldToEntitySpace( const Vector &in, Vector *pOut ) = 0; + virtual Vector EyePosition() = 0; + virtual QAngle EyeAngles() = 0; + virtual QAngle LocalEyeAngles() = 0; + virtual Vector EarPosition() = 0; + + // returns world aligned mins/maxs of this entity + virtual const Vector GetWorldMins() = 0; + virtual const Vector GetWorldMaxs() = 0; + virtual const Vector WorldSpaceCenter() = 0; + + virtual int GetWaterLevel() = 0; + + // if this entity has an owner, it returns their edict_t. + virtual edict_t *GetOwner() = 0; + virtual edict_t *GetParent() = 0; + virtual edict_t *GetMoveParent() = 0; + virtual edict_t *GetRootMoveParent() = 0; + + // if this entity is following another, returns that entities edict_t. + virtual edict_t *GetFollowedEntity() = 0; + virtual edict_t *GetGroundEntity() = 0; //returns the entity that this one is standing on - if set. + + // accessor to hook mod specific information about the entity. + virtual bool GetCustomInfo(int valueType, pluginvariant &outValue, pluginvariant options) = 0; + + // entity debugging stuff. + virtual const char *GetDebugName() = 0; + virtual void EntityText( int text_offset, const char *text, float flDuration, int r = 255, int g = 255, int b = 255, int a = 255 ) = 0; + + //Keyvalues + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) = 0; + + +}; + + +#define INTERFACEVERSION_ENTITYINFOMANAGER "EntityInfoManager001" +abstract_class IEntityInfoManager +{ +public: + virtual IEntityInfo *GetEntityInfo( edict_t *pEdict ) = 0; + virtual IEntityInfo *GetEntityInfo( int index ) = 0; //Retrieves the info + + //Experiment.. + virtual IServerUnknown *GetServerEntity( edict_t *pEdict ) = 0; + + //----------------------------------------------------------------------------- + // Purpose: Iterates the entities with a given classname. + // Input : pStartEntity - Last entity found, NULL to start a new iteration. + // szName - Classname to search for. + //----------------------------------------------------------------------------- + virtual edict_t *FindEntityByClassname( edict_t *pStartEntity, const char *szName ) = 0; + + //----------------------------------------------------------------------------- + // Purpose: Iterates the entities with a given name. + // Input : pStartEntity - Last entity found, NULL to start a new iteration. + // szName - Name to search for. + //----------------------------------------------------------------------------- + virtual edict_t *FindEntityByName( edict_t *pStartEntity, const char *szName ) = 0; + + //----------------------------------------------------------------------------- + // Purpose: Iterates the entities with a given model name. + // Input : pStartEntity - Last entity found, NULL to start a new iteration. + // szModelName - Model Name to search for. + //----------------------------------------------------------------------------- + virtual edict_t *FindEntityByModel( edict_t *pStartEntity, const char *szModelName ) = 0; + + //----------------------------------------------------------------------------- + // Purpose: Used to iterate all the entities within a sphere. + // Input : pStartEntity - + // vecCenter - + // flRadius - + //----------------------------------------------------------------------------- + virtual edict_t *FindEntityInSphere( edict_t *pStartEntity, const Vector &vecCenter, float flRadius ) = 0; + + virtual void GetWorldBounds( Vector &mins, Vector &maxs ) = 0; +}; +#endif // IENTITYINFO_H \ No newline at end of file diff --git a/public/game/server/igameinfo.h b/public/game/server/igameinfo.h new file mode 100644 index 0000000..11746a5 --- /dev/null +++ b/public/game/server/igameinfo.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: provides an interface for plugins to query information about the gamerules in a simple +// and organized mannor. +// +//===============================================================================================// +#ifndef IGAMEINFO_H +#define IGAMEINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" +#include "pluginvariant.h" + +//Tony; prefixing everything in this so that i can make IGameInfo an extension of CGameRules and not stomp on anything, since gamerules isn't an entity. +abstract_class IGameInfo +{ +public: + // returns an enumerated id for the current game type + virtual const int GetInfo_GameType() = 0; + // returns a name associated with the gametype, if defined. + virtual const char *GetInfo_GameTypeName() = 0; + // returns the team name associated with the number + virtual const char *GetInfo_GetTeamName(int teamNumber) = 0; + // returns how many teams the game has (typically always 4; 0 = unassigned, 1 = spectator, 2 = team1, 3 = team2) + virtual const int GetInfo_GetTeamCount() = 0; + // returns how many players are on a given team + virtual const int GetInfo_NumPlayersOnTeam(int teamNumber) = 0; + + // accessor to hook mod specific information about the rules. for TF2, fields such as + virtual bool GetInfo_Custom(int valueType, pluginvariant &outValue, pluginvariant options) = 0; + +}; + + +//Interface is very simple, there's not much really needed for the manager, this stuff is just in it's own interface so it's not mixed up with the entity +//or player managers. +#define INTERFACEVERSION_GAMEINFOMANAGER "GameInfoManager001" +abstract_class IGameInfoManager +{ +public: + virtual IGameInfo *GetGameInfo() = 0; +}; +#endif // IGAMEINFO_H \ No newline at end of file diff --git a/public/game/server/iplayerinfo.h b/public/game/server/iplayerinfo.h new file mode 100644 index 0000000..cf21c69 --- /dev/null +++ b/public/game/server/iplayerinfo.h @@ -0,0 +1,198 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: provides an interface for dlls to query information about players from the game dll +// +//=============================================================================// +#ifndef IPLAYERINFO_H +#define IPLAYERINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" + +// helper class for user commands +class CBotCmd +{ +public: + CBotCmd() + { + Reset(); + } + + virtual ~CBotCmd() { }; + + void Reset() + { + command_number = 0; + tick_count = 0; + viewangles.Init(); + forwardmove = 0.0f; + sidemove = 0.0f; + upmove = 0.0f; + buttons = 0; + impulse = 0; + weaponselect = 0; + weaponsubtype = 0; + random_seed = 0; + mousedx = 0; + mousedy = 0; + + hasbeenpredicted = false; + } + + CBotCmd& operator =( const CBotCmd& src ) + { + if ( this == &src ) + return *this; + + command_number = src.command_number; + tick_count = src.tick_count; + viewangles = src.viewangles; + forwardmove = src.forwardmove; + sidemove = src.sidemove; + upmove = src.upmove; + buttons = src.buttons; + impulse = src.impulse; + weaponselect = src.weaponselect; + weaponsubtype = src.weaponsubtype; + random_seed = src.random_seed; + mousedx = src.mousedx; + mousedy = src.mousedy; + hasbeenpredicted = src.hasbeenpredicted; + return *this; + } + + // For matching server and client commands for debugging + int command_number; + + // the tick the client created this command + int tick_count; + + // Player instantaneous view angles. + QAngle viewangles; + // Intended velocities + // forward velocity. + float forwardmove; + // sideways velocity. + float sidemove; + // upward velocity. + float upmove; + // Attack button states + int buttons; + // Impulse command issued. + byte impulse; + // Current weapon id + int weaponselect; + int weaponsubtype; + + int random_seed; // For shared random functions + + short mousedx; // mouse accum in x from create move + short mousedy; // mouse accum in y from create move + + // Client only, tracks whether we've predicted this command at least once + bool hasbeenpredicted; +}; + + + + +abstract_class IPlayerInfo +{ +public: + // returns the players name (UTF-8 encoded) + virtual const char *GetName() = 0; + // returns the userid (slot number) + virtual int GetUserID() = 0; + // returns the string of their network (i.e Steam) ID + virtual const char *GetNetworkIDString() = 0; + // returns the team the player is on + virtual int GetTeamIndex() = 0; + // changes the player to a new team (if the game dll logic allows it) + virtual void ChangeTeam( int iTeamNum ) = 0; + // returns the number of kills this player has (exact meaning is mod dependent) + virtual int GetFragCount() = 0; + // returns the number of deaths this player has (exact meaning is mod dependent) + virtual int GetDeathCount() = 0; + // returns if this player slot is actually valid + virtual bool IsConnected() = 0; + // returns the armor/health of the player (exact meaning is mod dependent) + virtual int GetArmorValue() = 0; + + // extensions added to V2 + + // various player flags + virtual bool IsHLTV() = 0; +#if defined( REPLAY_ENABLED ) + virtual bool IsReplay() = 0; +#endif + virtual bool IsPlayer() = 0; + virtual bool IsFakeClient() = 0; + virtual bool IsDead() = 0; + virtual bool IsInAVehicle() = 0; + virtual bool IsObserver() = 0; + + // player position and size + virtual const Vector GetAbsOrigin() = 0; + virtual const QAngle GetAbsAngles() = 0; + virtual const Vector GetPlayerMins() = 0; + virtual const Vector GetPlayerMaxs() = 0; + // the name of the weapon currently being carried + virtual const char *GetWeaponName() = 0; + // the name of the player model in use + virtual const char *GetModelName() = 0; + // current player health + virtual const int GetHealth() = 0; + // max health value + virtual const int GetMaxHealth() = 0; + // the last user input from this player + virtual CBotCmd GetLastUserCommand() = 0; +}; + + +#define INTERFACEVERSION_PLAYERINFOMANAGER "PlayerInfoManager002" +abstract_class IPlayerInfoManager +{ +public: + virtual IPlayerInfo *GetPlayerInfo( edict_t *pEdict ) = 0; + virtual CGlobalVars *GetGlobalVars() = 0; +}; + + + + +abstract_class IBotController +{ +public: + // change the bots position + virtual void SetAbsOrigin( Vector & vec ) = 0; + virtual void SetAbsAngles( QAngle & ang ) = 0; + virtual void SetLocalOrigin( const Vector& origin ) = 0; + virtual const Vector GetLocalOrigin( void ) = 0; + virtual void SetLocalAngles( const QAngle& angles ) = 0; + virtual const QAngle GetLocalAngles( void ) = 0; + + // strip them of weapons, etc + virtual void RemoveAllItems( bool removeSuit ) = 0; + // give them a weapon + virtual void SetActiveWeapon( const char *WeaponName ) = 0; + // called after running a move command + virtual void PostClientMessagesSent( void ) = 0; + // check various effect flags + virtual bool IsEFlagSet( int nEFlagMask ) = 0; + // fire a virtual move command to the bot + virtual void RunPlayerMove( CBotCmd *ucmd ) = 0; +}; + + +#define INTERFACEVERSION_PLAYERBOTMANAGER "BotManager001" +abstract_class IBotManager +{ +public: + virtual IBotController *GetBotController( edict_t *pEdict ) = 0; + // create a new bot and spawn it into the server + virtual edict_t *CreateBot( const char *botname ) = 0; +}; + +#endif // IPLAYERINFO_H diff --git a/public/game/server/pluginvariant.h b/public/game/server/pluginvariant.h new file mode 100644 index 0000000..82687fa --- /dev/null +++ b/public/game/server/pluginvariant.h @@ -0,0 +1,165 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PLUGINVARIANT_H +#define PLUGINVARIANT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "stdstring.h" +#include "mathlib/vmatrix.h" + +/* +//Tony; including stdstring at this point (which I need) messes up the offsetof override in linux, so I need to reset it here. +#if defined( POSIX ) +#undef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + */ +// +// A modified variant class for that functions almost identically to variant_t, for plugins to pass data back and forth. +// +class pluginvariant +{ + union + { + bool bVal; + int iVal; + float flVal; + float vecVal[3]; + color32 rgbaVal; + }; + //Tony; neither of these can be in the union because of constructors. + edict_t *eVal; + char iszVal[1024]; + + fieldtype_t fieldType; + +public: + + // constructor + pluginvariant() : fieldType(FIELD_VOID), iVal(0) {} + + inline bool Bool( void ) const { return( fieldType == FIELD_BOOLEAN ) ? bVal : false; } + inline const char *String( void ) const { return( ToString() ); } + inline int Int( void ) const { return( fieldType == FIELD_INTEGER ) ? iVal : 0; } + inline float Float( void ) const { return( fieldType == FIELD_FLOAT ) ? flVal : 0; } + inline const edict_t *Edict(void) const; + inline color32 Color32(void) const { return rgbaVal; } + inline void Vector3D(Vector &vec) const; + + fieldtype_t FieldType( void ) { return fieldType; } + + void SetBool( bool b ) { bVal = b; fieldType = FIELD_BOOLEAN; } + void SetString( char *str ) { Q_snprintf(iszVal, 1024, "%s", str); fieldType = FIELD_STRING; } + void SetInt( int val ) { iVal = val, fieldType = FIELD_INTEGER; } + void SetFloat( float val ) { flVal = val, fieldType = FIELD_FLOAT; } + void SetEdict( edict_t *val ) { eVal = val; fieldType = FIELD_EHANDLE; } + void SetVector3D( const Vector &val ) { vecVal[0] = val[0]; vecVal[1] = val[1]; vecVal[2] = val[2]; fieldType = FIELD_VECTOR; } + void SetPositionVector3D( const Vector &val ) { vecVal[0] = val[0]; vecVal[1] = val[1]; vecVal[2] = val[2]; fieldType = FIELD_POSITION_VECTOR; } + void SetColor32( color32 val ) { rgbaVal = val; fieldType = FIELD_COLOR32; } + void SetColor32( int r, int g, int b, int a ) { rgbaVal.r = r; rgbaVal.g = g; rgbaVal.b = b; rgbaVal.a = a; fieldType = FIELD_COLOR32; } + +protected: + + // + // Returns a string representation of the value without modifying the variant. + // + const char *ToString( void ) const + { + static char szBuf[512]; + + switch (fieldType) + { + case FIELD_STRING: + { + return (const char *)iszVal; + } + + case FIELD_BOOLEAN: + { + if (bVal == 0) + { + Q_strncpy(szBuf, "false",sizeof(szBuf)); + } + else + { + Q_strncpy(szBuf, "true",sizeof(szBuf)); + } + return(szBuf); + } + + case FIELD_INTEGER: + { + Q_snprintf( szBuf, sizeof( szBuf ), "%i", iVal ); + return(szBuf); + } + + case FIELD_FLOAT: + { + Q_snprintf(szBuf,sizeof(szBuf), "%g", flVal); + return(szBuf); + } + + case FIELD_COLOR32: + { + Q_snprintf(szBuf,sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a); + return(szBuf); + } + + case FIELD_VECTOR: + { + Q_snprintf(szBuf,sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]); + return(szBuf); + } + + case FIELD_VOID: + { + szBuf[0] = '\0'; + return(szBuf); + } + } + + return("No conversion to string"); + } +}; + + +////////////////////////// pluginvariant implementation ////////////////////////// + + +//----------------------------------------------------------------------------- +// Purpose: Returns this variant as a vector. +//----------------------------------------------------------------------------- +inline void pluginvariant::Vector3D(Vector &vec) const +{ + if (( fieldType == FIELD_VECTOR ) || ( fieldType == FIELD_POSITION_VECTOR )) + { + vec[0] = vecVal[0]; + vec[1] = vecVal[1]; + vec[2] = vecVal[2]; + } + else + { + vec = vec3_origin; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns this variant as an edict_t +//----------------------------------------------------------------------------- +inline const edict_t *pluginvariant::Edict(void) const +{ + if ( fieldType == FIELD_EHANDLE ) + return eVal; + + return NULL; +} + + +#endif // pluginvariant_H \ No newline at end of file diff --git a/public/gamebspfile.h b/public/gamebspfile.h new file mode 100644 index 0000000..2ad5194 --- /dev/null +++ b/public/gamebspfile.h @@ -0,0 +1,292 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines game-specific data +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef GAMEBSPFILE_H +#define GAMEBSPFILE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/vector.h" +#include "basetypes.h" + + +//----------------------------------------------------------------------------- +// This enumerations defines all the four-CC codes for the client lump names +//----------------------------------------------------------------------------- +enum +{ + GAMELUMP_DETAIL_PROPS = 'dprp', + GAMELUMP_DETAIL_PROP_LIGHTING = 'dplt', + GAMELUMP_STATIC_PROPS = 'sprp', + GAMELUMP_DETAIL_PROP_LIGHTING_HDR = 'dplh', +}; + +// Versions... +enum +{ + GAMELUMP_DETAIL_PROPS_VERSION = 4, + GAMELUMP_DETAIL_PROP_LIGHTING_VERSION = 0, + GAMELUMP_STATIC_PROPS_MIN_VERSION = 4, + GAMELUMP_STATIC_PROPS_VERSION = 9, + GAMELUMP_STATIC_PROP_LIGHTING_VERSION = 0, + GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION = 0, +}; + + +//----------------------------------------------------------------------------- +// This is the data associated with the GAMELUMP_DETAIL_PROPS lump +//----------------------------------------------------------------------------- +#define DETAIL_NAME_LENGTH 128 + +enum DetailPropOrientation_t +{ + DETAIL_PROP_ORIENT_NORMAL = 0, + DETAIL_PROP_ORIENT_SCREEN_ALIGNED, + DETAIL_PROP_ORIENT_SCREEN_ALIGNED_VERTICAL, +}; + +// NOTE: If DetailPropType_t enum changes, change CDetailModel::QuadsToDraw +// in detailobjectsystem.cpp +enum DetailPropType_t +{ + DETAIL_PROP_TYPE_MODEL = 0, + DETAIL_PROP_TYPE_SPRITE, + DETAIL_PROP_TYPE_SHAPE_CROSS, + DETAIL_PROP_TYPE_SHAPE_TRI, +}; + +//----------------------------------------------------------------------------- +// Model index when using studiomdls for detail props +//----------------------------------------------------------------------------- +struct DetailObjectDictLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + char m_Name[DETAIL_NAME_LENGTH]; // model name +}; + +//----------------------------------------------------------------------------- +// Information about the sprite to render +//----------------------------------------------------------------------------- +struct DetailSpriteDictLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + // NOTE: All detail prop sprites must lie in the material detail/detailsprites + Vector2D m_UL; // Coordinate of upper left + Vector2D m_LR; // Coordinate of lower right + Vector2D m_TexUL; // Texcoords of upper left + Vector2D m_TexLR; // Texcoords of lower left +}; + +struct DetailObjectLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_DetailModel; // either index into DetailObjectDictLump_t or DetailPropSpriteLump_t + unsigned short m_Leaf; + ColorRGBExp32 m_Lighting; + unsigned int m_LightStyles; + unsigned char m_LightStyleCount; + unsigned char m_SwayAmount; // how much do the details sway + unsigned char m_ShapeAngle; // angle param for shaped sprites + unsigned char m_ShapeSize; // size param for shaped sprites + unsigned char m_Orientation; // See DetailPropOrientation_t + unsigned char m_Padding2[3]; // FIXME: Remove when we rev the detail lump again.. + unsigned char m_Type; // See DetailPropType_t + unsigned char m_Padding3[3]; // FIXME: Remove when we rev the detail lump again.. + float m_flScale; // For sprites only currently +}; + +//----------------------------------------------------------------------------- +// This is the data associated with the GAMELUMP_DETAIL_PROP_LIGHTING lump +//----------------------------------------------------------------------------- +struct DetailPropLightstylesLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + ColorRGBExp32 m_Lighting; + unsigned char m_Style; +}; + +//----------------------------------------------------------------------------- +// This is the data associated with the GAMELUMP_STATIC_PROPS lump +//----------------------------------------------------------------------------- +enum +{ + STATIC_PROP_NAME_LENGTH = 128, + + // Flags field + // These are automatically computed + STATIC_PROP_FLAG_FADES = 0x1, + STATIC_PROP_USE_LIGHTING_ORIGIN = 0x2, + STATIC_PROP_NO_DRAW = 0x4, // computed at run time based on dx level + + // These are set in WC + STATIC_PROP_IGNORE_NORMALS = 0x8, + STATIC_PROP_NO_SHADOW = 0x10, + STATIC_PROP_UNUSED = 0x20, + + STATIC_PROP_NO_PER_VERTEX_LIGHTING = 0x40, // in vrad, compute lighting at + // lighting origin, not for each vertex + + STATIC_PROP_NO_SELF_SHADOWING = 0x80, // disable self shadowing in vrad + + STATIC_PROP_WC_MASK = 0xd8, // all flags settable in hammer (?) +}; + +enum +{ + STATIC_PROP_SCREEN_SPACE_FADE_OBSOLETE = 0x20, // only keeping these here to be able to report errors +}; + +struct StaticPropDictLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + char m_Name[STATIC_PROP_NAME_LENGTH]; // model name +}; + +struct StaticPropLumpV4_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; +// int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump +}; + +struct StaticPropLumpV5_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; + float m_flForcedFadeScale; +// int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump +}; + +struct StaticPropLumpV6_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; + float m_flForcedFadeScale; + unsigned short m_nMinDXLevel; + unsigned short m_nMaxDXLevel; + // int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump +}; + +struct StaticPropLumpV7_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; + float m_flForcedFadeScale; + unsigned short m_nMinDXLevel; + unsigned short m_nMaxDXLevel; + // int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump + color32 m_DiffuseModulation; // per instance color and alpha modulation +}; + +struct StaticPropLumpV8_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; + float m_flForcedFadeScale; + unsigned char m_nMinCPULevel; + unsigned char m_nMaxCPULevel; + unsigned char m_nMinGPULevel; + unsigned char m_nMaxGPULevel; + // int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump + color32 m_DiffuseModulation; // per instance color and alpha modulation +}; + +struct StaticPropLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector m_Origin; + QAngle m_Angles; + unsigned short m_PropType; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + unsigned char m_Solid; + unsigned char m_Flags; + int m_Skin; + float m_FadeMinDist; + float m_FadeMaxDist; + Vector m_LightingOrigin; + float m_flForcedFadeScale; + unsigned char m_nMinCPULevel; + unsigned char m_nMaxCPULevel; + unsigned char m_nMinGPULevel; + unsigned char m_nMaxGPULevel; + // int m_Lighting; // index into the GAMELUMP_STATIC_PROP_LIGHTING lump + color32 m_DiffuseModulation; // per instance color and alpha modulation + bool m_bDisableX360; +}; + +struct StaticPropLeafLump_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short m_Leaf; +}; + +//----------------------------------------------------------------------------- +// This is the data associated with the GAMELUMP_STATIC_PROP_LIGHTING lump +//----------------------------------------------------------------------------- +struct StaticPropLightstylesLump_t +{ + ColorRGBExp32 m_Lighting; +}; + +#endif // GAMEBSPFILE_H diff --git a/public/gametrace.h b/public/gametrace.h new file mode 100644 index 0000000..133c1fb --- /dev/null +++ b/public/gametrace.h @@ -0,0 +1,111 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef GAMETRACE_H +#define GAMETRACE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "cmodel.h" +#include "utlvector.h" +#include "ihandleentity.h" +#include "ispatialpartition.h" + +#if defined( CLIENT_DLL ) + class C_BaseEntity; +#else + class CBaseEntity; +#endif + + +//----------------------------------------------------------------------------- +// Purpose: A trace is returned when a box is swept through the world +// NOTE: eventually more of this class should be moved up into the base class!! +//----------------------------------------------------------------------------- +class CGameTrace : public CBaseTrace +{ +public: + + // Returns true if hEnt points at the world entity. + // If this returns true, then you can't use GetHitBoxIndex(). + bool DidHitWorld() const; + + // Returns true if we hit something and it wasn't the world. + bool DidHitNonWorldEntity() const; + + // Gets the entity's network index if the trace has hit an entity. + // If not, returns -1. + int GetEntityIndex() const; + + // Returns true if there was any kind of impact at all + bool DidHit() const; + + // The engine doesn't know what a CBaseEntity is, so it has a backdoor to + // let it get at the edict. +#if defined( ENGINE_DLL ) + void SetEdict( edict_t *pEdict ); + edict_t* GetEdict() const; +#endif + + +public: + + float fractionleftsolid; // time we left a solid, only valid if we started in solid + csurface_t surface; // surface hit (impact surface) + + int hitgroup; // 0 == generic, non-zero is specific body part + + short physicsbone; // physics bone hit by trace in studio + unsigned short worldSurfaceIndex; // Index of the msurface2_t, if applicable + +#if defined( CLIENT_DLL ) + C_BaseEntity *m_pEnt; +#else + CBaseEntity *m_pEnt; +#endif + + // NOTE: this member is overloaded. + // If hEnt points at the world entity, then this is the static prop index. + // Otherwise, this is the hitbox index. + int hitbox; // box hit by trace in studio + + CGameTrace() {} + +private: + // No copy constructors allowed + CGameTrace(const CGameTrace& vOther); +}; + + +//----------------------------------------------------------------------------- +// Returns true if there was any kind of impact at all +//----------------------------------------------------------------------------- +inline bool CGameTrace::DidHit() const +{ + return fraction < 1 || allsolid || startsolid; +} + + +typedef CGameTrace trace_t; + +//============================================================================= + +class ITraceListData +{ +public: + virtual ~ITraceListData() {} + + virtual void Reset() = 0; + virtual bool IsEmpty() = 0; + // CanTraceRay will return true if the current volume encloses the ray + // NOTE: The leaflist trace will NOT check this. Traces are intersected + // against the culled volume exclusively. + virtual bool CanTraceRay( const Ray_t &ray ) = 0; +}; +#endif // GAMETRACE_H + diff --git a/public/globalvars_base.h b/public/globalvars_base.h new file mode 100644 index 0000000..30f4542 --- /dev/null +++ b/public/globalvars_base.h @@ -0,0 +1,109 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef GLOBALVARS_BASE_H +#define GLOBALVARS_BASE_H + +#ifdef _WIN32 +#pragma once +#endif + +class CSaveRestoreData; + +//----------------------------------------------------------------------------- +// Purpose: Global variables used by shared code +//----------------------------------------------------------------------------- +class CGlobalVarsBase +{ +public: + + CGlobalVarsBase( bool bIsClient ); + + // This can be used to filter debug output or to catch the client or server in the act. + bool IsClient() const; + + // for encoding m_flSimulationTime, m_flAnimTime + int GetNetworkBase( int nTick, int nEntity ); + +public: + + // Absolute time (per frame still - Use Plat_FloatTime() for a high precision real time + // perf clock, but not that it doesn't obey host_timescale/host_framerate) + float realtime; + // Absolute frame counter - continues to increase even if game is paused + int framecount; + // Non-paused frametime + float absoluteframetime; + + // Current time + // + // On the client, this (along with tickcount) takes a different meaning based on what + // piece of code you're in: + // + // - While receiving network packets (like in PreDataUpdate/PostDataUpdate and proxies), + // this is set to the SERVER TICKCOUNT for that packet. There is no interval between + // the server ticks. + // [server_current_Tick * tick_interval] + // + // - While rendering, this is the exact client clock + // [client_current_tick * tick_interval + interpolation_amount] + // + // - During prediction, this is based on the client's current tick: + // [client_current_tick * tick_interval] + float curtime; + + // Time spent on last server or client frame (has nothing to do with think intervals) + float frametime; + // current maxplayers setting + int maxClients; + + // Simulation ticks - does not increase when game is paused + int tickcount; + + // Simulation tick interval + float interval_per_tick; + + // interpolation amount ( client-only ) based on fraction of next tick which has elapsed + float interpolation_amount; + int simTicksThisFrame; + + int network_protocol; + + // current saverestore data + CSaveRestoreData *pSaveData; + +private: + // Set to true in client code. + bool m_bClient; + + // 100 (i.e., tickcount is rounded down to this base and then the "delta" from this base is networked + int nTimestampNetworkingBase; + // 32 (entindex() % nTimestampRandomizeWindow ) is subtracted from gpGlobals->tickcount to set the networking basis, prevents + // all of the entities from forcing a new PackedEntity on the same tick (i.e., prevents them from getting lockstepped on this) + int nTimestampRandomizeWindow; + +}; + +inline int CGlobalVarsBase::GetNetworkBase( int nTick, int nEntity ) +{ + int nEntityMod = nEntity % nTimestampRandomizeWindow; + int nBaseTick = nTimestampNetworkingBase * (int)( ( nTick - nEntityMod ) / nTimestampNetworkingBase ); + return nBaseTick; +} + +inline CGlobalVarsBase::CGlobalVarsBase( bool bIsClient ) : + m_bClient( bIsClient ), + nTimestampNetworkingBase( 100 ), + nTimestampRandomizeWindow( 32 ) +{ +} + +inline bool CGlobalVarsBase::IsClient() const +{ + return m_bClient; +} + +#endif // GLOBALVARS_BASE_H diff --git a/public/iachievementmgr.h b/public/iachievementmgr.h new file mode 100644 index 0000000..5a2ae94 --- /dev/null +++ b/public/iachievementmgr.h @@ -0,0 +1,84 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IACHIEVEMENTMGR_H +#define IACHIEVEMENTMGR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlmap.h" + +class CBaseAchievement; + +abstract_class IAchievement +{ +public: + virtual int GetAchievementID() = 0; + virtual const char *GetName() = 0; + virtual int GetFlags() = 0; + virtual int GetGoal() = 0; + virtual int GetCount() = 0; + virtual bool IsAchieved() = 0; + virtual int GetPointValue() = 0; + virtual bool ShouldSaveWithGame() = 0; + virtual bool ShouldHideUntilAchieved() = 0; + virtual const char *GetIconPath() = 0; + virtual int GetDisplayOrder() = 0; +}; + + +abstract_class IAchievementMgr +{ +public: + virtual IAchievement* GetAchievementByIndex( int index, int nPlayerSlot ) = 0; + virtual IAchievement* GetAchievementByDisplayOrder( int orderIndex, int nPlayerSlot ) = 0; + virtual CBaseAchievement* GetAchievementByID ( int id, int nPlayerSlot ) = 0; + virtual int GetAchievementCount() = 0; + virtual void InitializeAchievements( ) = 0; + virtual void AwardAchievement( int nAchievementID, int nPlayerSlot ) = 0; + virtual void OnMapEvent( const char *pchEventName, int nPlayerSlot ) = 0; + virtual void SaveGlobalStateIfDirty( ) = 0; + virtual bool HasAchieved( const char *pchName, int nPlayerSlot ) = 0; + virtual const CUtlVector& GetAchievedDuringCurrentGame( int nPlayerSlot ) = 0; +}; + +// flags for IAchievement::GetFlags + +#define ACH_LISTEN_KILL_EVENTS 0x0001 +#define ACH_LISTEN_MAP_EVENTS 0x0002 +#define ACH_LISTEN_COMPONENT_EVENTS 0x0004 +#define ACH_HAS_COMPONENTS 0x0020 +#define ACH_SAVE_WITH_GAME 0x0040 +#define ACH_SAVE_GLOBAL 0x0080 +#define ACH_FILTER_ATTACKER_IS_PLAYER 0x0100 +#define ACH_FILTER_VICTIM_IS_PLAYER_ENEMY 0x0200 +#define ACH_FILTER_FULL_ROUND_ONLY 0x0400 +#define ACH_FILTER_LOCAL_PLAYER_EVENTS 0x0800 // Evaluate player-specific events only + +#define ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS ACH_LISTEN_KILL_EVENTS | ACH_FILTER_ATTACKER_IS_PLAYER | ACH_FILTER_VICTIM_IS_PLAYER_ENEMY +#define ACH_LISTEN_KILL_ENEMY_EVENTS ACH_LISTEN_KILL_EVENTS | ACH_FILTER_VICTIM_IS_PLAYER_ENEMY + +// Update this for changes in either abstract class in this file +#define ACHIEVEMENTMGR_INTERFACE_VERSION "ACHIEVEMENTMGR_INTERFACE_VERSION001" + +#define ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( name ) \ + ( g_pVGuiLocalize->Find( CFmtStr( "#%s_NAME", name ) ) ) + +#define ACHIEVEMENT_LOCALIZED_NAME( pAchievement ) \ + ( ACHIEVEMENT_LOCALIZED_NAME_FROM_STR( pAchievement->GetName() ) ) + +#define ACHIEVEMENT_LOCALIZED_DESC_FROM_STR( name ) \ + ( g_pVGuiLocalize->Find( CFmtStr( "#%s_DESC", name ) ) ) + +#define ACHIEVEMENT_LOCALIZED_DESC( pAchievement ) \ + ( ACHIEVEMENT_LOCALIZED_DESC_FROM_STR( pAchievement->GetName() ) ) + +#endif // IACHIEVEMENTMGR_H + +// Special slot designations +#define SINGLE_PLAYER_SLOT 0 +#define STEAM_PLAYER_SLOT 0 \ No newline at end of file diff --git a/public/ibsppack.h b/public/ibsppack.h new file mode 100644 index 0000000..233a8cc --- /dev/null +++ b/public/ibsppack.h @@ -0,0 +1,45 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IBSPPACK_H +#define IBSPPACK_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "utlvector.h" +#include "utlstring.h" + +class IFileSystem; + +abstract_class IBSPPack +{ +public: + virtual void LoadBSPFile( IFileSystem *pFileSystem, char *filename ) = 0; + virtual void WriteBSPFile( char *filename ) = 0; + virtual void ClearPackFile( void ) = 0; + virtual void AddFileToPack( const char *relativename, const char *fullpath ) = 0; + virtual void AddBufferToPack( const char *relativename, void *data, int length, bool bTextMode ) = 0; + virtual void SetHDRMode( bool bHDR ) = 0; + virtual bool SwapBSPFile( IFileSystem *pFileSystem, const char *filename, const char *swapFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc ) = 0; + + // used to get/set the pak file from a BSP + virtual bool GetPakFileLump( IFileSystem *pFileSystem, const char *pBSPFilename, void **pPakData, int *pPakSize ) = 0; + virtual bool SetPakFileLump( IFileSystem *pFileSystem, const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize ) = 0; + + // populates list of files that bsp owns, i.e. world/cubmap materials, statis props, etc + virtual bool GetBSPDependants( IFileSystem *pFileSystem, const char *pBSPFilename, CUtlVector< CUtlString > *pList ) = 0; + + // This is here to avoid having things like the runtime engine.dll have to statically link to LZMA for the encoder!!! + virtual void AddBufferToPackAndLZMACompress( const char *relativename, void *data, int length ) = 0; + virtual void RemoveFileFromPack( char const *relativename ) = 0; +}; + +#define IBSPPACK_VERSION_STRING "IBSPPACK003" + +#endif // IBSPPACK_H diff --git a/public/iclient.h b/public/iclient.h new file mode 100644 index 0000000..9f7f092 --- /dev/null +++ b/public/iclient.h @@ -0,0 +1,105 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ICLIENT_H +#define ICLIENT_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier0/platform.h" +#include "tier1/utlvector.h" + +class IServer; +class INetMessage; +struct NetMessageCvar_t; +struct USERID_t; + +abstract_class IClient : public INetChannelHandler +{ +public: + virtual ~IClient() {} + + // connect client + virtual void Connect( const char * szName, int nUserID, INetChannel *pNetChannel, bool bFakePlayer, CUtlVector< NetMessageCvar_t > *pVecCvars = NULL ) = 0; + + // set the client in a pending state waiting for a new game + virtual void Inactivate( void ) = 0; + + // Reconnect without dropiing the netchannel + virtual void Reconnect( void ) = 0; // froce reconnect + + // disconnects a client with a given reason + virtual void Disconnect( const char *reason, ... ) = 0; + + virtual int GetPlayerSlot() const = 0; // returns client slot (usually entity number-1) + virtual int GetUserID() const = 0; // unique ID on this server + virtual const USERID_t GetNetworkID() const = 0; // network wide ID + virtual const char *GetClientName() const = 0; // returns client name + virtual INetChannel *GetNetChannel() = 0; // returns client netchannel + virtual IServer *GetServer() = 0; // returns the object server the client belongs to + virtual const char *GetUserSetting(const char *cvar) const = 0; // returns a clients FCVAR_USERINFO setting + virtual const char *GetNetworkIDString() const = 0; // returns a human readable representation of the network id + + // set/get client data rate in bytes/second + virtual void SetRate( int nRate, bool bForce ) = 0; + virtual int GetRate( void ) const = 0; + + // set/get updates/second rate + virtual void SetUpdateRate( int nUpdateRate, bool bForce ) = 0; + virtual int GetUpdateRate( void ) const = 0; + + // clear complete object & free all memory + virtual void Clear( void ) = 0; + + // returns the highest world tick number acknowledge by client + virtual int GetMaxAckTickCount() const = 0; + + // execute a client command + virtual bool ExecuteStringCommand( const char *s ) = 0; + // send client a network message + virtual bool SendNetMsg(INetMessage &msg, bool bForceReliable = false, bool bVoice = false ) = 0; + // send client a text message + virtual void ClientPrintf (const char *fmt, ...) = 0; + + // client has established network channels, nothing else + virtual bool IsConnected( void ) const = 0; + // client is downloading signon data + virtual bool IsSpawned( void ) const = 0; + // client active is ingame, receiving snapshots + virtual bool IsActive( void ) const = 0; + // returns true, if client is not a real player + virtual bool IsFakeClient( void ) const = 0; + // returns true, if client is a HLTV proxy + virtual bool IsHLTV( void ) const = 0; + // returns true, if client is a Replay proxy + virtual bool IsReplay( void ) const = 0; + // returns true, if client hears this player + virtual bool IsHearingClient(int index) const = 0; + // returns true, if client hears this player by proximity + virtual bool IsProximityHearingClient(int index) const = 0; + + virtual void SetMaxRoutablePayloadSize( int nMaxRoutablePayloadSize ) = 0; + + // returns true, if client is a split screen user + virtual bool IsSplitScreenUser( void ) const = 0; + + virtual bool CheckConnect( void ) = 0; + + virtual bool IsLowViolenceClient( void ) const = 0; + + virtual IClient *GetSplitScreenOwner() = 0; + + // get the number of players on this client's machine + virtual int GetNumPlayers() = 0; + + virtual bool IsHumanPlayer() const = 0; + + virtual int GetClientPlatform() const = 0; +}; + +#endif // ICLIENT_H diff --git a/public/iclientalphaproperty.h b/public/iclientalphaproperty.h new file mode 100644 index 0000000..d9b01f5 --- /dev/null +++ b/public/iclientalphaproperty.h @@ -0,0 +1,89 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ICLIENTALPHAPROPERTY_H +#define ICLIENTALPHAPROPERTY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "iclientunknown.h" +#include "appframework/IAppSystem.h" +#include "const.h" + + +enum ClientAlphaDistanceFadeMode_t +{ + CLIENT_ALPHA_DISTANCE_FADE_USE_CENTER = 0, + CLIENT_ALPHA_DISTANCE_FADE_USE_NEAREST_BBOX, + + CLIENT_ALPHA_DISTANCE_FADE_MODE_COUNT, +}; + + + +//----------------------------------------------------------------------------- +// NOTE: Clients are *not* expected to implement this interface +// Instead, these are managed completely by the client DLL. +// Use the IClientTranslucency manager to allocate + free IClientTranslucency objects +//----------------------------------------------------------------------------- +abstract_class IClientAlphaProperty +{ +public: + // Gets at the containing class... + virtual IClientUnknown* GetIClientUnknown() = 0; + + // Sets a constant alpha modulation value + virtual void SetAlphaModulation( uint8 a ) = 0; + + // Sets an FX function + // NOTE: kRenderFxFadeSlow, kRenderFxFadeFast, kRenderFxSolidSlow, kRenderFxSolidFast all need a start time only. + // kRenderFxFadeIn/kRenderFxFadeOut needs start time + duration + // All other render fx require no parameters + virtual void SetRenderFX( RenderFx_t nRenderFx, RenderMode_t nRenderMode, float flStartTime = FLT_MAX, float flDuration = 0.0f ) = 0; + + // Sets fade parameters + virtual void SetFade( float flGlobalFadeScale, float flDistFadeStart, float flDistFadeEnd ) = 0; + + // Sets desync offset, used to make sine waves not match + virtual void SetDesyncOffset( int nOffset ) = 0; + + // Allows the owner to override alpha. + // The method IClientRenderable::OverrideAlphaModulation will be called + // to allow the owner to optionally return a different alpha modulation + virtual void EnableAlphaModulationOverride( bool bEnable ) = 0; + + // Allows the owner to override projected shadow alpha. + // The method IClientRenderable::OverrideShadowAlphaModulation will be called + // to allow the owner to optionally return a different alpha modulation for the shadow + virtual void EnableShadowAlphaModulationOverride( bool bEnable ) = 0; + + // Sets the distance fade mode + virtual void SetDistanceFadeMode( ClientAlphaDistanceFadeMode_t nFadeMode ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Manager used to deal with client translucency +//----------------------------------------------------------------------------- +#define CLIENT_ALPHA_PROPERTY_MGR_INTERFACE_VERSION "ClientAlphaPropertyMgrV001" + +abstract_class IClientAlphaPropertyMgr +{ +public: + // Class factory + virtual IClientAlphaProperty *CreateClientAlphaProperty( IClientUnknown *pUnk ) = 0; + virtual void DestroyClientAlphaProperty( IClientAlphaProperty *pAlphaProperty ) = 0; +}; + +#ifndef SWDS +extern IClientAlphaPropertyMgr *g_pClientAlphaPropertyMgr; +#endif + + +#endif // ICLIENTALPHAPROPERTY_H diff --git a/public/icliententity.h b/public/icliententity.h new file mode 100644 index 0000000..10c618f --- /dev/null +++ b/public/icliententity.h @@ -0,0 +1,50 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICLIENTENTITY_H +#define ICLIENTENTITY_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iclientrenderable.h" +#include "iclientnetworkable.h" +#include "iclientthinkable.h" + +struct Ray_t; +class CGameTrace; +typedef CGameTrace trace_t; +class CMouthInfo; +class IClientEntityInternal; +struct SpatializationInfo_t; + + +//----------------------------------------------------------------------------- +// Purpose: All client entities must implement this interface. +//----------------------------------------------------------------------------- +abstract_class IClientEntity : public IClientUnknown, public IClientRenderable, public IClientNetworkable, public IClientThinkable +{ +public: + // Delete yourself. + virtual void Release( void ) = 0; + + // Network origin + angles + virtual const Vector& GetAbsOrigin( void ) const = 0; + virtual const QAngle& GetAbsAngles( void ) const = 0; + + virtual CMouthInfo *GetMouth( void ) = 0; + + // Retrieve sound spatialization info for the specified sound on this entity + // Return false to indicate sound is not audible + virtual bool GetSoundSpatialization( SpatializationInfo_t& info ) = 0; + + virtual bool IsBlurred( void ) = 0; +}; + + +#endif // ICLIENTENTITY_H diff --git a/public/icliententitylist.h b/public/icliententitylist.h new file mode 100644 index 0000000..a928130 --- /dev/null +++ b/public/icliententitylist.h @@ -0,0 +1,67 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( ICLIENTENTITYLIST_H ) +#define ICLIENTENTITYLIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +class IClientEntity; +class ClientClass; +class IClientNetworkable; +class CBaseHandle; +class IClientUnknown; + +// Cached info for networked entities. +// NOTE: Changing this changes the interface between engine & client +struct EntityCacheInfo_t +{ + // Cached off because GetClientNetworkable is called a *lot* + IClientNetworkable *m_pNetworkable; + unsigned short m_BaseEntitiesIndex; // Index into m_BaseEntities (or m_BaseEntities.InvalidIndex() if none). + unsigned short m_bDormant; // cached dormant state - this is only a bit +}; + +//----------------------------------------------------------------------------- +// Purpose: Exposes IClientEntity's to engine +//----------------------------------------------------------------------------- +abstract_class IClientEntityList +{ +public: + // Get IClientNetworkable interface for specified entity + virtual IClientNetworkable* GetClientNetworkable( int entnum ) = 0; + virtual IClientNetworkable* GetClientNetworkableFromHandle( CBaseHandle hEnt ) = 0; + virtual IClientUnknown* GetClientUnknownFromHandle( CBaseHandle hEnt ) = 0; + + // NOTE: This function is only a convenience wrapper. + // It returns GetClientNetworkable( entnum )->GetIClientEntity(). + virtual IClientEntity* GetClientEntity( int entnum ) = 0; + virtual IClientEntity* GetClientEntityFromHandle( CBaseHandle hEnt ) = 0; + + // Returns number of entities currently in use + virtual int NumberOfEntities( bool bIncludeNonNetworkable ) = 0; + + // Returns highest index actually used + virtual int GetHighestEntityIndex( void ) = 0; + + // Sizes entity list to specified size + virtual void SetMaxEntities( int maxents ) = 0; + virtual int GetMaxEntities( ) = 0; + virtual EntityCacheInfo_t *GetClientNetworkableArray() = 0; +}; + +extern IClientEntityList *entitylist; + +#define VCLIENTENTITYLIST_INTERFACE_VERSION "VClientEntityList003" + +#endif // ICLIENTENTITYLIST_H + diff --git a/public/iclientnetworkable.h b/public/iclientnetworkable.h new file mode 100644 index 0000000..be8fac9 --- /dev/null +++ b/public/iclientnetworkable.h @@ -0,0 +1,105 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICLIENTNETWORKABLE_H +#define ICLIENTNETWORKABLE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iclientunknown.h" +#include "tier1/bitbuf.h" + + +class IClientEntity; +class ClientClass; + + +enum ShouldTransmitState_t +{ + SHOULDTRANSMIT_START=0, // The entity is starting to be transmitted (maybe it entered the PVS). + + SHOULDTRANSMIT_END // Called when the entity isn't being transmitted by the server. + // This signals a good time to hide the entity until next time + // the server wants to transmit its state. +}; + +// NOTE: All of these are commented out; NotifyShouldTransmit actually +// has all these in them. Left it as an enum in case we want to go back though +enum DataUpdateType_t +{ + DATA_UPDATE_CREATED = 0, // indicates it was created +and+ entered the pvs +// DATA_UPDATE_ENTERED_PVS, + DATA_UPDATE_DATATABLE_CHANGED, +// DATA_UPDATE_LEFT_PVS, +// DATA_UPDATE_DESTROYED, // FIXME: Could enable this, but it's a little worrying + // since it changes a bunch of existing code +}; + +abstract_class IClientNetworkable +{ +public: + // Gets at the containing class... + virtual IClientUnknown* GetIClientUnknown() = 0; + + // Called by the engine when the server deletes the entity. + virtual void Release() = 0; + + // Supplied automatically by the IMPLEMENT_CLIENTCLASS macros. + virtual ClientClass* GetClientClass() = 0; + + // This tells the entity what the server says for ShouldTransmit on this entity. + // Note: This used to be EntityEnteredPVS/EntityRemainedInPVS/EntityLeftPVS. + virtual void NotifyShouldTransmit( ShouldTransmitState_t state ) = 0; + + + + // + // NOTE FOR ENTITY WRITERS: + // + // In 90% of the cases, you should hook OnPreDataChanged/OnDataChanged instead of + // PreDataUpdate/PostDataUpdate. + // + // The DataChanged events are only called once per frame whereas Pre/PostDataUpdate + // are called once per packet (and sometimes multiple times per frame). + // + // OnDataChanged is called during simulation where entity origins are correct and + // attachments can be used. whereas PostDataUpdate is called while parsing packets + // so attachments and other entity origins may not be valid yet. + // + + virtual void OnPreDataChanged( DataUpdateType_t updateType ) = 0; + virtual void OnDataChanged( DataUpdateType_t updateType ) = 0; + + // Called when data is being updated across the network. + // Only low-level entities should need to know about these. + virtual void PreDataUpdate( DataUpdateType_t updateType ) = 0; + virtual void PostDataUpdate( DataUpdateType_t updateType ) = 0; + + + // Objects become dormant on the client if they leave the PVS on the server. + virtual bool IsDormant( void ) = 0; + + // Ent Index is the server handle used to reference this entity. + // If the index is < 0, that indicates the entity is not known to the server + virtual int entindex( void ) const = 0; + + // Server to client entity message received + virtual void ReceiveMessage( int classID, bf_read &msg ) = 0; + + // Get the base pointer to the networked data that GetClientClass->m_pRecvTable starts at. + // (This is usually just the "this" pointer). + virtual void* GetDataTableBasePtr() = 0; + + // Tells the entity that it's about to be destroyed due to the client receiving + // an uncompressed update that's caused it to destroy all entities & recreate them. + virtual void SetDestroyedOnRecreateEntities( void ) = 0; +}; + + +#endif // ICLIENTNETWORKABLE_H diff --git a/public/iclientrenderable.h b/public/iclientrenderable.h new file mode 100644 index 0000000..8560a31 --- /dev/null +++ b/public/iclientrenderable.h @@ -0,0 +1,329 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ICLIENTRENDERABLE_H +#define ICLIENTRENDERABLE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/mathlib.h" +#include "interface.h" +#include "iclientunknown.h" +#include "client_render_handle.h" +#include "engine/ivmodelrender.h" + +struct model_t; +struct matrix3x4_t; + +extern void DefaultRenderBoundsWorldspace( IClientRenderable *pRenderable, Vector &absMins, Vector &absMaxs ); + +//----------------------------------------------------------------------------- +// Handles to a client shadow +//----------------------------------------------------------------------------- +typedef unsigned short ClientShadowHandle_t; + +enum +{ + CLIENTSHADOW_INVALID_HANDLE = (ClientShadowHandle_t)~0 +}; + +//----------------------------------------------------------------------------- +// What kind of shadows to render? +//----------------------------------------------------------------------------- +enum ShadowType_t +{ + SHADOWS_NONE = 0, + SHADOWS_SIMPLE, + SHADOWS_RENDER_TO_TEXTURE, + SHADOWS_RENDER_TO_TEXTURE_DYNAMIC, // the shadow is always changing state + SHADOWS_RENDER_TO_DEPTH_TEXTURE, + SHADOWS_RENDER_TO_TEXTURE_DYNAMIC_CUSTOM, // changing, and entity uses custom rendering code for shadow +}; + + +// This provides a way for entities to know when they've entered or left the PVS. +// Normally, server entities can use NotifyShouldTransmit to get this info, but client-only +// entities can use this. Store a CPVSNotifyInfo in your +// +// When bInPVS=true, it's being called DURING rendering. It might be after rendering any +// number of views. +// +// If no views had the entity, then it is called with bInPVS=false after rendering. +abstract_class IPVSNotify +{ +public: + virtual void OnPVSStatusChanged( bool bInPVS ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Information needed to draw a model +//----------------------------------------------------------------------------- +struct RenderableInstance_t +{ + uint8 m_nAlpha; +}; + + +// client renderable frame buffer usage flags +#define ERENDERFLAGS_NEEDS_POWER_OF_TWO_FB 1 // needs refract texture +#define ERENDERFLAGS_NEEDS_FULL_FB 2 // needs full framebuffer texture +#define ERENDERFLAGS_REFRACT_ONLY_ONCE_PER_FRAME 4 // even if it needs a the refract texture, don't update it >once/ frame + +//----------------------------------------------------------------------------- +// Purpose: All client entities must implement this interface. +//----------------------------------------------------------------------------- +abstract_class IClientRenderable +{ +public: + // Gets at the containing class... + virtual IClientUnknown* GetIClientUnknown() = 0; + + // Data accessors + virtual Vector const& GetRenderOrigin( void ) = 0; + virtual QAngle const& GetRenderAngles( void ) = 0; + virtual bool ShouldDraw( void ) = 0; + virtual int GetRenderFlags( void ) = 0; // ERENDERFLAGS_xxx + virtual void Unused( void ) const {} + + virtual ClientShadowHandle_t GetShadowHandle() const = 0; + + // Used by the leaf system to store its render handle. + virtual ClientRenderHandle_t& RenderHandle() = 0; + + // Render baby! + virtual const model_t* GetModel( ) const = 0; + virtual int DrawModel( int flags, const RenderableInstance_t &instance ) = 0; + + // Get the body parameter + virtual int GetBody() = 0; + + // Determine the color modulation amount + virtual void GetColorModulation( float* color ) = 0; + + // Returns false if the entity shouldn't be drawn due to LOD. + // (NOTE: This is no longer used/supported, but kept in the vtable for backwards compat) + virtual bool LODTest() = 0; + + // Call this to get the current bone transforms for the model. + // currentTime parameter will affect interpolation + // nMaxBones specifies how many matrices pBoneToWorldOut can hold. (Should be greater than or + // equal to studiohdr_t::numbones. Use MAXSTUDIOBONES to be safe.) + virtual bool SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime ) = 0; + + virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) = 0; + virtual void DoAnimationEvents( void ) = 0; + + // Return this if you want PVS notifications. See IPVSNotify for more info. + // Note: you must always return the same value from this function. If you don't, + // undefined things will occur, and they won't be good. + virtual IPVSNotify* GetPVSNotifyInterface() = 0; + + // Returns the bounds relative to the origin (render bounds) + virtual void GetRenderBounds( Vector& mins, Vector& maxs ) = 0; + + // returns the bounds as an AABB in worldspace + virtual void GetRenderBoundsWorldspace( Vector& mins, Vector& maxs ) = 0; + + // These normally call through to GetRenderAngles/GetRenderBounds, but some entities custom implement them. + virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType ) = 0; + + // Should this object be able to have shadows cast onto it? + virtual bool ShouldReceiveProjectedTextures( int flags ) = 0; + + // These methods return true if we want a per-renderable shadow cast direction + distance + virtual bool GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const = 0; + virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const = 0; + + // Other methods related to shadow rendering + virtual bool IsShadowDirty( ) = 0; + virtual void MarkShadowDirty( bool bDirty ) = 0; + + // Iteration over shadow hierarchy + virtual IClientRenderable *GetShadowParent() = 0; + virtual IClientRenderable *FirstShadowChild() = 0; + virtual IClientRenderable *NextShadowPeer() = 0; + + // Returns the shadow cast type + virtual ShadowType_t ShadowCastType() = 0; + + // Create/get/destroy model instance + virtual void CreateModelInstance() = 0; + virtual ModelInstanceHandle_t GetModelInstance() = 0; + + // Returns the transform from RenderOrigin/RenderAngles to world + virtual const matrix3x4_t &RenderableToWorldTransform() = 0; + + // Attachments + virtual int LookupAttachment( const char *pAttachmentName ) = 0; + virtual bool GetAttachment( int number, Vector &origin, QAngle &angles ) = 0; + virtual bool GetAttachment( int number, matrix3x4_t &matrix ) = 0; + + // Rendering clip plane, should be 4 floats, return value of NULL indicates a disabled render clip plane + virtual float *GetRenderClipPlane( void ) = 0; + + // Get the skin parameter + virtual int GetSkin() = 0; + + virtual void OnThreadedDrawSetup() = 0; + + virtual bool UsesFlexDelayedWeights() = 0; + + virtual void RecordToolMessage() = 0; + virtual bool ShouldDrawForSplitScreenUser( int nSlot ) = 0; + + // NOTE: This is used by renderables to override the default alpha modulation, + // not including fades, for a renderable. The alpha passed to the function + // is the alpha computed based on the current renderfx. + virtual uint8 OverrideAlphaModulation( uint8 nAlpha ) = 0; + + // NOTE: This is used by renderables to override the default alpha modulation, + // not including fades, for a renderable's shadow. The alpha passed to the function + // is the alpha computed based on the current renderfx + any override + // computed in OverrideAlphaModulation + virtual uint8 OverrideShadowAlphaModulation( uint8 nAlpha ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: All client renderables supporting the fast-path mdl +// rendering algorithm must inherit from this interface +//----------------------------------------------------------------------------- +enum RenderableLightingModel_t +{ + LIGHTING_MODEL_NONE = -1, + LIGHTING_MODEL_STANDARD = 0, + LIGHTING_MODEL_STATIC_PROP, + LIGHTING_MODEL_PHYSICS_PROP, + + LIGHTING_MODEL_COUNT, +}; + +enum ModelDataCategory_t +{ + MODEL_DATA_LIGHTING_MODEL, // data type returned is a RenderableLightingModel_t + MODEL_DATA_STENCIL, // data type returned is a ShaderStencilState_t + + MODEL_DATA_CATEGORY_COUNT, +}; + + +abstract_class IClientModelRenderable +{ +public: + virtual bool GetRenderData( void *pData, ModelDataCategory_t nCategory ) = 0; +}; + + +// This class can be used to implement default versions of some of the +// functions of IClientRenderable. +abstract_class CDefaultClientRenderable : public IClientUnknown, public IClientRenderable +{ +public: + CDefaultClientRenderable() + { + m_hRenderHandle = INVALID_CLIENT_RENDER_HANDLE; + } + + virtual const Vector & GetRenderOrigin( void ) = 0; + virtual const QAngle & GetRenderAngles( void ) = 0; + virtual const matrix3x4_t & RenderableToWorldTransform() = 0; + virtual bool ShouldDraw( void ) = 0; + virtual void OnThreadedDrawSetup() {} + virtual int GetRenderFlags( void ) { return 0; } + + virtual ClientShadowHandle_t GetShadowHandle() const + { + return CLIENTSHADOW_INVALID_HANDLE; + } + + virtual ClientRenderHandle_t& RenderHandle() + { + return m_hRenderHandle; + } + + virtual int GetBody() { return 0; } + virtual int GetSkin() { return 0; } + virtual bool UsesFlexDelayedWeights() { return false; } + + virtual const model_t* GetModel( ) const { return NULL; } + virtual int DrawModel( int flags, const RenderableInstance_t &instance ) { return 0; } + virtual bool LODTest() { return true; } + virtual bool SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime ) { return true; } + virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) {} + virtual void DoAnimationEvents( void ) {} + virtual IPVSNotify* GetPVSNotifyInterface() { return NULL; } + virtual void GetRenderBoundsWorldspace( Vector& absMins, Vector& absMaxs ) { DefaultRenderBoundsWorldspace( this, absMins, absMaxs ); } + + // Determine the color modulation amount + virtual void GetColorModulation( float* color ) + { + Assert(color); + color[0] = color[1] = color[2] = 1.0f; + } + + // Should this object be able to have shadows cast onto it? + virtual bool ShouldReceiveProjectedTextures( int flags ) + { + return false; + } + + // These methods return true if we want a per-renderable shadow cast direction + distance + virtual bool GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const { return false; } + virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const { return false; } + + virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType ) + { + GetRenderBounds( mins, maxs ); + } + + virtual bool IsShadowDirty( ) { return false; } + virtual void MarkShadowDirty( bool bDirty ) {} + virtual IClientRenderable *GetShadowParent() { return NULL; } + virtual IClientRenderable *FirstShadowChild(){ return NULL; } + virtual IClientRenderable *NextShadowPeer() { return NULL; } + virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; } + + virtual void CreateModelInstance() {} + virtual ModelInstanceHandle_t GetModelInstance() { return MODEL_INSTANCE_INVALID; } + + // Attachments + virtual int LookupAttachment( const char *pAttachmentName ) { return -1; } + virtual bool GetAttachment( int number, Vector &origin, QAngle &angles ) { return false; } + virtual bool GetAttachment( int number, matrix3x4_t &matrix ) { return false; } + + // Rendering clip plane, should be 4 floats, return value of NULL indicates a disabled render clip plane + virtual float *GetRenderClipPlane() { return NULL; } + + virtual void RecordToolMessage() {} + + virtual bool ShouldDrawForSplitScreenUser( int nSlot ) { return true; } + virtual uint8 OverrideAlphaModulation( uint8 nAlpha ) { return nAlpha; } + virtual uint8 OverrideShadowAlphaModulation( uint8 nAlpha ) { return nAlpha; } + +// IClientUnknown implementation. +public: + virtual void SetRefEHandle( const CBaseHandle &handle ) { Assert( false ); } + virtual const CBaseHandle& GetRefEHandle() const { Assert( false ); return *((CBaseHandle*)0); } + + virtual IClientUnknown* GetIClientUnknown() { return this; } + virtual ICollideable* GetCollideable() { return 0; } + virtual IClientRenderable* GetClientRenderable() { return this; } + virtual IClientNetworkable* GetClientNetworkable() { return 0; } + virtual IClientEntity* GetIClientEntity() { return 0; } + virtual C_BaseEntity* GetBaseEntity() { return 0; } + virtual IClientThinkable* GetClientThinkable() { return 0; } + virtual IClientModelRenderable* GetClientModelRenderable() { return 0; } + virtual IClientAlphaProperty* GetClientAlphaProperty() { return 0; } + +public: + ClientRenderHandle_t m_hRenderHandle; +}; + + +#endif // ICLIENTRENDERABLE_H diff --git a/public/iclientthinkable.h b/public/iclientthinkable.h new file mode 100644 index 0000000..6a3f59a --- /dev/null +++ b/public/iclientthinkable.h @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICLIENTTHINKABLE_H +#define ICLIENTTHINKABLE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iclientunknown.h" + + +class CClientThinkHandlePtr; +typedef CClientThinkHandlePtr* ClientThinkHandle_t; + + +// Entities that implement this interface can be put into the client think list. +abstract_class IClientThinkable +{ +public: + // Gets at the containing class... + virtual IClientUnknown* GetIClientUnknown() = 0; + + virtual void ClientThink() = 0; + + // Called when you're added to the think list. + // GetThinkHandle's return value must be initialized to INVALID_THINK_HANDLE. + virtual ClientThinkHandle_t GetThinkHandle() = 0; + virtual void SetThinkHandle( ClientThinkHandle_t hThink ) = 0; + + // Called by the client when it deletes the entity. + virtual void Release() = 0; +}; + + +#endif // ICLIENTTHINKABLE_H diff --git a/public/iclientunknown.h b/public/iclientunknown.h new file mode 100644 index 0000000..eea33b2 --- /dev/null +++ b/public/iclientunknown.h @@ -0,0 +1,45 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ICLIENTUNKNOWN_H +#define ICLIENTUNKNOWN_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/platform.h" +#include "ihandleentity.h" + +class IClientNetworkable; +class C_BaseEntity; +class IClientRenderable; +class ICollideable; +class IClientEntity; +class IClientThinkable; +class IClientModelRenderable; +class IClientAlphaProperty; + + + +// This is the client's version of IUnknown. We may want to use a QueryInterface-like +// mechanism if this gets big. +abstract_class IClientUnknown : public IHandleEntity +{ +public: + virtual ICollideable* GetCollideable() = 0; + virtual IClientNetworkable* GetClientNetworkable() = 0; + virtual IClientRenderable* GetClientRenderable() = 0; + virtual IClientEntity* GetIClientEntity() = 0; + virtual C_BaseEntity* GetBaseEntity() = 0; + virtual IClientThinkable* GetClientThinkable() = 0; + virtual IClientModelRenderable* GetClientModelRenderable() = 0; + virtual IClientAlphaProperty* GetClientAlphaProperty() = 0; +}; + + +#endif // ICLIENTUNKNOWN_H diff --git a/public/icvar.h b/public/icvar.h new file mode 100644 index 0000000..bbf2842 --- /dev/null +++ b/public/icvar.h @@ -0,0 +1,213 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef ICVAR_H +#define ICVAR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" +#include "tier1/iconvar.h" +#include "tier1/utlvector.h" + + +class ConCommandBase; +class ConCommand; +class ConVar; +class Color; + + +//----------------------------------------------------------------------------- +// ConVars/ComCommands are marked as having a particular DLL identifier +//----------------------------------------------------------------------------- +typedef int CVarDLLIdentifier_t; + + +//----------------------------------------------------------------------------- +// Used to display console messages +//----------------------------------------------------------------------------- +abstract_class IConsoleDisplayFunc +{ +public: + virtual void ColorPrint( const Color& clr, const char *pMessage ) = 0; + virtual void Print( const char *pMessage ) = 0; + virtual void DPrint( const char *pMessage ) = 0; + + virtual void GetConsoleText( char *pchText, size_t bufSize ) const = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Applications can implement this to modify behavior in ICvar +//----------------------------------------------------------------------------- +#define CVAR_QUERY_INTERFACE_VERSION "VCvarQuery001" +abstract_class ICvarQuery : public IAppSystem +{ +public: + // Can these two convars be aliased? + virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: DLL interface to ConVars/ConCommands +//----------------------------------------------------------------------------- +abstract_class ICvar : public IAppSystem +{ +public: + // Allocate a unique DLL identifier + virtual CVarDLLIdentifier_t AllocateDLLIdentifier() = 0; + + // Register, unregister commands + virtual void RegisterConCommand( ConCommandBase *pCommandBase ) = 0; + virtual void UnregisterConCommand( ConCommandBase *pCommandBase ) = 0; + virtual void UnregisterConCommands( CVarDLLIdentifier_t id ) = 0; + + // If there is a + on the command line, this returns the value. + // Otherwise, it returns NULL. + virtual const char* GetCommandLineValue( const char *pVariableName ) = 0; + + // Try to find the cvar pointer by name + virtual ConCommandBase *FindCommandBase( const char *name ) = 0; + virtual const ConCommandBase *FindCommandBase( const char *name ) const = 0; + virtual ConVar *FindVar ( const char *var_name ) = 0; + virtual const ConVar *FindVar ( const char *var_name ) const = 0; + virtual ConCommand *FindCommand( const char *name ) = 0; + virtual const ConCommand *FindCommand( const char *name ) const = 0; + + + + // Install a global change callback (to be called when any convar changes) + virtual void InstallGlobalChangeCallback( FnChangeCallback_t callback ) = 0; + virtual void RemoveGlobalChangeCallback( FnChangeCallback_t callback ) = 0; + virtual void CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue ) = 0; + + // Install a console printer + virtual void InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) = 0; + virtual void RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc ) = 0; + virtual void ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const = 0; + virtual void ConsolePrintf( const char *pFormat, ... ) const = 0; + virtual void ConsoleDPrintf( const char *pFormat, ... ) const = 0; + + // Reverts cvars which contain a specific flag + virtual void RevertFlaggedConVars( int nFlag ) = 0; + + // Method allowing the engine ICvarQuery interface to take over + // A little hacky, owing to the fact the engine is loaded + // well after ICVar, so we can't use the standard connect pattern + virtual void InstallCVarQuery( ICvarQuery *pQuery ) = 0; + +#if defined( _X360 ) + virtual void PublishToVXConsole( ) = 0; +#endif + + virtual void SetMaxSplitScreenSlots( int nSlots ) = 0; + virtual int GetMaxSplitScreenSlots() const = 0; + + virtual void AddSplitScreenConVars() = 0; + virtual void RemoveSplitScreenConVars( CVarDLLIdentifier_t id ) = 0; + + virtual int GetConsoleDisplayFuncCount() const = 0; + virtual void GetConsoleText( int nDisplayFuncIndex, char *pchText, size_t bufSize ) const = 0; + + // Utilities for convars accessed by the material system thread + virtual bool IsMaterialThreadSetAllowed( ) const = 0; + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue ) = 0; + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, int nValue ) = 0; + virtual void QueueMaterialThreadSetValue( ConVar *pConVar, float flValue ) = 0; + virtual bool HasQueuedMaterialThreadConVarSets() const = 0; + virtual int ProcessQueuedMaterialThreadConVarSets() = 0; + +protected: class ICVarIteratorInternal; +public: + /// Iteration over all cvars. + /// (THIS IS A SLOW OPERATION AND YOU SHOULD AVOID IT.) + /// usage: + /// { ICVar::Iterator iter(g_pCVar); + /// for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) + /// { + /// ConCommandBase *cmd = iter.Get(); + /// } + /// } + /// The Iterator class actually wraps the internal factory methods + /// so you don't need to worry about new/delete -- scope takes care + // of it. + /// We need an iterator like this because we can't simply return a + /// pointer to the internal data type that contains the cvars -- + /// it's a custom, protected class with unusual semantics and is + /// prone to change. + class Iterator + { + public: + inline Iterator(ICvar *icvar); + inline ~Iterator(void); + inline void SetFirst( void ); + inline void Next( void ); + inline bool IsValid( void ); + inline ConCommandBase *Get( void ); + private: + ICVarIteratorInternal *m_pIter; + }; + +protected: + // internals for ICVarIterator + class ICVarIteratorInternal + { + public: + // warning: delete called on 'ICvar::ICVarIteratorInternal' that is abstract but has non-virtual destructor [-Wdelete-non-virtual-dtor] + virtual ~ICVarIteratorInternal() {} // This destructor exists in newer P2 builds and is why crashes occur + virtual void SetFirst( void ) = 0; + virtual void Next( void ) = 0; + virtual bool IsValid( void ) = 0; + virtual ConCommandBase *Get( void ) = 0; + }; + + virtual ICVarIteratorInternal *FactoryInternalIterator( void ) = 0; + friend class Iterator; +}; + +inline ICvar::Iterator::Iterator(ICvar *icvar) +{ + m_pIter = icvar->FactoryInternalIterator(); +} + +inline ICvar::Iterator::~Iterator( void ) +{ + g_pMemAlloc->Free(m_pIter); +} + +inline void ICvar::Iterator::SetFirst( void ) +{ + m_pIter->SetFirst(); +} + +inline void ICvar::Iterator::Next( void ) +{ + m_pIter->Next(); +} + +inline bool ICvar::Iterator::IsValid( void ) +{ + return m_pIter->IsValid(); +} + +inline ConCommandBase * ICvar::Iterator::Get( void ) +{ + return m_pIter->Get(); +} + + +//----------------------------------------------------------------------------- +// These global names are defined by tier1.h, duplicated here so you +// don't have to include tier1.h +//----------------------------------------------------------------------------- + +// These are marked DLL_EXPORT for Linux. +DECLARE_TIER1_INTERFACE( ICvar, cvar ); +DECLARE_TIER1_INTERFACE( ICvar, g_pCVar ); + + +#endif // ICVAR_H diff --git a/public/idedicatedexports.h b/public/idedicatedexports.h new file mode 100644 index 0000000..208adc5 --- /dev/null +++ b/public/idedicatedexports.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IDEDICATEDEXPORTS_H +#define IDEDICATEDEXPORTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "appframework/IAppSystem.h" + + +abstract_class IDedicatedExports : public IAppSystem +{ +public: + virtual void Sys_Printf( char *text ) = 0; + virtual void RunServer() = 0; + virtual bool IsGuiDedicatedServer() = 0; +}; + +#define VENGINE_DEDICATEDEXPORTS_API_VERSION "VENGINE_DEDICATEDEXPORTS_API_VERSION003" + + +#endif // IDEDICATEDEXPORTS_H diff --git a/public/iefx.h b/public/iefx.h new file mode 100644 index 0000000..aaafb91 --- /dev/null +++ b/public/iefx.h @@ -0,0 +1,68 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#if !defined( IEFX_H ) +#define IEFX_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "mathlib/vector.h" + +struct model_t; +struct dlight_t; +class IMaterial; + +#define MAX_DLIGHTS 32 + +//----------------------------------------------------------------------------- +// Purpose: Exposes effects api to client .dll +//----------------------------------------------------------------------------- +abstract_class IVEfx +{ +public: + // Retrieve decal texture index from decal by name + virtual int Draw_DecalIndexFromName ( char *name ) = 0; + + // Apply decal. See decal_private.h for flags (FDECAL_*) + // If normal is specified, don't decal surfaces antiparallel with normal + virtual void DecalShoot ( int textureIndex, int entity, + const model_t *model, const Vector& model_origin, const QAngle& model_angles, + const Vector& position, const Vector *saxis, int flags, const Vector *pNormal = NULL ) = 0; + + // Apply colored decal. See decal_private.h for flags (FDECAL_*) + virtual void DecalColorShoot ( int textureIndex, int entity, + const model_t *model, const Vector& model_origin, const QAngle& model_angles, + const Vector& position, const Vector *saxis, int flags, const color32 &rgbaColor, const Vector *pNormal = NULL ) = 0; + + virtual void PlayerDecalShoot( IMaterial *material, void *userdata, int entity, const model_t *model, + const Vector& model_origin, const QAngle& model_angles, + const Vector& position, const Vector *saxis, int flags, const color32 &rgbaColor ) = 0; + + // Allocate a dynamic world light ( key is the entity to whom it is associated ) + virtual dlight_t *CL_AllocDlight ( int key ) = 0; + + // Allocate a dynamic entity light ( key is the entity to whom it is associated ) + virtual dlight_t *CL_AllocElight ( int key ) = 0; + + // Get a list of the currently-active dynamic lights. + virtual int CL_GetActiveDLights( dlight_t *pList[MAX_DLIGHTS] ) = 0; + + // Retrieve decal texture name from decal by index + virtual const char *Draw_DecalNameFromIndex( int nIndex ) = 0; + + // Given an elight key, find it. Does not search ordinary dlights. May return NULL. + virtual dlight_t *GetElightByKey( int key ) = 0; +}; + +#define VENGINE_EFFECTS_INTERFACE_VERSION "VEngineEffects001" + +extern IVEfx *effects; + +#endif // IEFX_H diff --git a/public/ienginevgui.h b/public/ienginevgui.h new file mode 100644 index 0000000..6fb9f7c --- /dev/null +++ b/public/ienginevgui.h @@ -0,0 +1,67 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( IENGINEVGUI_H ) +#define IENGINEVGUI_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "vgui/VGUI.h" + +// Forward declarations. +namespace vgui +{ + class Panel; +}; + +enum VGuiPanel_t +{ + PANEL_ROOT = 0, + PANEL_GAMEUIDLL, // the console, game menu + PANEL_CLIENTDLL, + PANEL_TOOLS, + PANEL_INGAMESCREENS, + PANEL_GAMEDLL, + PANEL_CLIENTDLL_TOOLS, + PANEL_GAMEUIBACKGROUND, // the console background, shows under all other stuff in 3d engine view +}; + +// In-game panels are cropped to the current engine viewport size +enum PaintMode_t +{ + PAINT_UIPANELS = (1<<0), + PAINT_INGAMEPANELS = (1<<1), +}; + +abstract_class IEngineVGui +{ +public: + virtual ~IEngineVGui( void ) { } + + virtual vgui::VPANEL GetPanel( VGuiPanel_t type ) = 0; + + virtual bool IsGameUIVisible() = 0; + + virtual void ActivateGameUI() = 0; +}; + +#define VENGINE_VGUI_VERSION "VEngineVGui001" + +#if defined(_STATIC_LINKED) && defined(CLIENT_DLL) +namespace Client +{ +extern IEngineVGui *enginevgui; +} +#else +extern IEngineVGui *enginevgui; +#endif + +#endif // IENGINEVGUI_H diff --git a/public/ifilelist.h b/public/ifilelist.h new file mode 100644 index 0000000..0c58d7a --- /dev/null +++ b/public/ifilelist.h @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IFILELIST_H +#define IFILELIST_H +#ifdef _WIN32 +#pragma once +#endif + + +// This class represents a group of files. Internally, it can represent whole folders of files +// that are in or out of the group. So you can't iterate the list, but you can ask the +// class if a particular filename is in the list. +class IFileList +{ +public: + virtual bool IsFileInList( const char *pFilename ) = 0; + virtual void Release() = 0; +}; + + +#endif // IFILELIST_H + + diff --git a/public/igameevents.h b/public/igameevents.h new file mode 100644 index 0000000..d8a2694 --- /dev/null +++ b/public/igameevents.h @@ -0,0 +1,199 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( IGAMEEVENTS_H ) +#define IGAMEEVENTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" + +#define INTERFACEVERSION_GAMEEVENTSMANAGER "GAMEEVENTSMANAGER001" // old game event manager, don't use it! +#define INTERFACEVERSION_GAMEEVENTSMANAGER2 "GAMEEVENTSMANAGER002" // new game event manager, + +#include "tier1/bitbuf.h" +//----------------------------------------------------------------------------- +// Purpose: Engine interface into global game event management +//----------------------------------------------------------------------------- + +/* + +The GameEventManager keeps track and fires of all global game events. Game events +are fired by game.dll for events like player death or team wins. Each event has a +unique name and comes with a KeyValue structure providing informations about this +event. Some events are generated also by the engine. + +Events are networked to connected clients and invoked there to. Therefore you +have to specify all data fields and there data types in an public resource +file which is parsed by server and broadcasted to it's clients. A typical game +event is defined like this: + + "game_start" // a new game starts + { + "roundslimit" "long" // max round + "timelimit" "long" // time limit + "fraglimit" "long" // frag limit + "objective" "string" // round objective + } + +All events must have unique names (case sensitive) and may have a list +of data fields. each data field must specify a data type, so the engine +knows how to serialize/unserialize that event for network transmission. +Valid data types are string, float, long, short, byte & bool. If a +data field should not be broadcasted to clients, use the type "local". +*/ + + +#define MAX_EVENT_NAME_LENGTH 32 // max game event name length +#define MAX_EVENT_BITS 9 // max bits needed for an event index +#define MAX_EVENT_NUMBER (1< (fn) + +class INetMessage +{ +public: + virtual ~INetMessage() {}; + + // Use these to setup who can hear whose voice. + // Pass in client indices (which are their ent indices - 1). + + virtual void SetNetChannel(INetChannel * netchan) = 0; // netchannel this message is from/for + virtual void SetReliable( bool state ) = 0; // set to true if it's a reliable message + + virtual bool Process( void ) = 0; // calls the recently set handler to process this message + + virtual bool ReadFromBuffer( bf_read &buffer ) = 0; // returns true if parsing was OK + virtual bool WriteToBuffer( bf_write &buffer ) = 0; // returns true if writing was OK + + virtual bool IsReliable( void ) const = 0; // true, if message needs reliable handling + + virtual int GetType( void ) const = 0; // returns module specific header tag eg svc_serverinfo + virtual int GetGroup( void ) const = 0; // returns net message group of this message + virtual const char *GetName( void ) const = 0; // returns network message name, eg "svc_serverinfo" + virtual INetChannel *GetNetChannel( void ) const = 0; + virtual const char *ToString( void ) const = 0; // returns a human readable string about message content + virtual size_t GetSize() const = 0; +}; + + +#endif + diff --git a/public/inetmsghandler.h b/public/inetmsghandler.h new file mode 100644 index 0000000..8145192 --- /dev/null +++ b/public/inetmsghandler.h @@ -0,0 +1,186 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( INETMSGHANDLER_H ) +#define INETMSGHANDLER_H +#ifdef _WIN32 +#pragma once +#endif + +class INetChannel; +typedef struct netpacket_s netpacket_t; + +class INetChannelHandler +{ +public: + virtual ~INetChannelHandler( void ) {}; + + virtual void ConnectionStart(INetChannel *chan) = 0; // called first time network channel is established + + virtual void ConnectionClosing(const char *reason) = 0; // network channel is being closed by remote site + + virtual void ConnectionCrashed(const char *reason) = 0; // network error occured + + virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) = 0; // called each time a new packet arrived + + virtual void PacketEnd( void ) = 0; // all messages has been parsed + + virtual void FileRequested(const char *fileName, unsigned int transferID, bool isReplayDemoFile) = 0; // other side request a file for download + + virtual void FileReceived(const char *fileName, unsigned int transferID, bool isReplayDemoFile) = 0; // we received a file + + virtual void FileDenied(const char *fileName, unsigned int transferID, bool isReplayDemoFile) = 0; // a file request was denied by other side + + virtual void FileSent(const char *fileName, unsigned int transferID, bool isReplayDemoFile) = 0; // we sent a file +}; + +#define PROCESS_NET_MESSAGE( name ) \ + virtual bool Process##name( NET_##name *msg ) + +#define PROCESS_SVC_MESSAGE( name ) \ + virtual bool Process##name( SVC_##name *msg ) + +#define PROCESS_CLC_MESSAGE( name ) \ + virtual bool Process##name( CLC_##name *msg ) + + +#define REGISTER_NET_MSG( name ) \ + NET_##name * p##name = new NET_##name(); \ + p##name->m_pMessageHandler = this; \ + chan->RegisterMessage( p##name ); \ + +#define REGISTER_SVC_MSG( name ) \ + SVC_##name * p##name = new SVC_##name(); \ + p##name->m_pMessageHandler = this; \ + chan->RegisterMessage( p##name ); \ + +#define REGISTER_CLC_MSG( name ) \ + CLC_##name * p##name = new CLC_##name(); \ + p##name->m_pMessageHandler = this; \ + chan->RegisterMessage( p##name ); \ + + +class NET_Tick; +class NET_StringCmd; +class NET_SetConVar; +class NET_SignonState; +class NET_SplitScreenUser; + + +class INetMessageHandler +{ +public: + virtual ~INetMessageHandler( void ) {}; + + PROCESS_NET_MESSAGE( Tick ) = 0; + PROCESS_NET_MESSAGE( StringCmd ) = 0; + PROCESS_NET_MESSAGE( SetConVar ) = 0; + PROCESS_NET_MESSAGE( SignonState ) = 0; + PROCESS_NET_MESSAGE( SplitScreenUser ) = 0; +}; + +class CLC_ClientInfo; +class CLC_Move; +class CLC_VoiceData; +class CLC_BaselineAck; +class CLC_ListenEvents; +class CLC_RespondCvarValue; +class CLC_FileCRCCheck; +class CLC_SplitPlayerConnect; +class CLC_LoadingProgress; +class CLC_SplitPlayerConnect; +class CLC_CmdKeyValues; +class IClientMessageHandler : public INetMessageHandler +{ +public: + virtual ~IClientMessageHandler( void ) {}; + + PROCESS_CLC_MESSAGE( ClientInfo ) = 0; + PROCESS_CLC_MESSAGE( Move ) = 0; + PROCESS_CLC_MESSAGE( VoiceData ) = 0; + PROCESS_CLC_MESSAGE( BaselineAck ) = 0; + PROCESS_CLC_MESSAGE( ListenEvents ) = 0; + PROCESS_CLC_MESSAGE( RespondCvarValue ) = 0; + PROCESS_CLC_MESSAGE( SplitPlayerConnect ) = 0; + PROCESS_CLC_MESSAGE( FileCRCCheck ) = 0; + PROCESS_CLC_MESSAGE( LoadingProgress ) = 0; + PROCESS_CLC_MESSAGE( CmdKeyValues ) = 0; +}; + +class SVC_Print; +class SVC_ServerInfo; +class SVC_SendTable; +class SVC_ClassInfo; +class SVC_SetPause; +class SVC_CreateStringTable; +class SVC_UpdateStringTable; +class SVC_VoiceInit; +class SVC_VoiceData; +class SVC_Sounds; +class SVC_SetView; +class SVC_FixAngle; +class SVC_CrosshairAngle; +class SVC_BSPDecal; +class SVC_GameEvent; +class SVC_UserMessage; +class SVC_EntityMessage; +class SVC_PacketEntities; +class SVC_TempEntities; +class SVC_Prefetch; +class SVC_Menu; +class SVC_GameEventList; +class SVC_GetCvarValue; +class SVC_SplitScreen; +class SVC_CmdKeyValues; + +class IServerMessageHandler : public INetMessageHandler +{ +public: + virtual ~IServerMessageHandler( void ) {}; + + PROCESS_SVC_MESSAGE( Print ) = 0; + PROCESS_SVC_MESSAGE( ServerInfo ) = 0; + PROCESS_SVC_MESSAGE( SendTable ) = 0; + PROCESS_SVC_MESSAGE( ClassInfo ) = 0; + PROCESS_SVC_MESSAGE( SetPause ) = 0; + PROCESS_SVC_MESSAGE( CreateStringTable ) = 0; + PROCESS_SVC_MESSAGE( UpdateStringTable ) = 0; + PROCESS_SVC_MESSAGE( VoiceInit ) = 0; + PROCESS_SVC_MESSAGE( VoiceData ) = 0; + PROCESS_SVC_MESSAGE( Sounds ) = 0; + PROCESS_SVC_MESSAGE( SetView ) = 0; + PROCESS_SVC_MESSAGE( FixAngle ) = 0; + PROCESS_SVC_MESSAGE( CrosshairAngle ) = 0; + PROCESS_SVC_MESSAGE( BSPDecal ) = 0; + PROCESS_SVC_MESSAGE( GameEvent ) = 0; + PROCESS_SVC_MESSAGE( UserMessage ) = 0; + PROCESS_SVC_MESSAGE( EntityMessage ) = 0; + PROCESS_SVC_MESSAGE( PacketEntities ) = 0; + PROCESS_SVC_MESSAGE( TempEntities ) = 0; + PROCESS_SVC_MESSAGE( Prefetch ) = 0; + PROCESS_SVC_MESSAGE( Menu ) = 0; + PROCESS_SVC_MESSAGE( GameEventList ) = 0; + PROCESS_SVC_MESSAGE( GetCvarValue ) = 0; + PROCESS_SVC_MESSAGE( SplitScreen ) = 0; + PROCESS_SVC_MESSAGE( CmdKeyValues ) = 0; +}; + +class IConnectionlessPacketHandler +{ +public: + virtual ~IConnectionlessPacketHandler( void ) {}; + + virtual bool ProcessConnectionlessPacket( netpacket_t *packet ) = 0; // process a connectionless packet +}; + + +#endif // INETMSGHANDLER_H diff --git a/public/inetwork.h b/public/inetwork.h new file mode 100644 index 0000000..28a4798 --- /dev/null +++ b/public/inetwork.h @@ -0,0 +1,52 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef INETWORK_H +#define INETWORK_H +#ifdef _WIN32 +#pragma once +#endif + +class IConnectionlessPacketHandler; + +abstract_class INetwork +{ +public: + virtual ~INetwork( void ) {}; + + virtual void Init( void ) = 0; + virtual void Config (bool multiplayer); + virtual void IsMultiplayer( void ) = 0; // true = full MP mode, false = loopback SP mode + virtual void IsEnabled( void ) = 0; + + // shuts down Network, closes all UPD/TCP channels + virtual void Shutdown( void ) = 0; + + // must be called each system frame to do any asynchronouse TCP stuff + virtual void RunFrame( double time ) = 0; + + virtual void ProcessSocket( netsrc_t sock, IConnectionlessPacketHandler * handler ) = 0; + + virtual void OutOfBandPrintf(netsrc_t sock, netadr_t &adr, const char *format, ...) = 0; + virtual void SendConnectionless(netsrc_t sock, netadr_t &adr, unsigned char * data, int length ) = 0; + + virtual void LogBadPacket(netpacket_t * packet) = 0; + + // Address conversion + virtual bool StringToAdr ( const char *s, netadr_t *a) = 0; + + // Convert from host to network byte ordering + virtual unsigned short HostToNetShort( unsigned short us_in ); + + // and vice versa + virtual unsigned short NetToHostShort( unsigned short us_in ); + + + +}; + + +#endif // INETWORK_H diff --git a/public/inputsystem/AnalogCode.h b/public/inputsystem/AnalogCode.h new file mode 100644 index 0000000..4ddac7f --- /dev/null +++ b/public/inputsystem/AnalogCode.h @@ -0,0 +1,44 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ANALOGCODE_H +#define ANALOGCODE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "inputsystem/InputEnums.h" + + +//----------------------------------------------------------------------------- +// Macro to get at joystick codes +//----------------------------------------------------------------------------- +#define JOYSTICK_AXIS_INTERNAL( _joystick, _axis ) ( JOYSTICK_FIRST_AXIS + ((_joystick) * MAX_JOYSTICK_AXES) + (_axis) ) +#define JOYSTICK_AXIS( _joystick, _axis ) ( (AnalogCode_t)JOYSTICK_AXIS_INTERNAL( _joystick, _axis ) ) + + +//----------------------------------------------------------------------------- +// Enumeration for analog input devices. Includes joysticks, mousewheel, mouse +//----------------------------------------------------------------------------- +enum AnalogCode_t +{ + ANALOG_CODE_INVALID = -1, + MOUSE_X = 0, + MOUSE_Y, + MOUSE_XY, // Invoked when either x or y changes + MOUSE_WHEEL, + + JOYSTICK_FIRST_AXIS, + JOYSTICK_LAST_AXIS = JOYSTICK_AXIS_INTERNAL( MAX_JOYSTICKS-1, MAX_JOYSTICK_AXES-1 ), + + ANALOG_CODE_LAST, +}; + + +#endif // ANALOGCODE_H diff --git a/public/inputsystem/ButtonCode.h b/public/inputsystem/ButtonCode.h new file mode 100644 index 0000000..8c705c0 --- /dev/null +++ b/public/inputsystem/ButtonCode.h @@ -0,0 +1,345 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef BUTTONCODE_H +#define BUTTONCODE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "inputsystem/InputEnums.h" + +//----------------------------------------------------------------------------- +// Button enum. "Buttons" are binary-state input devices (mouse buttons, keyboard keys) +//----------------------------------------------------------------------------- +enum +{ + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) + +#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) + +enum ButtonCode_t +{ + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL( MAX_JOYSTICKS-1, JOYSTICK_MAX_BUTTON_COUNT-1 ), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL( MAX_JOYSTICKS-1, JOYSTICK_POV_BUTTON_COUNT-1 ), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL( MAX_JOYSTICKS-1, JOYSTICK_AXIS_BUTTON_COUNT-1 ), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + KEY_XBUTTON_INACTIVE_START, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; + +inline bool IsAlpha( ButtonCode_t code ) +{ + return ( code >= KEY_A ) && ( code <= KEY_Z ); +} + +inline bool IsAlphaNumeric( ButtonCode_t code ) +{ + return ( code >= KEY_0 ) && ( code <= KEY_Z ); +} + +inline bool IsSpace( ButtonCode_t code ) +{ + return ( code == KEY_ENTER ) || ( code == KEY_TAB ) || ( code == KEY_SPACE ); +} + +inline bool IsKeypad( ButtonCode_t code ) +{ + return ( code >= MOUSE_FIRST ) && ( code <= KEY_PAD_DECIMAL ); +} + +inline bool IsPunctuation( ButtonCode_t code ) +{ + return ( code >= KEY_0 ) && ( code <= KEY_SPACE ) && !IsAlphaNumeric( code ) && !IsSpace( code ) && !IsKeypad( code ); +} + +inline bool IsKeyCode( ButtonCode_t code ) +{ + return ( code >= KEY_FIRST ) && ( code <= KEY_LAST ); +} + +inline bool IsMouseCode( ButtonCode_t code ) +{ + return ( code >= MOUSE_FIRST ) && ( code <= MOUSE_LAST ); +} + +inline bool IsJoystickCode( ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST ) && ( code <= JOYSTICK_LAST ); +} + +inline bool IsJoystickButtonCode( ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_BUTTON ) && ( code <= JOYSTICK_LAST_BUTTON ); +} + +inline bool IsJoystickPOVCode( ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_POV_BUTTON ) && ( code <= JOYSTICK_LAST_POV_BUTTON ); +} + +inline bool IsJoystickAxisCode( ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_AXIS_BUTTON ) && ( code <= JOYSTICK_LAST_AXIS_BUTTON ); +} + +inline ButtonCode_t GetBaseButtonCode( ButtonCode_t code ) +{ + if ( IsJoystickButtonCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_BUTTON ) % JOYSTICK_MAX_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_BUTTON + offset ); + } + + if ( IsJoystickPOVCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_POV_BUTTON ) % JOYSTICK_POV_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_POV_BUTTON + offset ); + } + + if ( IsJoystickAxisCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_AXIS_BUTTON ) % JOYSTICK_AXIS_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_AXIS_BUTTON + offset ); + } + + return code; +} + +inline int GetJoystickForCode( ButtonCode_t code ) +{ + if ( !IsJoystickCode( code ) ) + return 0; + + if ( IsJoystickButtonCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_BUTTON ) / JOYSTICK_MAX_BUTTON_COUNT; + return offset; + } + if ( IsJoystickPOVCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_POV_BUTTON ) / JOYSTICK_POV_BUTTON_COUNT; + return offset; + } + if ( IsJoystickAxisCode( code ) ) + { + int offset = ( code - JOYSTICK_FIRST_AXIS_BUTTON ) / JOYSTICK_AXIS_BUTTON_COUNT; + return offset; + } + + return 0; +} + +inline ButtonCode_t ButtonCodeToJoystickButtonCode( ButtonCode_t code, int nDesiredJoystick ) +{ + if ( !IsJoystickCode( code ) || nDesiredJoystick == 0 ) + return code; + + nDesiredJoystick = clamp( nDesiredJoystick, 0, MAX_JOYSTICKS - 1 ); + + code = GetBaseButtonCode( code ); + + // Now upsample it + if ( IsJoystickButtonCode( code ) ) + { + int nOffset = code - JOYSTICK_FIRST_BUTTON; + return JOYSTICK_BUTTON( nDesiredJoystick, nOffset ); + } + + if ( IsJoystickPOVCode( code ) ) + { + int nOffset = code - JOYSTICK_FIRST_POV_BUTTON; + return JOYSTICK_POV_BUTTON( nDesiredJoystick, nOffset ); + } + + if ( IsJoystickAxisCode( code ) ) + { + int nOffset = code - JOYSTICK_FIRST_AXIS_BUTTON; + return JOYSTICK_AXIS_BUTTON( nDesiredJoystick, nOffset ); + } + + return code; +} + +#endif // BUTTONCODE_H diff --git a/public/inputsystem/InputEnums.h b/public/inputsystem/InputEnums.h new file mode 100644 index 0000000..06da5da --- /dev/null +++ b/public/inputsystem/InputEnums.h @@ -0,0 +1,109 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef INPUTENUMS_H +#define INPUTENUMS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/basetypes.h" + +// Standard maximum +/- value of a joystick axis +#define MAX_BUTTONSAMPLE 32768 + +#if !defined( _X360 ) +#define INVALID_USER_ID -1 +#else +#define INVALID_USER_ID XBX_INVALID_USER_ID +#endif + +//----------------------------------------------------------------------------- +// Forward declarations: +//----------------------------------------------------------------------------- + +enum +{ + MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, + MOUSE_BUTTON_COUNT = 5, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum JoystickDeadzoneMode_t +{ + JOYSTICK_DEADZONE_CROSS = 0, + JOYSTICK_DEADZONE_SQUARE = 1, +}; + + +//----------------------------------------------------------------------------- +// Extra mouse codes +//----------------------------------------------------------------------------- +enum +{ + MS_WM_XBUTTONDOWN = 0x020B, + MS_WM_XBUTTONUP = 0x020C, + MS_WM_XBUTTONDBLCLK = 0x020D, + MS_MK_BUTTON4 = 0x0020, + MS_MK_BUTTON5 = 0x0040, +}; + +//----------------------------------------------------------------------------- +// Events +//----------------------------------------------------------------------------- +enum InputEventType_t +{ + IE_ButtonPressed = 0, // m_nData contains a ButtonCode_t + IE_ButtonReleased, // m_nData contains a ButtonCode_t + IE_ButtonDoubleClicked, // m_nData contains a ButtonCode_t + IE_AnalogValueChanged, // m_nData contains an AnalogCode_t, m_nData2 contains the value + + IE_FirstSystemEvent = 100, + IE_Quit = IE_FirstSystemEvent, + IE_ControllerInserted, // m_nData contains the controller ID + IE_ControllerUnplugged, // m_nData contains the controller ID + IE_Close, + IE_WindowSizeChanged, // m_nData contains width, m_nData2 contains height, m_nData3 = 0 if not minimized, 1 if minimized + + IE_FirstUIEvent = 200, + IE_LocateMouseClick = IE_FirstUIEvent, + IE_SetCursor, + IE_KeyTyped, + IE_KeyCodeTyped, + IE_InputLanguageChanged, + IE_IMESetWindow, + IE_IMEStartComposition, + IE_IMEComposition, + IE_IMEEndComposition, + IE_IMEShowCandidates, + IE_IMEChangeCandidates, + IE_IMECloseCandidates, + IE_IMERecomputeModes, + + IE_FirstVguiEvent = 1000, // Assign ranges for other systems that post user events here + IE_FirstAppEvent = 2000, +}; + +struct InputEvent_t +{ + int m_nType; // Type of the event (see InputEventType_t) + int m_nTick; // Tick on which the event occurred + int m_nData; // Generic 32-bit data, what it contains depends on the event + int m_nData2; // Generic 32-bit data, what it contains depends on the event + int m_nData3; // Generic 32-bit data, what it contains depends on the event +}; + +#endif // INPUTENUMS_H diff --git a/public/inputsystem/iinputstacksystem.h b/public/inputsystem/iinputstacksystem.h new file mode 100644 index 0000000..0b32fa1 --- /dev/null +++ b/public/inputsystem/iinputstacksystem.h @@ -0,0 +1,76 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: This is input priority system, allowing various clients to +// cause input messages / cursor control to be routed to them as opposed to +// other clients. +// +//===========================================================================// + +#ifndef IINPUTCLIENTSTACK_H +#define IINPUTCLIENTSTACK_H +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" +#include "inputsystem/iinputsystem.h" + + +///----------------------------------------------------------------------------- +/// A handle to an input context. These are arranged in a priority-based +/// stack; the top context on the stack which is also enabled wins. +///----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( InputContextHandle_t ); +#define INPUT_CONTEXT_HANDLE_INVALID ( (InputContextHandle_t)0 ) + + +///----------------------------------------------------------------------------- +/// Purpose: This is input priority system, allowing various clients to +/// cause input messages / cursor control to be routed to them as opposed to +/// other clients. +/// +/// NOTE: For Source1, it would be a huge change to move all input (like +/// the code in engine/keys.cpp for example) to go through this interface. +/// Therefore, I'm going to stick with only dealing with cursor control, +/// which is necessary for Jen's new gameUI system to interoperate with VGui. +///----------------------------------------------------------------------------- +abstract_class IInputStackSystem : public IAppSystem +{ +public: + /// Allocates an input context, pushing it on top of the input stack, + /// thereby giving it top priority + virtual InputContextHandle_t PushInputContext() = 0; + + /// Pops the top input context off the input stack, and destroys it. + virtual void PopInputContext( ) = 0; + + /// Enables/disables an input context, allowing something lower on the + /// stack to have control of input. Disabling an input context which + /// owns mouse capture + virtual void EnableInputContext( InputContextHandle_t hContext, bool bEnable ) = 0; + + /// Allows a context to make the cursor visible; + /// the topmost enabled context wins + virtual void SetCursorVisible( InputContextHandle_t hContext, bool bVisible ) = 0; + + /// Allows a context to set the cursor icon; + /// the topmost enabled context wins + virtual void SetCursorIcon( InputContextHandle_t hContext, InputCursorHandle_t hCursor ) = 0; + + /// Allows a context to enable mouse capture. Disabling an input context + /// deactivates mouse capture. Capture will occur if it happens on the + /// topmost enabled context + virtual void SetMouseCapture( InputContextHandle_t hContext, bool bEnable ) = 0; + + /// Allows a context to set the mouse position. It only has any effect if the + /// specified context is the topmost enabled context + virtual void SetCursorPosition( InputContextHandle_t hContext, int x, int y ) = 0; + + /// Returns true if the specified context is the topmost enabled context + virtual bool IsTopmostEnabledContext( InputContextHandle_t hContext ) const = 0; +}; + +DECLARE_TIER2_INTERFACE( IInputStackSystem, g_pInputStackSystem ); + + +#endif // IINPUTCLIENTSTACK_H diff --git a/public/inputsystem/iinputsystem.h b/public/inputsystem/iinputsystem.h new file mode 100644 index 0000000..a419a8d --- /dev/null +++ b/public/inputsystem/iinputsystem.h @@ -0,0 +1,178 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IINPUTSYSTEM_H +#define IINPUTSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/platwindow.h" +#include "appframework/IAppSystem.h" +#include "inputsystem/InputEnums.h" +#include "inputsystem/ButtonCode.h" +#include "inputsystem/AnalogCode.h" + + +///----------------------------------------------------------------------------- +/// A handle to a cursor icon +///----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( InputCursorHandle_t ); +#define INPUT_CURSOR_HANDLE_INVALID ( (InputCursorHandle_t)0 ) + + +///----------------------------------------------------------------------------- +/// An enumeration describing well-known cursor icons +///----------------------------------------------------------------------------- +enum InputStandardCursor_t +{ + INPUT_CURSOR_NONE = 0, + INPUT_CURSOR_ARROW, + INPUT_CURSOR_IBEAM, + INPUT_CURSOR_HOURGLASS, + INPUT_CURSOR_CROSSHAIR, + INPUT_CURSOR_WAITARROW, + INPUT_CURSOR_UP, + INPUT_CURSOR_SIZE_NW_SE, + INPUT_CURSOR_SIZE_NE_SW, + INPUT_CURSOR_SIZE_W_E, + INPUT_CURSOR_SIZE_N_S, + INPUT_CURSOR_SIZE_ALL, + INPUT_CURSOR_NO, + INPUT_CURSOR_HAND, + + INPUT_CURSOR_COUNT +}; + + +///----------------------------------------------------------------------------- +/// Main interface for input. This is a low-level interface, creating an +/// OS-independent queue of low-level input events which were sampled since +/// the last call to PollInputState. It also contains facilities for cursor +/// control and creation. +///----------------------------------------------------------------------------- +abstract_class IInputSystem : public IAppSystem +{ +public: + /// Attach, detach input system from a particular window + /// This window should be the root window for the application + /// Only 1 window should be attached at any given time. + virtual void AttachToWindow( void* hWnd ) = 0; + virtual void DetachFromWindow( ) = 0; + + /// Enables/disables input. PollInputState will not update current + /// button/analog states when it is called if the system is disabled. + virtual void EnableInput( bool bEnable ) = 0; + + /// Enables/disables the windows message pump. PollInputState will not + /// Peek/Dispatch messages if this is disabled + virtual void EnableMessagePump( bool bEnable ) = 0; + + /// Polls the current input state + virtual void PollInputState() = 0; + + /// Gets the time of the last polling in ms + virtual int GetPollTick() const = 0; + + /// Is a button down? "Buttons" are binary-state input devices (mouse buttons, keyboard keys) + virtual bool IsButtonDown( ButtonCode_t code ) const = 0; + + /// Returns the tick at which the button was pressed and released + virtual int GetButtonPressedTick( ButtonCode_t code ) const = 0; + virtual int GetButtonReleasedTick( ButtonCode_t code ) const = 0; + + /// Gets the value of an analog input device this frame + /// Includes joysticks, mousewheel, mouse + virtual int GetAnalogValue( AnalogCode_t code ) const = 0; + + /// Gets the change in a particular analog input device this frame + /// Includes joysticks, mousewheel, mouse + virtual int GetAnalogDelta( AnalogCode_t code ) const = 0; + + /// Returns the input events since the last poll + virtual int GetEventCount() const = 0; + virtual const InputEvent_t* GetEventData( ) const = 0; + + /// Posts a user-defined event into the event queue; this is expected + /// to be called in overridden wndprocs connected to the root panel. + virtual void PostUserEvent( const InputEvent_t &event ) = 0; + + /// Returns the number of joysticks + virtual int GetJoystickCount() const = 0; + + /// Enable/disable joystick, it has perf costs + virtual void EnableJoystickInput( int nJoystick, bool bEnable ) = 0; + + /// Enable/disable diagonal joystick POV (simultaneous POV buttons down) + virtual void EnableJoystickDiagonalPOV( int nJoystick, bool bEnable ) = 0; + + /// Sample the joystick and append events to the input queue + virtual void SampleDevices( void ) = 0; + + // FIXME: Currently force-feedback is only supported on the Xbox 360 + virtual void SetRumble( float fLeftMotor, float fRightMotor, int userId = INVALID_USER_ID ) = 0; + virtual void StopRumble( int userId = INVALID_USER_ID ) = 0; + + /// Resets the input state + virtual void ResetInputState() = 0; + + /// Sets a player as the primary user - all other controllers will be ignored. + virtual void SetPrimaryUserId( int userId ) = 0; + + /// Convert back + forth between ButtonCode/AnalogCode + strings + virtual const char *ButtonCodeToString( ButtonCode_t code ) const = 0; + virtual const char *AnalogCodeToString( AnalogCode_t code ) const = 0; + virtual ButtonCode_t StringToButtonCode( const char *pString ) const = 0; + virtual AnalogCode_t StringToAnalogCode( const char *pString ) const = 0; + + /// Sleeps until input happens. Pass a negative number to sleep infinitely + virtual void SleepUntilInput( int nMaxSleepTimeMS = -1 ) = 0; + + /// Convert back + forth between virtual codes + button codes + // FIXME: This is a temporary piece of code + virtual ButtonCode_t VirtualKeyToButtonCode( int nVirtualKey ) const = 0; + virtual int ButtonCodeToVirtualKey( ButtonCode_t code ) const = 0; + virtual ButtonCode_t ScanCodeToButtonCode( int lParam ) const = 0; + + /// How many times have we called PollInputState? + virtual int GetPollCount() const = 0; + + /// Sets the cursor position + virtual void SetCursorPosition( int x, int y ) = 0; + + /// Tells the input system to generate UI-related events, defined + /// in inputsystem/inputenums.h (see IE_FirstUIEvent) + /// We could have multiple clients that care about UI-related events + /// so we refcount the clients with an Add/Remove strategy. If there + /// are no interested clients, the UI events are not generated + virtual void AddUIEventListener() = 0; + virtual void RemoveUIEventListener() = 0; + + /// Returns the currently attached window + virtual PlatWindow_t GetAttachedWindow() const = 0; + + /// Creates a cursor using one of the well-known cursor icons + virtual InputCursorHandle_t GetStandardCursor( InputStandardCursor_t id ) = 0; + + /// Loads a cursor defined in a file + virtual InputCursorHandle_t LoadCursorFromFile( const char *pFileName, const char *pPathID = NULL ) = 0; + + /// Sets the cursor icon + virtual void SetCursorIcon( InputCursorHandle_t hCursor ) = 0; + + /// Gets the cursor position + virtual void GetCursorPosition( int *pX, int *pY ) = 0; + + /// Mouse capture + virtual void EnableMouseCapture( PlatWindow_t hWnd ) = 0; + virtual void DisableMouseCapture() = 0; +}; + +DECLARE_TIER2_INTERFACE( IInputSystem, g_pInputSystem ); + + +#endif // IINPUTSYSTEM_H diff --git a/public/interfaces/interfaces.h b/public/interfaces/interfaces.h new file mode 100644 index 0000000..35b7a38 --- /dev/null +++ b/public/interfaces/interfaces.h @@ -0,0 +1,287 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef INTERFACES_H +#define INTERFACES_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Interface creation function +//----------------------------------------------------------------------------- +typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); + + +//----------------------------------------------------------------------------- +// Macros to declare interfaces appropriate for various tiers +//----------------------------------------------------------------------------- +#if 1 || defined( TIER1_LIBRARY ) || defined( TIER2_LIBRARY ) || defined( TIER3_LIBRARY ) || defined( TIER4_LIBRARY ) || defined( APPLICATION ) +#define DECLARE_TIER1_INTERFACE( _Interface, _Global ) extern _Interface * _Global; +#else +#define DECLARE_TIER1_INTERFACE( _Interface, _Global ) +#endif + +#if 1 || defined( TIER2_LIBRARY ) || defined( TIER3_LIBRARY ) || defined( TIER4_LIBRARY ) || defined( APPLICATION ) +#define DECLARE_TIER2_INTERFACE( _Interface, _Global ) extern _Interface * _Global; +#else +#define DECLARE_TIER2_INTERFACE( _Interface, _Global ) +#endif + +#if 1 || defined( TIER3_LIBRARY ) || defined( TIER4_LIBRARY ) || defined( APPLICATION ) +#define DECLARE_TIER3_INTERFACE( _Interface, _Global ) extern _Interface * _Global; +#else +#define DECLARE_TIER3_INTERFACE( _Interface, _Global ) +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class ICvar; +class IProcessUtils; +class ILocalize; +class IPhysics2; +class IPhysics2ActorManager; +class IPhysics2ResourceManager; +class IEventSystem; + +class IAsyncFileSystem; +class IColorCorrectionSystem; +class IDebugTextureInfo; +class IFileSystem; +class IRenderHardwareConfig; +class IInputSystem; +class IInputStackSystem; +class IMaterialSystem; +class IMaterialSystem2; +class IMaterialSystemHardwareConfig; +class IMdlLib; +class INetworkSystem; +class IP4; +class IQueuedLoader; +class IResourceAccessControl; +class IPrecacheSystem; +class IRenderDevice; +class IRenderDeviceMgr; +class IResourceSystem; +class IVBAllocTracker; +class IXboxInstaller; +class IMatchFramework; +class ISoundSystem; +class IStudioRender; +class IMatSystemSurface; +class IGameUISystemMgr; +class IDataCache; +class IMDLCache; +class IAvi; +class IBik; +class IDmeMakefileUtils; +class IPhysicsCollision; +class ISoundEmitterSystemBase; +class IMeshSystem; +class IWorldRendererMgr; +class ISceneSystem; +class IVGuiRenderSurface; + +namespace vgui +{ + class ISurface; + class IVGui; + class IInput; + class IPanel; + class ILocalize; + class ISchemeManager; + class ISystem; +} + + + +//----------------------------------------------------------------------------- +// Fills out global DLL exported interface pointers +//----------------------------------------------------------------------------- +#define CVAR_INTERFACE_VERSION "VEngineCvar007" +DECLARE_TIER1_INTERFACE( ICvar, cvar ); +DECLARE_TIER1_INTERFACE( ICvar, g_pCVar ) + +#define PROCESS_UTILS_INTERFACE_VERSION "VProcessUtils002" +DECLARE_TIER1_INTERFACE( IProcessUtils, g_pProcessUtils ); + +#define VPHYSICS2_INTERFACE_VERSION "Physics2 Interface v0.3" +DECLARE_TIER1_INTERFACE( IPhysics2, g_pPhysics2 ); + +#define VPHYSICS2_ACTOR_MGR_INTERFACE_VERSION "Physics2 Interface ActorMgr v0.1" +DECLARE_TIER1_INTERFACE( IPhysics2ActorManager, g_pPhysics2ActorManager ); + +#define VPHYSICS2_RESOURCE_MGR_INTERFACE_VERSION "Physics2 Interface ResourceMgr v0.1" +DECLARE_TIER1_INTERFACE( IPhysics2ResourceManager, g_pPhysics2ResourceManager ); + +#define EVENTSYSTEM_INTERFACE_VERSION "EventSystem001" +DECLARE_TIER1_INTERFACE( IEventSystem, g_pEventSystem ); + +#define LOCALIZE_INTERFACE_VERSION "Localize_001" +DECLARE_TIER2_INTERFACE( ILocalize, g_pLocalize ); +DECLARE_TIER3_INTERFACE( vgui::ILocalize, g_pVGuiLocalize ); + +#define RENDER_DEVICE_MGR_INTERFACE_VERSION "RenderDeviceMgr001" +DECLARE_TIER2_INTERFACE( IRenderDeviceMgr, g_pRenderDeviceMgr ); + +#define FILESYSTEM_INTERFACE_VERSION "VFileSystem017" +DECLARE_TIER2_INTERFACE( IFileSystem, g_pFullFileSystem ); + +#define ASYNCFILESYSTEM_INTERFACE_VERSION "VNewAsyncFileSystem001" +DECLARE_TIER2_INTERFACE( IAsyncFileSystem, g_pAsyncFileSystem ); + +#define RESOURCESYSTEM_INTERFACE_VERSION "ResourceSystem004" +DECLARE_TIER2_INTERFACE( IResourceSystem, g_pResourceSystem ); + +#define MATERIAL_SYSTEM_INTERFACE_VERSION "VMaterialSystem080" +DECLARE_TIER2_INTERFACE( IMaterialSystem, materials ); +DECLARE_TIER2_INTERFACE( IMaterialSystem, g_pMaterialSystem ); + +#define MATERIAL_SYSTEM2_INTERFACE_VERSION "VMaterialSystem2_001" +DECLARE_TIER2_INTERFACE( IMaterialSystem2, g_pMaterialSystem2 ); + +#define INPUTSYSTEM_INTERFACE_VERSION "InputSystemVersion001" +DECLARE_TIER2_INTERFACE( IInputSystem, g_pInputSystem ); + +#define INPUTSTACKSYSTEM_INTERFACE_VERSION "InputStackSystemVersion001" +DECLARE_TIER2_INTERFACE( IInputStackSystem, g_pInputStackSystem ); + +#define NETWORKSYSTEM_INTERFACE_VERSION "NetworkSystemVersion001" +DECLARE_TIER2_INTERFACE( INetworkSystem, g_pNetworkSystem ); + +#define MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION "MaterialSystemHardwareConfig013" +DECLARE_TIER2_INTERFACE( IMaterialSystemHardwareConfig, g_pMaterialSystemHardwareConfig ); + +#define DEBUG_TEXTURE_INFO_VERSION "DebugTextureInfo001" +DECLARE_TIER2_INTERFACE( IDebugTextureInfo, g_pMaterialSystemDebugTextureInfo ); + +#define VB_ALLOC_TRACKER_INTERFACE_VERSION "VBAllocTracker001" +DECLARE_TIER2_INTERFACE( IVBAllocTracker, g_VBAllocTracker ); + +#define COLORCORRECTION_INTERFACE_VERSION "COLORCORRECTION_VERSION_1" +DECLARE_TIER2_INTERFACE( IColorCorrectionSystem, colorcorrection ); + +#define P4_INTERFACE_VERSION "VP4002" +DECLARE_TIER2_INTERFACE( IP4, p4 ); + +#define MDLLIB_INTERFACE_VERSION "VMDLLIB001" +DECLARE_TIER2_INTERFACE( IMdlLib, mdllib ); + +#define QUEUEDLOADER_INTERFACE_VERSION "QueuedLoaderVersion001" +DECLARE_TIER2_INTERFACE( IQueuedLoader, g_pQueuedLoader ); + +#define RESOURCE_ACCESS_CONTROL_INTERFACE_VERSION "VResourceAccessControl001" +DECLARE_TIER2_INTERFACE( IResourceAccessControl, g_pResourceAccessControl ); + +#define PRECACHE_SYSTEM_INTERFACE_VERSION "VPrecacheSystem001" +DECLARE_TIER2_INTERFACE( IPrecacheSystem, g_pPrecacheSystem ); + +#if defined( _X360 ) +#define XBOXINSTALLER_INTERFACE_VERSION "XboxInstallerVersion001" +DECLARE_TIER2_INTERFACE( IXboxInstaller, g_pXboxInstaller ); +#endif + +#define MATCHFRAMEWORK_INTERFACE_VERSION "MATCHFRAMEWORK_001" +DECLARE_TIER2_INTERFACE( IMatchFramework, g_pMatchFramework ); + +#define GAMEUISYSTEMMGR_INTERFACE_VERSION "GameUISystemMgr001" +DECLARE_TIER3_INTERFACE( IGameUISystemMgr, g_pGameUISystemMgr ); + + +//----------------------------------------------------------------------------- +// Not exactly a global, but we're going to keep track of these here anyways +// NOTE: Appframework deals with connecting these bad boys. See materialsystem2app.cpp +//----------------------------------------------------------------------------- +#define RENDER_DEVICE_INTERFACE_VERSION "RenderDevice001" +DECLARE_TIER2_INTERFACE( IRenderDevice, g_pRenderDevice ); + +#define RENDER_HARDWARECONFIG_INTERFACE_VERSION "RenderHardwareConfig001" +DECLARE_TIER2_INTERFACE( IRenderHardwareConfig, g_pRenderHardwareConfig ); + +#define SOUNDSYSTEM_INTERFACE_VERSION "SoundSystem001" +DECLARE_TIER2_INTERFACE( ISoundSystem, g_pSoundSystem ); + +#define MESHSYSTEM_INTERFACE_VERSION "MeshSystem001" +DECLARE_TIER3_INTERFACE( IMeshSystem, g_pMeshSystem ); + +#define STUDIO_RENDER_INTERFACE_VERSION "VStudioRender026" +DECLARE_TIER3_INTERFACE( IStudioRender, g_pStudioRender ); +DECLARE_TIER3_INTERFACE( IStudioRender, studiorender ); + +#define MAT_SYSTEM_SURFACE_INTERFACE_VERSION "MatSystemSurface006" +DECLARE_TIER3_INTERFACE( IMatSystemSurface, g_pMatSystemSurface ); + +#define RENDER_SYSTEM_SURFACE_INTERFACE_VERSION "RenderSystemSurface001" +DECLARE_TIER3_INTERFACE( IVGuiRenderSurface, g_pVGuiRenderSurface ); + +#define SCENESYSTEM_INTERFACE_VERSION "SceneSystem_001" +DECLARE_TIER3_INTERFACE( ISceneSystem, g_pSceneSystem ); + +#define VGUI_SURFACE_INTERFACE_VERSION "VGUI_Surface031" +DECLARE_TIER3_INTERFACE( vgui::ISurface, g_pVGuiSurface ); + +#define SCHEME_SURFACE_INTERFACE_VERSION "SchemeSurface001" + +#define VGUI_INPUT_INTERFACE_VERSION "VGUI_Input005" +DECLARE_TIER3_INTERFACE( vgui::IInput, g_pVGuiInput ); + +#define VGUI_IVGUI_INTERFACE_VERSION "VGUI_ivgui008" +DECLARE_TIER3_INTERFACE( vgui::IVGui, g_pVGui ); + +#define VGUI_PANEL_INTERFACE_VERSION "VGUI_Panel009" +DECLARE_TIER3_INTERFACE( vgui::IPanel, g_pVGuiPanel ); + +#define VGUI_SCHEME_INTERFACE_VERSION "VGUI_Scheme010" +DECLARE_TIER3_INTERFACE( vgui::ISchemeManager, g_pVGuiSchemeManager ); + +#define VGUI_SYSTEM_INTERFACE_VERSION "VGUI_System010" +DECLARE_TIER3_INTERFACE( vgui::ISystem, g_pVGuiSystem ); + +#define DATACACHE_INTERFACE_VERSION "VDataCache003" +DECLARE_TIER3_INTERFACE( IDataCache, g_pDataCache ); // FIXME: Should IDataCache be in tier2? + +#define MDLCACHE_INTERFACE_VERSION "MDLCache004" +DECLARE_TIER3_INTERFACE( IMDLCache, g_pMDLCache ); +DECLARE_TIER3_INTERFACE( IMDLCache, mdlcache ); + +#define AVI_INTERFACE_VERSION "VAvi001" +DECLARE_TIER3_INTERFACE( IAvi, g_pAVI ); + +#define BIK_INTERFACE_VERSION "VBik001" +DECLARE_TIER3_INTERFACE( IBik, g_pBIK ); + +#define DMEMAKEFILE_UTILS_INTERFACE_VERSION "VDmeMakeFileUtils001" +DECLARE_TIER3_INTERFACE( IDmeMakefileUtils, g_pDmeMakefileUtils ); + +#define VPHYSICS_COLLISION_INTERFACE_VERSION "VPhysicsCollision007" +DECLARE_TIER3_INTERFACE( IPhysicsCollision, g_pPhysicsCollision ); + +#define SOUNDEMITTERSYSTEM_INTERFACE_VERSION "VSoundEmitter003" +DECLARE_TIER3_INTERFACE( ISoundEmitterSystemBase, g_pSoundEmitterSystem ); + +#define WORLD_RENDERER_MGR_INTERFACE_VERSION "WorldRendererMgr001" +DECLARE_TIER3_INTERFACE( IWorldRendererMgr, g_pWorldRendererMgr ); + +//----------------------------------------------------------------------------- +// Fills out global DLL exported interface pointers +//----------------------------------------------------------------------------- +void ConnectInterfaces( CreateInterfaceFn *pFactoryList, int nFactoryCount ); +void DisconnectInterfaces(); + + +//----------------------------------------------------------------------------- +// Reconnects an interface +//----------------------------------------------------------------------------- +void ReconnectInterface( CreateInterfaceFn factory, const char *pInterfaceName ); + + +#endif // INTERFACES_H + diff --git a/public/interpolatortypes.cpp b/public/interpolatortypes.cpp new file mode 100644 index 0000000..d8ff512 --- /dev/null +++ b/public/interpolatortypes.cpp @@ -0,0 +1,506 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "basetypes.h" +#include "tier1/strtools.h" +#include "interpolatortypes.h" +#include "tier0/dbg.h" +#include "mathlib/mathlib.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +struct InterpolatorNameMap_t +{ + int type; + char const *name; + char const *printname; +}; + +static InterpolatorNameMap_t g_InterpolatorNameMap[] = +{ + { INTERPOLATE_DEFAULT, "default", "Default" }, + { INTERPOLATE_CATMULL_ROM_NORMALIZEX, "catmullrom_normalize_x", "Catmull-Rom (Norm X)" }, + { INTERPOLATE_EASE_IN, "easein", "Ease In" }, + { INTERPOLATE_EASE_OUT, "easeout", "Ease Out" }, + { INTERPOLATE_EASE_INOUT, "easeinout", "Ease In/Out" }, + { INTERPOLATE_BSPLINE, "bspline", "B-Spline" }, + { INTERPOLATE_LINEAR_INTERP, "linear_interp", "Linear Interp." }, + { INTERPOLATE_KOCHANEK_BARTELS, "kochanek", "Kochanek-Bartels" }, + { INTERPOLATE_KOCHANEK_BARTELS_EARLY, "kochanek_early", "Kochanek-Bartels Early" }, + { INTERPOLATE_KOCHANEK_BARTELS_LATE, "kochanek_late", "Kochanek-Bartels Late" }, + { INTERPOLATE_SIMPLE_CUBIC, "simple_cubic", "Simple Cubic" }, + { INTERPOLATE_CATMULL_ROM, "catmullrom", "Catmull-Rom" }, + { INTERPOLATE_CATMULL_ROM_NORMALIZE, "catmullrom_normalize", "Catmull-Rom (Norm)" }, + { INTERPOLATE_CATMULL_ROM_TANGENT, "catmullrom_tangent", "Catmull-Rom (Tangent)" }, + { INTERPOLATE_EXPONENTIAL_DECAY, "exponential_decay", "Exponential Decay" }, + { INTERPOLATE_HOLD, "hold", "Hold" }, +}; + +int Interpolator_InterpolatorForName( char const *name ) +{ + for ( int i = 0; i < NUM_INTERPOLATE_TYPES; ++i ) + { + InterpolatorNameMap_t *slot = &g_InterpolatorNameMap[ i ]; + if ( !Q_stricmp( name, slot->name ) ) + return slot->type; + } + + Assert( !"Interpolator_InterpolatorForName failed!!!" ); + return INTERPOLATE_DEFAULT; +} + +char const *Interpolator_NameForInterpolator( int type, bool printname ) +{ + int i = (int)type; + int c = ARRAYSIZE( g_InterpolatorNameMap ); + if ( i < 0 || i >= c ) + { + Assert( "!Interpolator_NameForInterpolator: bogus type!" ); + // returns "unspecified!!!"; + return printname ? g_InterpolatorNameMap[ 0 ].printname : g_InterpolatorNameMap[ 0 ].name; + } + + return printname ? g_InterpolatorNameMap[ i ].printname : g_InterpolatorNameMap[ i ].name; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +struct CurveNameMap_t +{ + int type; + int hotkey; +}; + +static CurveNameMap_t g_CurveNameMap[] = +{ + { CURVE_CATMULL_ROM_TO_CATMULL_ROM, '1' }, + { CURVE_EASE_IN_TO_EASE_OUT, '2' }, + { CURVE_EASE_IN_TO_EASE_IN, '3' }, + { CURVE_EASE_OUT_TO_EASE_OUT, '4' }, + { CURVE_BSPLINE_TO_BSPLINE, '5' }, + { CURVE_LINEAR_INTERP_TO_LINEAR_INTERP, '6' }, + { CURVE_KOCHANEK_BARTELS_TO_KOCHANEK_BARTELS, '7' }, + { CURVE_KOCHANEK_BARTELS_EARLY_TO_KOCHANEK_BARTELS_EARLY, '8' }, + { CURVE_KOCHANEK_BARTELS_LATE_TO_KOCHANEK_BARTELS_LATE, '9' }, + { CURVE_SIMPLE_CUBIC_TO_SIMPLE_CUBIC, '0' }, +}; + +// Turn enum into string and vice versa +int Interpolator_CurveTypeForName( const char *name ) +{ + char sz[ 128 ]; + Q_strncpy( sz, name, sizeof( sz ) ); + + int leftcurve = 0; + int rightcurve = 0; + + int skip = Q_strlen( "curve_" ); + + if ( !Q_strnicmp( sz, "curve_", skip ) ) + { + char *p = sz + skip; + char *second = Q_stristr( p, "_to_curve_" ); + + char save = *second; + *second = 0; + + leftcurve = Interpolator_InterpolatorForName( p ); + + *second = save; + + p = second + Q_strlen( "_to_curve_" ); + + rightcurve = Interpolator_InterpolatorForName( p ); + } + + return MAKE_CURVE_TYPE( leftcurve, rightcurve ); +} + +const char *Interpolator_NameForCurveType( int type, bool printname ) +{ + static char outname[ 256 ]; + + int leftside = GET_LEFT_CURVE( type ); + int rightside = GET_RIGHT_CURVE( type ); + + if ( !printname ) + { + Q_snprintf( outname, sizeof( outname ), "curve_%s_to_curve_%s", + Interpolator_NameForInterpolator( leftside, printname ), + Interpolator_NameForInterpolator( rightside, printname ) ); + } + else + { + Q_snprintf( outname, sizeof( outname ), "%s <-> %s", + Interpolator_NameForInterpolator( leftside, printname ), + Interpolator_NameForInterpolator( rightside, printname ) ); + } + + return outname; +} + +void Interpolator_CurveInterpolatorsForType( int type, int& inbound, int& outbound ) +{ + inbound = GET_LEFT_CURVE( type ); + outbound = GET_RIGHT_CURVE( type ); +} + +int Interpolator_CurveTypeForHotkey( int key ) +{ + int c = ARRAYSIZE( g_CurveNameMap ); + for ( int i = 0; i < c; ++i ) + { + CurveNameMap_t *slot = &g_CurveNameMap[ i ]; + if ( slot->hotkey == key ) + return slot->type; + } + + return -1; +} + +void Interpolator_GetKochanekBartelsParams( int interpolationType, float& tension, float& bias, float& continuity ) +{ + switch ( interpolationType ) + { + default: + tension = 0.0f; + bias = 0.0f; + continuity = 0.0f; + Assert( 0 ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + tension = 0.77f; + bias = 0.0f; + continuity = 0.77f; + break; + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + tension = 0.77f; + bias = -1.0f; + continuity = 0.77f; + break; + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + tension = 0.77f; + bias = 1.0f; + continuity = 0.77f; + break; + } +} + +void Interpolator_CurveInterpolate( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ) +{ + vOut.Init(); + + switch ( interpolationType ) + { + default: + Warning( "Unknown interpolation type %d\n", + (int)interpolationType ); + // break; // Fall through and use catmull_rom as default + case INTERPOLATE_DEFAULT: + case INTERPOLATE_CATMULL_ROM_NORMALIZEX: + Catmull_Rom_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM: + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM_NORMALIZE: + Catmull_Rom_Spline_Normalize( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_CATMULL_ROM_TANGENT: + Catmull_Rom_Spline_Tangent( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EASE_IN: + { + f = sin( M_PI * f * 0.5f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_OUT: + { + f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_INOUT: + { + f = SimpleSpline( f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_LINEAR_INTERP: + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + { + float t, b, c; + Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); + Kochanek_Bartels_Spline_NormalizeX + ( + t, b, c, + vPre, + vStart, + vEnd, + vNext, + f, + vOut + ); + } + break; + case INTERPOLATE_SIMPLE_CUBIC: + Cubic_Spline_NormalizeX( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_BSPLINE: + BSpline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EXPONENTIAL_DECAY: + { + float dt = vEnd.x - vStart.x; + if ( dt > 0.0f ) + { + float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); + vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); + } + else + { + vOut.y = vStart.y; + } + } + break; + case INTERPOLATE_HOLD: + { + vOut.y = vStart.y; + } + break; + } +} + +void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ) +{ + vOut.Init(); + + switch ( interpolationType ) + { + default: + Warning( "Unknown interpolation type %d\n", + (int)interpolationType ); + // break; // Fall through and use catmull_rom as default + case INTERPOLATE_CATMULL_ROM_NORMALIZEX: + case INTERPOLATE_DEFAULT: + case INTERPOLATE_CATMULL_ROM: + case INTERPOLATE_CATMULL_ROM_NORMALIZE: + case INTERPOLATE_CATMULL_ROM_TANGENT: + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EASE_IN: + { + f = sin( M_PI * f * 0.5f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_OUT: + { + f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_INOUT: + { + f = SimpleSpline( f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_LINEAR_INTERP: + // Fixme, since this ignores vPre and vNext we could omit computing them aove + VectorLerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_KOCHANEK_BARTELS: + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + { + float t, b, c; + Interpolator_GetKochanekBartelsParams( interpolationType, t, b, c ); + Kochanek_Bartels_Spline + ( + t, b, c, + vPre, + vStart, + vEnd, + vNext, + f, + vOut + ); + } + break; + case INTERPOLATE_SIMPLE_CUBIC: + Cubic_Spline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_BSPLINE: + BSpline( + vPre, + vStart, + vEnd, + vNext, + f, + vOut ); + break; + case INTERPOLATE_EXPONENTIAL_DECAY: + { + float dt = vEnd.x - vStart.x; + if ( dt > 0.0f ) + { + float val = 1.0f - ExponentialDecay( 0.001, dt, f * dt ); + vOut.y = vStart.y + val * ( vEnd.y - vStart.y ); + } + else + { + vOut.y = vStart.y; + } + } + break; + case INTERPOLATE_HOLD: + { + vOut.y = vStart.y; + } + break; + } +} + + +void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, + const Quaternion &vPre, + const Quaternion &vStart, + const Quaternion &vEnd, + const Quaternion &vNext, + float f, + Quaternion &vOut ) +{ + vOut.Init(); + + switch ( interpolationType ) + { + default: + Warning( "Unknown interpolation type %d\n", + (int)interpolationType ); + // break; // Fall through and use catmull_rom as default + case INTERPOLATE_CATMULL_ROM_NORMALIZEX: + case INTERPOLATE_DEFAULT: + case INTERPOLATE_CATMULL_ROM: + case INTERPOLATE_CATMULL_ROM_NORMALIZE: + case INTERPOLATE_CATMULL_ROM_TANGENT: + case INTERPOLATE_KOCHANEK_BARTELS: + case INTERPOLATE_KOCHANEK_BARTELS_EARLY: + case INTERPOLATE_KOCHANEK_BARTELS_LATE: + case INTERPOLATE_SIMPLE_CUBIC: + case INTERPOLATE_BSPLINE: + // FIXME, since this ignores vPre and vNext we could omit computing them aove + QuaternionSlerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_EASE_IN: + { + f = sin( M_PI * f * 0.5f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + QuaternionSlerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_OUT: + { + f = 1.0f - sin( M_PI * f * 0.5f + 0.5f * M_PI ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + QuaternionSlerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_EASE_INOUT: + { + f = SimpleSpline( f ); + // Fixme, since this ignores vPre and vNext we could omit computing them aove + QuaternionSlerp( vStart, vEnd, f, vOut ); + } + break; + case INTERPOLATE_LINEAR_INTERP: + // Fixme, since this ignores vPre and vNext we could omit computing them aove + QuaternionSlerp( vStart, vEnd, f, vOut ); + break; + case INTERPOLATE_EXPONENTIAL_DECAY: + vOut.Init(); + break; + case INTERPOLATE_HOLD: + { + vOut = vStart; + } + break; + } +} diff --git a/public/interpolatortypes.h b/public/interpolatortypes.h new file mode 100644 index 0000000..123f673 --- /dev/null +++ b/public/interpolatortypes.h @@ -0,0 +1,102 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef INTERPOLATORTYPES_H +#define INTERPOLATORTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +class Quaternion; + +enum +{ + INTERPOLATE_DEFAULT = 0, + INTERPOLATE_CATMULL_ROM_NORMALIZEX, + INTERPOLATE_EASE_IN, + INTERPOLATE_EASE_OUT, + INTERPOLATE_EASE_INOUT, + INTERPOLATE_BSPLINE, + INTERPOLATE_LINEAR_INTERP, + INTERPOLATE_KOCHANEK_BARTELS, + INTERPOLATE_KOCHANEK_BARTELS_EARLY, + INTERPOLATE_KOCHANEK_BARTELS_LATE, + INTERPOLATE_SIMPLE_CUBIC, + + INTERPOLATE_CATMULL_ROM, + INTERPOLATE_CATMULL_ROM_NORMALIZE, + INTERPOLATE_CATMULL_ROM_TANGENT, + + INTERPOLATE_EXPONENTIAL_DECAY, + + INTERPOLATE_HOLD, + + NUM_INTERPOLATE_TYPES, +}; + +#define MAKE_CURVE_TYPE( left, right ) ( ( right ) & 0xff ) | ( ( ( left ) & 0xff ) << 8 ) + +#define GET_RIGHT_CURVE(w) ( ( w ) & 0xff ) +#define GET_LEFT_CURVE(w) ( ( ( w ) >> 8) & 0xff ) + +// Presets used by faceposer +enum +{ + CURVE_DEFAULT = MAKE_CURVE_TYPE( INTERPOLATE_DEFAULT, INTERPOLATE_DEFAULT ), + CURVE_CATMULL_ROM_TO_CATMULL_ROM = MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX , INTERPOLATE_CATMULL_ROM_NORMALIZEX ), // hotkey 1 + CURVE_EASE_IN_TO_EASE_OUT = MAKE_CURVE_TYPE( INTERPOLATE_EASE_IN, INTERPOLATE_EASE_OUT ), // hotkey 2 + CURVE_EASE_IN_TO_EASE_IN = MAKE_CURVE_TYPE( INTERPOLATE_EASE_IN, INTERPOLATE_EASE_IN ), + CURVE_EASE_OUT_TO_EASE_OUT = MAKE_CURVE_TYPE( INTERPOLATE_EASE_OUT, INTERPOLATE_EASE_OUT ), + CURVE_BSPLINE_TO_BSPLINE = MAKE_CURVE_TYPE( INTERPOLATE_BSPLINE, INTERPOLATE_BSPLINE ), + CURVE_LINEAR_INTERP_TO_LINEAR_INTERP = MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_LINEAR_INTERP ), + CURVE_KOCHANEK_BARTELS_TO_KOCHANEK_BARTELS = MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS, INTERPOLATE_KOCHANEK_BARTELS ), + CURVE_KOCHANEK_BARTELS_EARLY_TO_KOCHANEK_BARTELS_EARLY = MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_EARLY, INTERPOLATE_KOCHANEK_BARTELS_EARLY ), + CURVE_KOCHANEK_BARTELS_LATE_TO_KOCHANEK_BARTELS_LATE = MAKE_CURVE_TYPE( INTERPOLATE_KOCHANEK_BARTELS_LATE, INTERPOLATE_KOCHANEK_BARTELS_LATE ), + CURVE_SIMPLE_CUBIC_TO_SIMPLE_CUBIC = MAKE_CURVE_TYPE( INTERPOLATE_SIMPLE_CUBIC, INTERPOLATE_SIMPLE_CUBIC ), + CURVE_LINEAR_TO_HOLD = MAKE_CURVE_TYPE( INTERPOLATE_LINEAR_INTERP, INTERPOLATE_HOLD ), + CURVE_HOLD_TO_LINEAR = MAKE_CURVE_TYPE( INTERPOLATE_HOLD, INTERPOLATE_LINEAR_INTERP ), +}; + +// Turn enum into string and vice versa +int Interpolator_CurveTypeForName( const char *name ); +const char *Interpolator_NameForCurveType( int type, bool printname ); +void Interpolator_CurveInterpolatorsForType( int type, int& inbound, int& outbound ); +int Interpolator_CurveTypeForHotkey( int key ); + +int Interpolator_InterpolatorForName( char const *name ); +char const *Interpolator_NameForInterpolator( int type, bool printname ); + +void Interpolator_GetKochanekBartelsParams( int interpolatorType, float& tension, float& bias, float& continuity ); + +class Vector; +// Main spline interpolation function, assumes .x holds time and .y holds one dimensional value +void Interpolator_CurveInterpolate( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ); + +// Main spline interpolation function for Vectors, doesn't assume time is in .x and doesn't do normalization +void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, + const Vector &vPre, + const Vector &vStart, + const Vector &vEnd, + const Vector &vNext, + float f, + Vector &vOut ); + +// Main spline interpolation function for Vectors, doesn't assume time is in .x and doesn't do normalization +void Interpolator_CurveInterpolate_NonNormalized( int interpolationType, + const Quaternion &vPre, + const Quaternion &vStart, + const Quaternion &vEnd, + const Quaternion &vNext, + float f, + Quaternion &vOut ); + +#endif // INTERPOLATORTYPES_H diff --git a/public/iprediction.h b/public/iprediction.h new file mode 100644 index 0000000..8f554a2 --- /dev/null +++ b/public/iprediction.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( IPREDICTION_H ) +#define IPREDICTION_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "interface.h" +#include "mathlib/vector.h" // Solely to get at define for QAngle + + +class IMoveHelper; + +//----------------------------------------------------------------------------- +// Purpose: Engine interface into client side prediction system +//----------------------------------------------------------------------------- +abstract_class IPrediction +{ +public: + virtual ~IPrediction( void ) {}; + + virtual void Init( void ) = 0; + virtual void Shutdown( void ) = 0; + + // Run prediction + virtual void Update + ( + int startframe, // World update ( un-modded ) most recently received + bool validframe, // Is frame data valid + int incoming_acknowledged, // Last command acknowledged to have been run by server (un-modded) + int outgoing_command // Last command (most recent) sent to server (un-modded) + ) = 0; + + // We are about to get a network update from the server. We know the update #, so we can pull any + // data purely predicted on the client side and transfer it to the new from data state. + virtual void PreEntityPacketReceived( int commands_acknowledged, int current_world_update_packet ) = 0; + virtual void PostEntityPacketReceived( void ) = 0; + virtual void PostNetworkDataReceived( int commands_acknowledged ) = 0; + + virtual void OnReceivedUncompressedPacket( void ) = 0; + + // The engine needs to be able to access a few predicted values + virtual void GetViewOrigin( Vector& org ) = 0; + virtual void SetViewOrigin( Vector& org ) = 0; + virtual void GetViewAngles( QAngle& ang ) = 0; + virtual void SetViewAngles( QAngle& ang ) = 0; + virtual void GetLocalViewAngles( QAngle& ang ) = 0; + virtual void SetLocalViewAngles( QAngle& ang ) = 0; +}; + +extern IPrediction *g_pClientSidePrediction; + +#define VCLIENT_PREDICTION_INTERFACE_VERSION "VClientPrediction001" + +#endif // IPREDICTION_H + diff --git a/public/irecipientfilter.h b/public/irecipientfilter.h new file mode 100644 index 0000000..ad5041c --- /dev/null +++ b/public/irecipientfilter.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IRECIPIENTFILTER_H +#define IRECIPIENTFILTER_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Generic interface for routing messages to users +//----------------------------------------------------------------------------- +class IRecipientFilter +{ +public: + virtual ~IRecipientFilter() {} + + virtual bool IsReliable( void ) const = 0; + virtual bool IsInitMessage( void ) const = 0; + + virtual int GetRecipientCount( void ) const = 0; + virtual int GetRecipientIndex( int slot ) const = 0; +}; + +#endif // IRECIPIENTFILTER_H diff --git a/public/iregistry.h b/public/iregistry.h new file mode 100644 index 0000000..da9ce8d --- /dev/null +++ b/public/iregistry.h @@ -0,0 +1,48 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// +#if !defined( UTIL_REGISTRY_H ) +#define UTIL_REGISTRY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// Purpose: Interface to registry +//----------------------------------------------------------------------------- +abstract_class IRegistry +{ +public: + // Init/shutdown + virtual bool Init( const char *platformName ) = 0; + virtual void Shutdown( void ) = 0; + + // Read/write integers + virtual int ReadInt( const char *key, int defaultValue = 0 ) = 0; + virtual void WriteInt( const char *key, int value ) = 0; + + // Read/write strings + virtual const char *ReadString( const char *key, const char *defaultValue = 0 ) = 0; + virtual void WriteString( const char *key, const char *value ) = 0; + + // Read/write helper methods + virtual int ReadInt( const char *pKeyBase, const char *pKey, int defaultValue = 0 ) = 0; + virtual void WriteInt( const char *pKeyBase, const char *key, int value ) = 0; + virtual const char *ReadString( const char *pKeyBase, const char *key, const char *defaultValue ) = 0; + virtual void WriteString( const char *pKeyBase, const char *key, const char *value ) = 0; +}; + +extern IRegistry *registry; + +// Creates it and calls Init +IRegistry *InstanceRegistry( char const *subDirectoryUnderValve ); +// Calls Shutdown and deletes it +void ReleaseInstancedRegistry( IRegistry *reg ); + +#endif // UTIL_REGISTRY_H diff --git a/public/ireplay.h b/public/ireplay.h new file mode 100644 index 0000000..ecf720f --- /dev/null +++ b/public/ireplay.h @@ -0,0 +1,41 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef IREPLAY_H +#define IREPLAY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +class IServer; +class IReplayDirector; +class IGameEvent; +struct netadr_s; + +//----------------------------------------------------------------------------- +// Interface the Replay module exposes to the engine +//----------------------------------------------------------------------------- +#define INTERFACEVERSION_REPLAYSERVER "ReplayServer001" + +class IReplayServer : public IBaseInterface +{ +public: + virtual ~IReplayServer() {} + + virtual IServer *GetBaseServer( void ) = 0; // get Replay base server interface + virtual IReplayDirector *GetDirector( void ) = 0; // get director interface + virtual int GetReplaySlot( void ) = 0; // return entity index-1 of Replay in game + virtual float GetOnlineTime( void ) = 0; // seconds since broadcast started + + virtual void BroadcastEvent(IGameEvent *event) = 0; // send a director command to all specs +}; + +#endif diff --git a/public/ireplaydirector.h b/public/ireplaydirector.h new file mode 100644 index 0000000..15322da --- /dev/null +++ b/public/ireplaydirector.h @@ -0,0 +1,37 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef IREPLAYDIRECTOR_H +#define IREPLAYDIRECTOR_H +#ifdef _WIN32 +#pragma once +#endif + +class IReplayServer; +class KeyValues; +class Vector; + +#define INTERFACEVERSION_REPLAYDIRECTOR "ReplayDirector001" + +class IReplayDirector +{ +public: + virtual ~IReplayDirector() {} + + virtual bool IsActive( void ) = 0; // true if director is active + + virtual void SetReplayServer( IReplayServer *Replay ) = 0; // give the director the engine Replay interface + virtual IReplayServer* GetReplayServer( void ) = 0; // get current Replay server interface + + virtual int GetDirectorTick( void ) = 0; // get current broadcast tick from director + virtual int GetPVSEntity( void ) = 0; // get current view entity (PVS), 0 if coords are used + virtual Vector GetPVSOrigin( void ) = 0; // get current PVS origin + virtual float GetDelay( void ) = 0; // returns current delay in seconds + + virtual const char** GetModEvents() = 0; +}; + +#endif // IREPLAYDIRECTOR_H diff --git a/public/isaverestore.h b/public/isaverestore.h new file mode 100644 index 0000000..63e51d5 --- /dev/null +++ b/public/isaverestore.h @@ -0,0 +1,392 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISAVERESTORE_H +#define ISAVERESTORE_H + +#include "string_t.h" +#include "datamap.h" +#include "mathlib/vmatrix.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifndef CLIENT_DLL +class SINGLE_INHERITANCE CBaseEntity; +#endif + +class Vector; +class VMatrix; +struct edict_t; +template< class T > class CHandle; +typedef CHandle EHANDLE; +struct matrix3x4_t; + +class CSaveRestoreData; +class CGameSaveRestoreInfo; + +class ISave; +class IRestore; + +//----------------------------------------------------------------------------- + +#pragma pack(push,1) + +struct SaveRestoreRecordHeader_t +{ + unsigned short size; + unsigned short symbol; +}; + + +#pragma pack(pop) + +//----------------------------------------------------------------------------- +// +// ISaveRestoreBlockHandler +// +//----------------------------------------------------------------------------- + +const int MAX_BLOCK_NAME_LEN = 31; +const int SIZE_BLOCK_NAME_BUF = 31 + 1; + +//------------------------------------- + +abstract_class ISaveRestoreBlockHandler +{ +public: + virtual const char *GetBlockName() = 0; + + virtual void PreSave( CSaveRestoreData * ) = 0; // Called immediately prior to save, generally used to set up any necessary tables + virtual void Save( ISave * ) = 0; + virtual void WriteSaveHeaders( ISave * ) = 0; // Called after save to allow the writing out of any dictionaries/tables/indexes generated during save + virtual void PostSave() = 0; + + virtual void PreRestore() = 0; + virtual void ReadRestoreHeaders( IRestore * ) = 0; // Called prior to Restore() + virtual void Restore( IRestore *, bool fCreatePlayers ) = 0; + virtual void PostRestore() = 0; +}; + +//------------------------------------- + +abstract_class ISaveRestoreBlockSet : public ISaveRestoreBlockHandler +{ +public: + virtual void AddBlockHandler( ISaveRestoreBlockHandler *pHandler ) = 0; + virtual void RemoveBlockHandler( ISaveRestoreBlockHandler *pHandler ) = 0; + virtual void CallBlockHandlerRestore( ISaveRestoreBlockHandler *pHandler, int baseFilePos, IRestore *pRestore, bool fCreatePlayers ) = 0; +}; + +extern ISaveRestoreBlockSet *g_pGameSaveRestoreBlockSet; + +//------------------------------------- + +abstract_class CDefSaveRestoreBlockHandler : public ISaveRestoreBlockHandler +{ + virtual const char *GetBlockName() = 0; + + virtual void PreSave( CSaveRestoreData * ) {} + virtual void Save( ISave * ) {} + virtual void WriteSaveHeaders( ISave * ) {} + virtual void PostSave() {} + + virtual void PreRestore() {} + virtual void ReadRestoreHeaders( IRestore * ) {} + virtual void Restore( IRestore *, bool fCreatePlayers ) {} + virtual void PostRestore() {} +}; + +//----------------------------------------------------------------------------- +// +// ISave +// +//----------------------------------------------------------------------------- + +abstract_class ISave +{ +public: + + //--------------------------------- + // Logging + virtual void StartLogging( const char *pszLogName ) = 0; + virtual void EndLogging( void ) = 0; + + //--------------------------------- + virtual bool IsAsync() = 0; + + //--------------------------------- + + virtual int GetWritePos() const = 0; + virtual void SetWritePos(int pos) = 0; + + //--------------------------------- + // Datamap based writing + // + + virtual int WriteAll( const void *pLeafObject, datamap_t *pLeafMap ) = 0; + virtual int WriteFields( const char *pname, const void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount ) = 0; + + template + int WriteAll( const T *pLeafObject ) + { + return WriteAll( pLeafObject, &pLeafObject->m_DataMap ); + } + + //--------------------------------- + // Block support + // + // Using these, one doesn't have to worry about queuing up the read pointer on restore if only + // a subset of the data is read + // + + virtual void StartBlock( const char *pszBlockName ) = 0; + virtual void StartBlock() = 0; + virtual void EndBlock() = 0; + + //--------------------------------- + // Primitive types + // + + virtual void WriteShort( const short *value, int count = 1 ) = 0; + virtual void WriteInt( const int *value, int count = 1 ) = 0; // Save an int + inline void WriteInt( const unsigned *value, int count = 1 ) { WriteInt( (int *)value, count ); } + virtual void WriteBool( const bool *value, int count = 1 ) = 0; // Save a bool + virtual void WriteFloat( const float *value, int count = 1 ) = 0; // Save a float + virtual void WriteData( const char *pdata, int size ) = 0; // Save a binary data block + virtual void WriteString( const char *pstring ) = 0; // Save a null-terminated string + virtual void WriteString( const string_t *stringId, int count = 1 ) = 0; // Save a null-terminated string (engine string) + virtual void WriteVector( const Vector &value ) = 0; // Save a vector + virtual void WriteVector( const Vector *value, int count = 1 ) = 0; // Save a vector array + virtual void WriteQuaternion( const Quaternion &value ) = 0; // Save a Quaternion + virtual void WriteQuaternion( const Quaternion *value, int count = 1 ) = 0; // Save a Quaternion array + + // Note: All of the following will write out both a header and the data. On restore, + // this needs to be cracked + virtual void WriteShort( const char *pname, const short *value, int count = 1 ) = 0; + virtual void WriteInt( const char *pname, const int *value, int count = 1 ) = 0; // Save an int + virtual void WriteBool( const char *pname, const bool *value, int count = 1 ) = 0; // Save a bool + virtual void WriteFloat( const char *pname, const float *value, int count = 1 ) = 0; // Save a float + virtual void WriteData( const char *pname, int size, const char *pdata ) = 0; // Save a binary data block + virtual void WriteString( const char *pname, const char *pstring ) = 0; // Save a null-terminated string + virtual void WriteString( const char *pname, const string_t *stringId, int count = 1 ) = 0; // Save a null-terminated string (engine string) + virtual void WriteVector( const char *pname, const Vector &value ) = 0; // Save a vector + virtual void WriteVector( const char *pname, const Vector *value, int count = 1 ) = 0; // Save a vector array + virtual void WriteQuaternion( const char *pname, const Quaternion &value ) = 0; // Save a Quaternion + virtual void WriteQuaternion( const char *pname, const Quaternion *value, int count = 1 ) = 0; // Save a Quaternion array + + //--------------------------------- + // Game types + // + + virtual void WriteTime( const char *pname, const float *value, int count = 1 ) = 0; // Save a float (timevalue) + virtual void WriteTick( const char *pname, const int *value, int count = 1 ) = 0; // Save a tick (timevalue) + virtual void WritePositionVector( const char *pname, const Vector &value ) = 0; // Offset for landmark if necessary + virtual void WritePositionVector( const char *pname, const Vector *value, int count = 1 ) = 0; // array of pos vectors + virtual void WriteFunction( datamap_t *pMap, const char *pname, const int *value, int count = 1 ) = 0; // Save a function pointer + + virtual void WriteTime( const float *value, int count = 1 ) = 0; // Save a float (timevalue) + virtual void WriteTick( const int *value, int count = 1 ) = 0; // Save a tick (timevalue) + virtual void WritePositionVector( const Vector &value ) = 0; // Offset for landmark if necessary + virtual void WritePositionVector( const Vector *value, int count = 1 ) = 0; // array of pos vectors + + virtual void WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count = 1 ) = 0; + virtual void WriteEdictPtr( const char *pname, edict_t **ppEdict, int count = 1 ) = 0; + virtual void WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count = 1 ) = 0; + + virtual void WriteEntityPtr( CBaseEntity **ppEntity, int count = 1 ) = 0; + virtual void WriteEdictPtr( edict_t **ppEdict, int count = 1 ) = 0; + virtual void WriteEHandle( const EHANDLE *pEHandle, int count = 1 ) = 0; + + //--------------------------------- + // Back door to support somewhat awkward ownership of game save/restore data + virtual CGameSaveRestoreInfo *GetGameSaveRestoreInfo() = 0; + +protected: + virtual ~ISave() {}; +}; + +//----------------------------------------------------------------------------- +// +// IRestore +// +//----------------------------------------------------------------------------- + +abstract_class IRestore +{ +public: + + //--------------------------------- + + virtual int GetReadPos() const = 0; + virtual void SetReadPos( int pos ) = 0; + + //--------------------------------- + // Datamap based reading + // + + virtual int ReadAll( void *pLeafObject, datamap_t *pLeafMap ) = 0; + + virtual int ReadFields( const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldcount = 1 ) = 0; + virtual void EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldcount = 1 ) = 0; + + template + int ReadAll( T *pLeafObject ) + { + return ReadAll( pLeafObject, &pLeafObject->m_DataMap ); + } + + //--------------------------------- + // Block support + // + + virtual void StartBlock( SaveRestoreRecordHeader_t *pHeader ) = 0; + virtual void StartBlock( char szBlockName[SIZE_BLOCK_NAME_BUF] ) = 0; + virtual void StartBlock() = 0; + virtual void EndBlock() = 0; + + //--------------------------------- + // Field header cracking + // + + virtual void ReadHeader( SaveRestoreRecordHeader_t *pheader ) = 0; + virtual int SkipHeader() = 0; // skips the header, but returns the size of the field + virtual const char *StringFromHeaderSymbol( int symbol ) = 0; + + //--------------------------------- + // Primitive types + // + + virtual short ReadShort( void ) = 0; + virtual int ReadShort( short *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadInt( int *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + inline int ReadInt( unsigned *pValue, int count = 1, int nBytesAvailable = 0 ) { return ReadInt( (int *)pValue, count, nBytesAvailable ); } + virtual int ReadInt( void ) = 0; + virtual int ReadBool( bool *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadFloat( float *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadData( char *pData, int size, int nBytesAvailable ) = 0; + virtual void ReadString( char *pDest, int nSizeDest, int nBytesAvailable ) = 0; // A null-terminated string + virtual int ReadString( string_t *pString, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadVector( Vector *pValue ) = 0; + virtual int ReadVector( Vector *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadQuaternion( Quaternion *pValue ) = 0; + virtual int ReadQuaternion( Quaternion *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + + //--------------------------------- + // Game types + // + + virtual int ReadTime( float *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadTick( int *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadPositionVector( Vector *pValue ) = 0; + virtual int ReadPositionVector( Vector *pValue, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadFunction( datamap_t *pMap, void **pValue, int count = 1, int nBytesAvailable = 0) = 0; + + virtual int ReadEntityPtr( CBaseEntity **ppEntity, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadEdictPtr( edict_t **ppEdict, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadEHandle( EHANDLE *pEHandle, int count = 1, int nBytesAvailable = 0 ) = 0; + virtual int ReadVMatrix( VMatrix *pValue, int count = 1, int nBytesAvailable = 0) = 0; + virtual int ReadVMatrixWorldspace( VMatrix *pValue, int count = 1, int nBytesAvailable = 0) = 0; + virtual int ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems = 1, int nBytesAvailable = 0 ) = 0; + + // Used by Foundry to restore a certain entity's data (with Foundry data) while loading a savegame. + // Returns -1 if not found. + virtual int ScanAheadForHammerID() = 0; + virtual void SkipEntityData() = 0; // This skips the current entity's data. + + //--------------------------------- + + virtual bool GetPrecacheMode( void ) = 0; + + //--------------------------------- + // Back door to support somewhat awkward ownership of game save/restore data + virtual CGameSaveRestoreInfo *GetGameSaveRestoreInfo() = 0; + +protected: + virtual ~IRestore() {}; +}; + +//----------------------------------------------------------------------------- +// Purpose: The operations necessary to save and restore custom types (FIELD_CUSTOM) +// +// + +struct SaveRestoreFieldInfo_t +{ + void * pField; + + // Note that it is legal for the following two fields to be NULL, + // though it may be disallowed by implementors of ISaveRestoreOps + void * pOwner; + typedescription_t *pTypeDesc; +}; + +abstract_class ISaveRestoreOps +{ +public: + // save data type interface + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) = 0; + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) = 0; + + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) = 0; + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) = 0; + virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue ) = 0; + + //--------------------------------- + + void Save( void *pField, ISave *pSave ) { SaveRestoreFieldInfo_t fieldInfo = { pField, NULL, NULL }; Save( fieldInfo, pSave ); } + void Restore( void *pField, IRestore *pRestore ) { SaveRestoreFieldInfo_t fieldInfo = { pField, NULL, NULL }; Restore( fieldInfo, pRestore ); } + + bool IsEmpty( void *pField) { SaveRestoreFieldInfo_t fieldInfo = { pField, NULL, NULL }; return IsEmpty( fieldInfo ); } + void MakeEmpty( void *pField) { SaveRestoreFieldInfo_t fieldInfo = { pField, NULL, NULL }; MakeEmpty( fieldInfo ); } + bool Parse( void *pField, char const *pszValue ) { SaveRestoreFieldInfo_t fieldInfo = { pField, NULL, NULL }; return Parse( fieldInfo, pszValue ); } +}; + +//------------------------------------- + +class CDefSaveRestoreOps : public ISaveRestoreOps +{ +public: + // save data type interface + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) {} + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) {} + + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) { return false; } + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) {} + virtual bool Parse( const SaveRestoreFieldInfo_t &fieldInfo, char const* szValue ) { return false; } +}; + + +//----------------------------------------------------------------------------- +// Used by ops that deal with pointers +//----------------------------------------------------------------------------- +class CClassPtrSaveRestoreOps : public CDefSaveRestoreOps +{ +public: + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + void **ppClassPtr = (void **)fieldInfo.pField; + int nObjects = fieldInfo.pTypeDesc->fieldSize; + for ( int i = 0; i < nObjects; i++ ) + { + if ( ppClassPtr[i] != NULL ) + return false; + } + return true; + } + + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + memset( fieldInfo.pField, 0, fieldInfo.pTypeDesc->fieldSize * sizeof( void * ) ); + } +}; + + +//============================================================================= + +#endif // ISAVERESTORE_H diff --git a/public/iscratchpad3d.h b/public/iscratchpad3d.h new file mode 100644 index 0000000..69ee7aa --- /dev/null +++ b/public/iscratchpad3d.h @@ -0,0 +1,326 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISCRATCHPAD3D_H +#define ISCRATCHPAD3D_H +#ifdef _WIN32 +#pragma once +#endif + + +// IScratchPad3D will store drawing commands in a file to be viewed by ScratchPad3DViewer. +// It can be used while stepping through geometry code to visualize what is going on as +// drawing commands will be immediately visible in ScratchPad3DViewer even while you're stuck +// in the debugger + +// ScratchPad3DViewer initially orbits 100 inches from the origin, so it can be useful +// to call SetMapping to map what you're drawing input into this cube. + + +#include "mathlib/vector.h" +#include "mathlib/vector2d.h" +#include "utlvector.h" + +class IFileSystem; + + +class CSPColor +{ +public: + CSPColor() + { + m_flAlpha = 1; + } + CSPColor( const Vector &vColor, float flAlpha=1 ) + { + m_vColor = vColor; + m_flAlpha = flAlpha; + } + CSPColor( float r, float g, float b, float a=1 ) + { + m_vColor.Init( r, g, b ); + m_flAlpha = a; + } + + Vector m_vColor; + float m_flAlpha; +}; + + +class CSPVert +{ +public: + CSPVert(); + CSPVert( Vector const &vPos, const CSPColor &vColor=CSPColor( Vector(1, 1, 1), 1 ) ); + + void Init( Vector const &vPos, const CSPColor &vColor=CSPColor( Vector(1, 1, 1), 1 ) ); + +public: + Vector m_vPos; + CSPColor m_vColor; +}; + + +class CSPVertList +{ +public: + CSPVertList( int nVerts = 0 ); + CSPVertList(CSPVert const *pVerts, int nVerts); + CSPVertList(Vector const *pVerts, int nVerts, CSPColor vColor=CSPColor(1,1,1) ); + + CSPVertList(Vector const *pVerts, Vector const *pColors, int nVerts); + CSPVertList(Vector const *pVerts, CSPColor const *pColors, int nVerts); + + CSPVertList(Vector const &vert1, CSPColor const &color1, + Vector const &vert2, CSPColor const &color2, + Vector const &vert3, CSPColor const &color3); + + CUtlVector m_Verts; +}; + +class SPRGBA +{ +public: + unsigned char r,g,b,a; +}; + + +class CTextParams +{ +public: + CTextParams(); + + + Vector m_vColor; // Color of the string (starting color.. at some point, + // we can embed commands in the text itself to change the color). + float m_flAlpha; // Alpha of the whole thing. + + bool m_bSolidBackground; // Should the background be solid or alpha'd? + + // Draw an outline around the text? + bool m_bOutline; + + Vector m_vPos; // Where to render the text. + bool m_bCentered; // Centered on m_vPos, or is m_vPos the upper-left corner? + + QAngle m_vAngles; // Orientation of the text. + bool m_bTwoSided; // Render the text from both sides? + + float m_flLetterWidth; // Letter width in world space. +}; + + +abstract_class IScratchPad3D +{ +protected: + + virtual ~IScratchPad3D() {} + + +// Types. +public: + + enum RenderState + { + RS_FillMode=0, // val = one of the FillMode enums + RS_ZRead, + RS_ZBias // val = 0 - 16 to push Z towards viewer + }; + + enum FillMode + { + FillMode_Wireframe=0, + FillMode_Solid + }; + + +public: + + virtual void Release() = 0; + + // This sets up a mapping between input coordinates and output coordinates. + // This can be used to zoom into an area of interest where you'll be drawing things. + // An alternative is to press Z while in VisLibViewer to have it center and zoom on + // everything that has been drawn. + virtual void SetMapping( + Vector const &vInputMin, + Vector const &vInputMax, + Vector const &vOutputMin, + Vector const &vOutputMax ) = 0; + + // Enable/disable auto flush. When set to true (the default), all drawing commands + // are immediately written to the file and will show up in VisLibViewer right away. + // If you want to draw a lot of things, you can set this to false and call Flush() + // manually when you want the file written out. + // When you set auto flush to true, it calls Flush(). + virtual bool GetAutoFlush() = 0; + virtual void SetAutoFlush( bool bAutoFlush ) = 0; + + // Draw a point. Point size is (roughly) in world coordinates, so points + // get smaller as the viewer moves away. + virtual void DrawPoint( CSPVert const &v, float flPointSize ) = 0; + + // Draw a line. + virtual void DrawLine( CSPVert const &v1, CSPVert const &v2 ) = 0; + + // Draw a polygon. + virtual void DrawPolygon( CSPVertList const &verts ) = 0; + + // Draw 2D rectangles. + virtual void DrawRectYZ( float xPos, Vector2D const &vMin, Vector2D const &vMax, const CSPColor &vColor ) = 0; + virtual void DrawRectXZ( float yPos, Vector2D const &vMin, Vector2D const &vMax, const CSPColor &vColor ) = 0; + virtual void DrawRectXY( float zPos, Vector2D const &vMin, Vector2D const &vMax, const CSPColor &vColor ) = 0; + + // Draw a wireframe box. + virtual void DrawWireframeBox( Vector const &vMin, Vector const &vMax, Vector const &vColor ) = 0; + + // Draw some text. + virtual void DrawText( const char *pStr, const CTextParams ¶ms ) = 0; + + // Wireframe on/off. + virtual void SetRenderState( RenderState state, unsigned long val ) = 0; + + // Clear all the drawing commands. + virtual void Clear() = 0; + + // Calling this writes all the commands to the file. If AutoFlush is true, this is called + // automatically in all the drawing commands. + virtual void Flush() = 0; + + +// Primitives that build on the atomic primitives. +public: + + // Draw a black and white image. + // Corners are in this order: bottom-left, top-left, top-right, bottom-right. + // If the corners are NULL, then the image is drawn in the XY plane from (-100,-100) to (100,100). + virtual void DrawImageBW( + unsigned char const *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels=true, + bool bOutlineImage=false, + Vector *vCorners=NULL ) = 0; + + // Draw an RGBA image. + // Corners are in this order: bottom-left, top-left, top-right, bottom-right. + virtual void DrawImageRGBA( + SPRGBA *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels=true, + bool bOutlineImage=false, + Vector *vCorners=NULL ) = 0; +}; + + +// Just a helper for functions where you want to have a CScratchPad3D around +// and release it automatically when the function exits. +class CScratchPadAutoRelease +{ +public: + CScratchPadAutoRelease( IScratchPad3D *pPad ) { m_pPad = pPad; } + ~CScratchPadAutoRelease() { if( m_pPad ) m_pPad->Release(); } + + IScratchPad3D *m_pPad; +}; + + + +IScratchPad3D* ScratchPad3D_Create( char const *pFilename = "scratch.pad" ); + + + +// ------------------------------------------------------------------------------------ // +// Inlines. +// ------------------------------------------------------------------------------------ // + +inline CTextParams::CTextParams() +{ + m_vColor.Init( 1, 1, 1 ); + m_flAlpha = 1; + m_bSolidBackground = true; + m_bOutline = true; + m_vPos.Init(); + m_bCentered = true; + m_vAngles.Init(); + m_bTwoSided = true; + m_flLetterWidth = 3; +} + +inline CSPVert::CSPVert() +{ +} + +inline CSPVert::CSPVert( Vector const &vPos, const CSPColor &vColor ) +{ + Init( vPos, vColor ); +} + +inline void CSPVert::Init( Vector const &vPos, const CSPColor &vColor ) +{ + m_vPos = vPos; + m_vColor = vColor; +} + + +inline CSPVertList::CSPVertList( int nVerts ) +{ + if( nVerts ) + m_Verts.AddMultipleToTail( nVerts ); +} + +inline CSPVertList::CSPVertList(CSPVert const *pVerts, int nVerts ) +{ + m_Verts.CopyArray( pVerts, nVerts ); +} + +inline CSPVertList::CSPVertList(Vector const *pVerts, int nVerts, CSPColor vColor ) +{ + m_Verts.AddMultipleToTail( nVerts ); + for( int i=0; i < nVerts; i++ ) + { + m_Verts[i].m_vPos = pVerts[i]; + m_Verts[i].m_vColor = vColor; + } +} + +inline CSPVertList::CSPVertList( Vector const *pVerts, Vector const *pColors, int nVerts ) +{ + m_Verts.AddMultipleToTail( nVerts ); + for( int i=0; i < nVerts; i++ ) + { + m_Verts[i].m_vPos = pVerts[i]; + m_Verts[i].m_vColor = pColors[i]; + } +} + +inline CSPVertList::CSPVertList( Vector const *pVerts, CSPColor const *pColors, int nVerts ) +{ + m_Verts.AddMultipleToTail( nVerts ); + for( int i=0; i < nVerts; i++ ) + { + m_Verts[i].m_vPos = pVerts[i]; + m_Verts[i].m_vColor = pColors[i]; + } +} + +inline CSPVertList::CSPVertList( + Vector const &vert1, CSPColor const &color1, + Vector const &vert2, CSPColor const &color2, + Vector const &vert3, CSPColor const &color3 ) +{ + m_Verts.AddMultipleToTail( 3 ); + m_Verts[0].Init( vert1, color1 ); + m_Verts[1].Init( vert2, color2 ); + m_Verts[2].Init( vert3, color3 ); +} + + +#endif // ISCRATCHPAD3D_H diff --git a/public/iserver.h b/public/iserver.h new file mode 100644 index 0000000..e5e56c8 --- /dev/null +++ b/public/iserver.h @@ -0,0 +1,70 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ISERVER_H +#define ISERVER_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +class INetMessage; +class IRecipientFilter; +class IClient; + +typedef struct player_info_s player_info_t; + +abstract_class IServer : public IConnectionlessPacketHandler +{ +public: + virtual ~IServer() {} + + virtual int GetNumClients( void ) const = 0; // returns current number of clients + virtual int GetNumProxies( void ) const = 0; // returns number of attached HLTV proxies + virtual int GetNumFakeClients() const = 0; // returns number of fake clients/bots + virtual int GetMaxClients( void ) const = 0; // returns current client limit + virtual IClient *GetClient( int index ) = 0; // returns interface to client + virtual int GetClientCount() const = 0; // returns number of clients slots (used & unused) + virtual int GetUDPPort( void ) const = 0; // returns current used UDP port + virtual float GetTime( void ) const = 0; // returns game world time + virtual int GetTick( void ) const = 0; // returns game world tick + virtual float GetTickInterval( void ) const = 0; // tick interval in seconds + virtual float GetTimescale( void ) const = 0; // returns the game time scale (multiplied in conjunction with host_timescale) + virtual const char *GetName( void ) const = 0; // public server name + virtual const char *GetMapName( void ) const = 0; // current map name (BSP) + virtual int GetSpawnCount( void ) const = 0; + virtual int GetNumClasses( void ) const = 0; + virtual int GetClassBits( void ) const = 0; + virtual void GetNetStats( float &avgIn, float &avgOut ) = 0; // total net in/out in bytes/sec + virtual int GetNumPlayers() = 0; + virtual bool GetPlayerInfo( int nClientIndex, player_info_t *pinfo ) = 0; + + virtual bool IsActive( void ) const = 0; + virtual bool IsLoading( void ) const = 0; + virtual bool IsDedicated( void ) const = 0; + virtual bool IsPaused( void ) const = 0; + virtual bool IsMultiplayer( void ) const = 0; + virtual bool IsPausable() const = 0; + virtual bool IsHLTV() const = 0; + virtual bool IsReplay() const = 0; + + virtual const char * GetPassword() const = 0; // returns the password or NULL if none set + + virtual void SetPaused(bool paused) = 0; + virtual void SetTimescale( float flTimescale ) = 0; + virtual void SetPassword(const char *password) = 0; // set password (NULL to disable) + + virtual void BroadcastMessage( INetMessage &msg, bool onlyActive = false, bool reliable = false) = 0; + virtual void BroadcastMessage( INetMessage &msg, IRecipientFilter &filter ) = 0; + + virtual void DisconnectClient( IClient *client, const char *reason ) = 0; +}; + + +#endif // ISERVER_H diff --git a/public/iserverentity.h b/public/iserverentity.h new file mode 100644 index 0000000..93b0a49 --- /dev/null +++ b/public/iserverentity.h @@ -0,0 +1,42 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISERVERENTITY_H +#define ISERVERENTITY_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "iserverunknown.h" +#include "string_t.h" + + + +struct Ray_t; +class ServerClass; +class ICollideable; +class IServerNetworkable; +class Vector; +class QAngle; + +// This class is how the engine talks to entities in the game DLL. +// CBaseEntity implements this interface. +class IServerEntity : public IServerUnknown +{ +public: + virtual ~IServerEntity() {} + +// Previously in pev + virtual int GetModelIndex( void ) const = 0; + virtual string_t GetModelName( void ) const = 0; + + virtual void SetModelIndex( int index ) = 0; +}; + + +#endif // ISERVERENTITY_H diff --git a/public/iservernetworkable.h b/public/iservernetworkable.h new file mode 100644 index 0000000..f2752ee --- /dev/null +++ b/public/iservernetworkable.h @@ -0,0 +1,114 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISERVERNETWORKABLE_H +#define ISERVERNETWORKABLE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "ihandleentity.h" +#include "basetypes.h" +#include "bitvec.h" +#include "const.h" +#include "bspfile.h" + + + +// Entities can span this many clusters before we revert to a slower area checking algorithm +#define MAX_FAST_ENT_CLUSTERS 4 +#define MAX_ENT_CLUSTERS 64 +#define MAX_WORLD_AREAS 8 + + +class ServerClass; +class SendTable; +struct edict_t; +class CBaseEntity; +class CSerialEntity; +class CBaseNetworkable; + + +class CCheckTransmitInfo +{ +public: + edict_t *m_pClientEnt; // pointer to receiver edict + byte m_PVS[PAD_NUMBER( MAX_MAP_CLUSTERS,8 ) / 8]; + int m_nPVSSize; // PVS size in bytes + + CBitVec *m_pTransmitEdict; // entity n is already marked for transmission + CBitVec *m_pTransmitAlways; // entity n is always send even if not in PVS (HLTV and Replay only) + + int m_AreasNetworked; // number of networked areas + int m_Areas[MAX_WORLD_AREAS]; // the areas + + // This is used to determine visibility, so if the previous state + // is the same as the current state (along with pvs and areas networked), + // then the parts of the map that the player can see haven't changed. + byte m_AreaFloodNums[MAX_MAP_AREAS]; + int m_nMapAreas; +}; + +//----------------------------------------------------------------------------- +// Stores information necessary to perform PVS testing. +//----------------------------------------------------------------------------- +struct PVSInfo_t +{ + // headnode for the entity's bounding box + short m_nHeadNode; + + // number of clusters or -1 if too many + short m_nClusterCount; + + // cluster indices + unsigned short *m_pClusters; + + // For dynamic "area portals" + short m_nAreaNum; + short m_nAreaNum2; + + // current position + float m_vCenter[3]; + +private: + unsigned short m_pClustersInline[MAX_FAST_ENT_CLUSTERS]; + + friend class CVEngineServer; +}; + + +// IServerNetworkable is the interface the engine uses for all networkable data. +class IServerNetworkable +{ +// These functions are handled automatically by the server_class macros and CBaseNetworkable. +public: + // Gets at the entity handle associated with the collideable + virtual IHandleEntity *GetEntityHandle() = 0; + + // Tell the engine which class this object is. + virtual ServerClass* GetServerClass() = 0; + + virtual edict_t *GetEdict() const = 0; + + virtual const char* GetClassName() const = 0; + virtual void Release() = 0; + + virtual int AreaNum() const = 0; + + // In place of a generic QueryInterface. + virtual CBaseNetworkable* GetBaseNetworkable() = 0; + virtual CBaseEntity* GetBaseEntity() = 0; // Only used by game code. + virtual PVSInfo_t* GetPVSInfo() = 0; // get current visibilty data + +protected: + // Should never call delete on this! + virtual ~IServerNetworkable() {} +}; + + +#endif // ISERVERNETWORKABLE_H diff --git a/public/iserverunknown.h b/public/iserverunknown.h new file mode 100644 index 0000000..7fc47ee --- /dev/null +++ b/public/iserverunknown.h @@ -0,0 +1,35 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISERVERUNKNOWN_H +#define ISERVERUNKNOWN_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "ihandleentity.h" + +class ICollideable; +class IServerNetworkable; +class CBaseEntity; + + +// This is the server's version of IUnknown. We may want to use a QueryInterface-like +// mechanism if this gets big. +class IServerUnknown : public IHandleEntity +{ +public: + // Gets the interface to the collideable + networkable representation of the entity + virtual ICollideable* GetCollideable() = 0; + virtual IServerNetworkable* GetNetworkable() = 0; + virtual CBaseEntity* GetBaseEntity() = 0; +}; + + +#endif // ISERVERUNKNOWN_H diff --git a/public/ishadercompiledll.h b/public/ishadercompiledll.h new file mode 100644 index 0000000..57deefd --- /dev/null +++ b/public/ishadercompiledll.h @@ -0,0 +1,26 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ISHADERCOMPILEDLL_H +#define ISHADERCOMPILEDLL_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "interface.h" + +#define SHADER_COMPILE_INTERFACE_VERSION "shadercompiledll_0" + +// This is the DLL interface to ShaderCompile +abstract_class IShaderCompileDLL +{ +public: + // All vrad.exe does is load the VRAD DLL and run this. + virtual int main( int argc, char **argv ) = 0; +}; + +#endif // ISHADERCOMPILEDLL_H diff --git a/public/isoundcombiner.h b/public/isoundcombiner.h new file mode 100644 index 0000000..db4c564 --- /dev/null +++ b/public/isoundcombiner.h @@ -0,0 +1,40 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ISOUNDCOMBINER_H +#define ISOUNDCOMBINER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" + +class IFileSystem; + +struct CombinerEntry +{ + CombinerEntry() + { + wavefile[ 0 ] = 0; + startoffset = 0.0f; + } + + char wavefile[ MAX_PATH ]; + float startoffset; +}; + +abstract_class ISoundCombiner +{ +public: + virtual ~ISoundCombiner() {} + + virtual bool CombineSoundFiles( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ) = 0; + virtual bool IsCombinedFileChecksumValid( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ) = 0; +}; + +extern ISoundCombiner *soundcombiner; + +#endif // ISOUNDCOMBINER_H diff --git a/public/ispatialpartition.h b/public/ispatialpartition.h new file mode 100644 index 0000000..f95c96b --- /dev/null +++ b/public/ispatialpartition.h @@ -0,0 +1,211 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISPATIALPARTITION_H +#define ISPATIALPARTITION_H + +#include "interface.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- + +class Vector; +struct Ray_t; +class IHandleEntity; + + +#define INTERFACEVERSION_SPATIALPARTITION "SpatialPartition001" + +//----------------------------------------------------------------------------- +// These are the various partition lists. Note some are server only, some +// are client only +//----------------------------------------------------------------------------- + +enum +{ + PARTITION_ENGINE_SOLID_EDICTS = (1 << 0), // every edict_t that isn't SOLID_TRIGGER or SOLID_NOT (and static props) + PARTITION_ENGINE_TRIGGER_EDICTS = (1 << 1), // every edict_t that IS SOLID_TRIGGER + PARTITION_CLIENT_SOLID_EDICTS = (1 << 2), + PARTITION_CLIENT_RESPONSIVE_EDICTS = (1 << 3), // these are client-side only objects that respond to being forces, etc. + PARTITION_ENGINE_NON_STATIC_EDICTS = (1 << 4), // everything in solid & trigger except the static props, includes SOLID_NOTs + PARTITION_CLIENT_STATIC_PROPS = (1 << 5), + PARTITION_ENGINE_STATIC_PROPS = (1 << 6), + PARTITION_CLIENT_NON_STATIC_EDICTS = (1 << 7), // everything except the static props + PARTITION_CLIENT_TRIGGER_ENTITIES = (1 << 8), // client side prediction related triggers + PARTITION_CLIENT_IK_ATTACHMENT = (1 << 9), // Can be used as an IK attachment +}; + +// Use this to look for all client edicts. +#define PARTITION_ALL_CLIENT_EDICTS ( \ + PARTITION_CLIENT_NON_STATIC_EDICTS | \ + PARTITION_CLIENT_STATIC_PROPS | \ + PARTITION_CLIENT_RESPONSIVE_EDICTS | \ + PARTITION_CLIENT_SOLID_EDICTS | \ + PARTITION_CLIENT_TRIGGER_ENTITIES \ + ) + + +// These are the only handles in the spatial partition that the game is controlling (everything but static props) +// These masks are used to handle updating the dirty spatial partition list in each game DLL +#define PARTITION_CLIENT_GAME_EDICTS (PARTITION_ALL_CLIENT_EDICTS & ~PARTITION_CLIENT_STATIC_PROPS) +#define PARTITION_SERVER_GAME_EDICTS (PARTITION_ENGINE_SOLID_EDICTS|PARTITION_ENGINE_TRIGGER_EDICTS|PARTITION_ENGINE_NON_STATIC_EDICTS) + +//----------------------------------------------------------------------------- +// Clients that want to know about all elements within a particular +// volume must inherit from this +//----------------------------------------------------------------------------- + +enum IterationRetval_t +{ + ITERATION_CONTINUE = 0, + ITERATION_STOP, +}; + + +typedef unsigned short SpatialPartitionHandle_t; + +// A combination of the PARTITION_ flags above. +typedef int SpatialPartitionListMask_t; + +typedef int SpatialTempHandle_t; + + +//----------------------------------------------------------------------------- +// Any search in the CSpatialPartition must use this to filter out entities it doesn't want. +// You're forced to use listMasks because it can filter by listMasks really fast. Any other +// filtering can be done by EnumElement. +//----------------------------------------------------------------------------- + +class IPartitionEnumerator +{ +public: + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Installs a callback to call right before a spatial partition query occurs +//----------------------------------------------------------------------------- +class IPartitionQueryCallback +{ +public: + virtual void OnPreQuery( SpatialPartitionListMask_t listMask ) = 0; + virtual void OnPostQuery( SpatialPartitionListMask_t listMask ) = 0; +}; + + +//----------------------------------------------------------------------------- +// This is the spatial partition manager, groups objects into buckets +//----------------------------------------------------------------------------- +enum +{ + PARTITION_INVALID_HANDLE = (SpatialPartitionHandle_t)~0 +}; + + +abstract_class ISpatialPartition +{ +public: + // Create/destroy a handle for this dude in our system. Destroy + // will also remove it from all lists it happens to be in + virtual SpatialPartitionHandle_t CreateHandle( IHandleEntity *pHandleEntity ) = 0; + + // A fast method of creating a handle + inserting into the tree in the right place + virtual SpatialPartitionHandle_t CreateHandle( IHandleEntity *pHandleEntity, + SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs ) = 0; + + virtual void DestroyHandle( SpatialPartitionHandle_t handle ) = 0; + + // Adds, removes an handle from a particular spatial partition list + // There can be multiple partition lists; each has a unique id + virtual void Insert( SpatialPartitionListMask_t listMask, + SpatialPartitionHandle_t handle ) = 0; + virtual void Remove( SpatialPartitionListMask_t listMask, + SpatialPartitionHandle_t handle ) = 0; + + // Same as calling Remove() then Insert(). For performance-sensitive areas where you want to save a call. + virtual void RemoveAndInsert( SpatialPartitionListMask_t removeMask, SpatialPartitionListMask_t insertMask, + SpatialPartitionHandle_t handle ) = 0; + + // This will remove a particular handle from all lists + virtual void Remove( SpatialPartitionHandle_t handle ) = 0; + + // Call this when an entity moves... + virtual void ElementMoved( SpatialPartitionHandle_t handle, + const Vector& mins, const Vector& maxs ) = 0; + + // A fast method to insert + remove a handle from the tree... + // This is used to suppress collision of a single model.. + virtual SpatialTempHandle_t HideElement( SpatialPartitionHandle_t handle ) = 0; + virtual void UnhideElement( SpatialPartitionHandle_t handle, SpatialTempHandle_t tempHandle ) = 0; + + // Installs callbacks to get called right before a query occurs + virtual void InstallQueryCallback( IPartitionQueryCallback *pCallback ) = 0; + virtual void RemoveQueryCallback( IPartitionQueryCallback *pCallback ) = 0; + + // Gets all entities in a particular volume... + // if coarseTest == true, it'll return all elements that are in + // spatial partitions that intersect the box + // if coarseTest == false, it'll return only elements that truly intersect + virtual void EnumerateElementsInBox( + SpatialPartitionListMask_t listMask, + const Vector& mins, + const Vector& maxs, + bool coarseTest, + IPartitionEnumerator* pIterator + ) = 0; + + virtual void EnumerateElementsInSphere( + SpatialPartitionListMask_t listMask, + const Vector& origin, + float radius, + bool coarseTest, + IPartitionEnumerator* pIterator + ) = 0; + + virtual void EnumerateElementsAlongRay( + SpatialPartitionListMask_t listMask, + const Ray_t& ray, + bool coarseTest, + IPartitionEnumerator* pIterator + ) = 0; + + virtual void EnumerateElementsAtPoint( + SpatialPartitionListMask_t listMask, + const Vector& pt, + bool coarseTest, + IPartitionEnumerator* pIterator + ) = 0; + + // For debugging.... suppress queries on particular lists + virtual void SuppressLists( SpatialPartitionListMask_t nListMask, bool bSuppress ) = 0; + virtual SpatialPartitionListMask_t GetSuppressedLists() = 0; + + virtual void RenderAllObjectsInTree( float flTime ) = 0; + virtual void RenderObjectsInPlayerLeafs( const Vector &vecPlayerMin, const Vector &vecPlayerMax, float flTime ) = 0; + virtual void RenderLeafsForRayTraceStart( float flTime ) = 0; + virtual void RenderLeafsForRayTraceEnd( void ) = 0; + virtual void RenderLeafsForHullTraceStart( float flTime ) = 0; + virtual void RenderLeafsForHullTraceEnd( void ) = 0; + virtual void RenderLeafsForBoxStart( float flTime ) = 0; + virtual void RenderLeafsForBoxEnd( void ) = 0; + virtual void RenderLeafsForSphereStart( float flTime ) = 0; + virtual void RenderLeafsForSphereEnd( void ) = 0; + + virtual void RenderObjectsInBox( const Vector &vecMin, const Vector &vecMax, float flTime ) = 0; + virtual void RenderObjectsInSphere( const Vector &vecCenter, float flRadius, float flTime ) = 0; + virtual void RenderObjectsAlongRay( const Ray_t& ray, float flTime ) = 0; + + virtual void ReportStats( const char *pFileName ) = 0; +}; + +#endif + + + diff --git a/public/ispsharedmemory.h b/public/ispsharedmemory.h new file mode 100644 index 0000000..ce074ef --- /dev/null +++ b/public/ispsharedmemory.h @@ -0,0 +1,27 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef ISPSHAREDMEMORY_H +#define ISPSHAREDMEMORY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "platform.h" + +abstract_class ISPSharedMemory +{ +public: + virtual bool Init( size_t iSize ) = 0; //Initial implementation assumes the size is fixed/hardcoded, returns true if this call actually created the memory, false if it already existed + virtual uint8 * Base( void ) = 0; + virtual size_t Size( void ) = 0; + + virtual void AddRef( void ) = 0; + virtual void Release( void ) = 0; +}; + +#endif diff --git a/public/isqlwrapper.h b/public/isqlwrapper.h new file mode 100644 index 0000000..8b240bf --- /dev/null +++ b/public/isqlwrapper.h @@ -0,0 +1,116 @@ +//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============ +// +// Purpose: Sql wrapper, encapsulates basic SQL functionality +// +// $NoKeywords: $ +//============================================================================= +#ifndef ISQLWRAPPER_H +#define ISQLWRAPPER_H + +#include "tier0/platform.h" + +#ifdef _WIN32 +#pragma once +#endif + +// SQL column types +enum EColumnType +{ + SQL_NONE = 0, + SQL_INT, + SQL_STRING, + SQL_FLOAT, + SQL_TIME, + SQL_UINT64 +}; + + +//----------------------------------------------------------------------------- +// encapsulates a table's description +//----------------------------------------------------------------------------- +class ISQLTable +{ +public: + virtual int GetCSQLColumn() const = 0; + virtual const char *PchColumnName( int iSQLColumn ) const = 0; + virtual EColumnType GetEColumnType( int iSQLColumn ) const = 0; + virtual const char *PchName() const = 0; +}; + + +//----------------------------------------------------------------------------- +// encapsulates a database worth of tables +//----------------------------------------------------------------------------- +class ISQLTableSet +{ +public: + virtual int GetCSQLTable() const = 0; + virtual const ISQLTable *PSQLTable( int iSQLTable ) const = 0; +}; + + +//----------------------------------------------------------------------------- +// encapsulates a single returned row from a valid SQL query +//----------------------------------------------------------------------------- +class ISQLRow +{ +public: + virtual int GetCSQLRowData() const = 0; + virtual const char *PchData( int iSQLColumn ) const = 0; + virtual int NData( int iSQLColumn ) const = 0; + virtual uint64 UlData( int iSQLColumn ) const = 0; + virtual float FlData( int iSQLColumn ) const = 0; + virtual uint64 UlTime( int iSQLColumn ) const = 0; + virtual bool BData( int iSQLColumn ) const = 0; + virtual EColumnType GetEColumnType( int iSQLColumn ) const = 0; +}; + + +//----------------------------------------------------------------------------- +// encapsulates a result set, which is made up of a series of rows +// You need to call PNextResult() NRow() times to get all the results +// (there is no random access to the result set). +//----------------------------------------------------------------------------- +class IResultSet +{ +public: + virtual int GetCSQLRow() const = 0; + virtual const ISQLRow *PSQLRowNextResult() = 0; // note, not const on the class because it makes a new row object +}; + + +//----------------------------------------------------------------------------- +// This interface encapsulates a database connection and lets you operate on it. +// Use the ISQLWrapperFactory factory to get access to the interface. +// NOTE - you can only have one outstanding query at a time. When you are done with a query call FreeResult() +//----------------------------------------------------------------------------- +class ISQLWrapper +{ +public: + // run a SQL statement against the DB, typically an insert statement as no data is returned + virtual bool BInsert( const char *pchQueryString ) = 0; + // get a description of the tables associated with the db you connected to + virtual const ISQLTableSet *PSQLTableSetDescription() = 0; + // run a query against the db and then iterate the result set (you can only have 1 outstanding query at a time, and call FreeResults() when you are done) + virtual IResultSet *PResultSetQuery( const char *pchQueryString ) = 0; + // you MUST call then after you finish with the IResultSet from the above query + virtual void FreeResult() = 0; +}; + + +//----------------------------------------------------------------------------- +// This is a factory to create objects that let you interact with a MySQL database. +// Make sure you Free() any interfaces you create. +//----------------------------------------------------------------------------- +class ISQLWrapperFactory +{ +public: + // setup details about this db connection + virtual ISQLWrapper *Create( const char *pchDB, const char *pchHost, const char *pchUsername, const char *pchPassword ) = 0; + virtual void Free( ISQLWrapper *pSQLWrapper ) = 0; +}; + +#define INTERFACEVERSION_ISQLWRAPPER "ISQLWRAPPER001" + +#endif // ISQLWRAPPER_H + diff --git a/public/istudiorender.h b/public/istudiorender.h new file mode 100644 index 0000000..a82cdff --- /dev/null +++ b/public/istudiorender.h @@ -0,0 +1,425 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ISTUDIORENDER_H +#define ISTUDIORENDER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlvector.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialsystem.h" +#include "appframework/IAppSystem.h" +#include "datacache/imdlcache.h" +#include "tier0/fasttimer.h" +#include "studio.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct studiohdr_t; +struct studiomeshdata_t; +class Vector; +struct LightDesc_t; +class IMaterial; +struct studiohwdata_t; +struct Ray_t; +class Vector4D; +class IMaterialSystem; +struct matrix3x4_t; +class IMesh; +struct vertexFileHeader_t; +struct FlashlightState_t; +class VMatrix; +namespace OptimizedModel { struct FileHeader_t; } +class IPooledVBAllocator; +struct MeshInstanceData_t; +struct ShaderStencilState_t; + + +// undone: what's the standard for function type naming? +typedef void (*StudioRender_Printf_t)( const char *fmt, ... ); + +struct StudioRenderConfig_t +{ + float fEyeShiftX; // eye X position + float fEyeShiftY; // eye Y position + float fEyeShiftZ; // eye Z position + float fEyeSize; // adjustment to iris textures + float fEyeGlintPixelWidthLODThreshold; + + int maxDecalsPerModel; + int drawEntities; + int skin; + int fullbright; + + bool bEyeMove : 1; // look around + bool bSoftwareSkin : 1; + bool bNoHardware : 1; + bool bNoSoftware : 1; + bool bTeeth : 1; + bool bEyes : 1; + bool bFlex : 1; + bool bWireframe : 1; + bool bDrawNormals : 1; + bool bDrawTangentFrame : 1; + bool bDrawZBufferedWireframe : 1; + bool bSoftwareLighting : 1; + bool bShowEnvCubemapOnly : 1; + bool bWireframeDecals : 1; + + // Reserved for future use + int m_nReserved[4]; +}; + + + +//----------------------------------------------------------------------------- +// Studio render interface +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( StudioDecalHandle_t ); +#define STUDIORENDER_DECAL_INVALID ( (StudioDecalHandle_t)0 ) + +enum +{ + ADDDECAL_TO_ALL_LODS = -1 +}; + + +//----------------------------------------------------------------------------- +// DrawModel flags +//----------------------------------------------------------------------------- +enum +{ + STUDIORENDER_DRAW_ENTIRE_MODEL = 0, + STUDIORENDER_DRAW_OPAQUE_ONLY = 0x01, + STUDIORENDER_DRAW_TRANSLUCENT_ONLY = 0x02, + STUDIORENDER_DRAW_GROUP_MASK = 0x03, + + STUDIORENDER_DRAW_NO_FLEXES = 0x04, + STUDIORENDER_DRAW_STATIC_LIGHTING = 0x08, + + STUDIORENDER_DRAW_ACCURATETIME = 0x10, // Use accurate timing when drawing the model. + STUDIORENDER_DRAW_NO_SHADOWS = 0x20, + STUDIORENDER_DRAW_GET_PERF_STATS = 0x40, + + STUDIORENDER_DRAW_WIREFRAME = 0x80, + + STUDIORENDER_DRAW_ITEM_BLINK = 0x100, + + STUDIORENDER_SHADOWDEPTHTEXTURE = 0x200, + + STUDIORENDER_NO_SKIN = 0x400, + + STUDIORENDER_SKIP_DECALS = 0x800, +}; + + +//----------------------------------------------------------------------------- +// Standard model vertex formats +//----------------------------------------------------------------------------- +// FIXME: remove these (materials/shaders should drive vertex format). Need to +// list required forcedmaterialoverrides in models/bsps (rather than +// all models supporting all possible overrides, as they do currently). +#define VERTEX_TEXCOORD0_2D ( ( (uint64) 2 ) << ( TEX_COORD_SIZE_BIT + ( 3*0 ) ) ) +enum MaterialVertexFormat_t +{ + MATERIAL_VERTEX_FORMAT_MODEL = (VertexFormat_t) VERTEX_POSITION | VERTEX_COLOR_STREAM_1 | VERTEX_NORMAL | VERTEX_TEXCOORD0_2D | VERTEX_USERDATA_SIZE(4), +}; + + +//----------------------------------------------------------------------------- +// What kind of material override is it? +//----------------------------------------------------------------------------- +enum OverrideType_t +{ + OVERRIDE_NORMAL = 0, + OVERRIDE_BUILD_SHADOWS, + OVERRIDE_DEPTH_WRITE, +}; + + +//----------------------------------------------------------------------------- +// DrawModel info +//----------------------------------------------------------------------------- + +// Special flag for studio models that have a compiled in shadow lod version +// It's negative 2 since positive numbers == use a regular slot and -1 means +// have studiorender compute a value instead +enum +{ + USESHADOWLOD = -2, +}; + +// beyond this number of materials, you won't get info back from DrawModel +#define MAX_DRAW_MODEL_INFO_MATERIALS 8 + +struct DrawModelResults_t +{ + int m_ActualTriCount; + int m_TextureMemoryBytes; + int m_NumHardwareBones; + int m_NumBatches; + int m_NumMaterials; + int m_nLODUsed; + int m_flLODMetric; + CFastTimer m_RenderTime; + CUtlVectorFixed m_Materials; +}; + +struct ColorMeshInfo_t +{ + // A given color mesh can own a unique Mesh, or it can use a shared Mesh + // (in which case it uses a sub-range defined by m_nVertOffset and m_nNumVerts) + IMesh * m_pMesh; + IPooledVBAllocator * m_pPooledVBAllocator; + int m_nVertOffsetInBytes; + int m_nNumVerts; +}; + +struct DrawModelInfo_t +{ + studiohdr_t *m_pStudioHdr; + studiohwdata_t *m_pHardwareData; + StudioDecalHandle_t m_Decals; + int m_Skin; + int m_Body; + int m_HitboxSet; + void *m_pClientEntity; + int m_Lod; + ColorMeshInfo_t *m_pColorMeshes; + bool m_bStaticLighting; + MaterialLightingState_t m_LightingState; + + IMPLEMENT_OPERATOR_EQUAL( DrawModelInfo_t ); +}; + +enum +{ + // This is because we store which flashlights are on which model + // in a 32-bit field (see ModelArrayInstanceData_t::m_nFlashlightUsage) + MAX_FLASHLIGHTS_PER_INSTANCE_DRAW_CALL = 32 +}; + +struct FlashlightInstance_t +{ + IMaterial *m_pDebugMaterial; + FlashlightState_t m_FlashlightState; + VMatrix m_WorldToTexture; + ITexture *m_pFlashlightDepthTexture; +}; + +struct StudioModelArrayInfo2_t +{ + int m_nFlashlightCount; + FlashlightInstance_t *m_pFlashlights; // NOTE: Can have at most MAX_FLASHLIGHTS_PER_INSTANCE_DRAW_CALL of these +}; + +struct StudioModelArrayInfo_t : public StudioModelArrayInfo2_t +{ + studiohdr_t *m_pStudioHdr; + studiohwdata_t *m_pHardwareData; +}; + +struct StudioArrayData_t +{ + studiohdr_t *m_pStudioHdr; + studiohwdata_t *m_pHardwareData; + void *m_pInstanceData; // See StudioShadowArrayInstanceData_t or StudioArrayInstanceData_t + int m_nCount; +}; + +struct StudioShadowArrayInstanceData_t +{ + int m_nLOD; + int m_nBody; + int m_nSkin; + matrix3x4a_t *m_pPoseToWorld; + float *m_pFlexWeights; + float *m_pDelayedFlexWeights; +}; + +struct StudioArrayInstanceData_t : public StudioShadowArrayInstanceData_t +{ + MaterialLightingState_t *m_pLightingState; + MaterialLightingState_t *m_pDecalLightingState; + ITexture *m_pEnvCubemapTexture; + StudioDecalHandle_t m_Decals; + uint32 m_nFlashlightUsage; // Mask indicating which flashlights to use. + ShaderStencilState_t *m_pStencilState; + ColorMeshInfo_t *m_pColorMeshInfo; + Vector4D m_DiffuseModulation; +}; + +struct GetTriangles_Vertex_t +{ + Vector m_Position; + Vector m_Normal; + Vector4D m_TangentS; + Vector2D m_TexCoord; + Vector4D m_BoneWeight; + int m_BoneIndex[4]; + int m_NumBones; + + IMPLEMENT_OPERATOR_EQUAL( GetTriangles_Vertex_t ); +}; + +struct GetTriangles_MaterialBatch_t +{ + IMaterial *m_pMaterial; + CUtlVector m_Verts; + CUtlVector m_TriListIndices; +}; + +struct GetTriangles_Output_t +{ + CUtlVector m_MaterialBatches; + matrix3x4_t m_PoseToWorld[MAXSTUDIOBONES]; + + DISALLOW_OPERATOR_EQUAL( GetTriangles_Output_t ); +}; + +//----------------------------------------------------------------------------- +// Cache Callback Function +// implementation can either statically persist data (tools) or lru cache (engine) it. +// caller returns base pointer to resident data. +// code expectes data to be dynamic and invokes cache callback prior to iterative access. +// virtualModel is member passed in via studiohdr_t and passed back for model identification. +//----------------------------------------------------------------------------- +#define STUDIO_DATA_CACHE_INTERFACE_VERSION "VStudioDataCache005" + +abstract_class IStudioDataCache : public IAppSystem +{ +public: + virtual bool VerifyHeaders( studiohdr_t *pStudioHdr ) = 0; + virtual vertexFileHeader_t *CacheVertexData( studiohdr_t *pStudioHdr ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Studio render interface +//----------------------------------------------------------------------------- +abstract_class IStudioRender : public IAppSystem +{ +public: + virtual void BeginFrame( void ) = 0; + virtual void EndFrame( void ) = 0; + + // Used for the mat_stub console command. + virtual void Mat_Stub( IMaterialSystem *pMatSys ) = 0; + + // Updates the rendering configuration + virtual void UpdateConfig( const StudioRenderConfig_t& config ) = 0; + virtual void GetCurrentConfig( StudioRenderConfig_t& config ) = 0; + + // Load, unload model data + virtual bool LoadModel( studiohdr_t *pStudioHdr, void *pVtxData, studiohwdata_t *pHardwareData ) = 0; + virtual void UnloadModel( studiohwdata_t *pHardwareData ) = 0; + + // Refresh the studiohdr since it was lost... + virtual void RefreshStudioHdr( studiohdr_t* pStudioHdr, studiohwdata_t* pHardwareData ) = 0; + + // This is needed to do eyeglint and calculate the correct texcoords for the eyes. + virtual void SetEyeViewTarget( const studiohdr_t *pStudioHdr, int nBodyIndex, const Vector& worldPosition ) = 0; + + // Methods related to lighting state + // NOTE: SetAmbientLightColors assumes that the arraysize is the same as + // returned from GetNumAmbientLightSamples + virtual int GetNumAmbientLightSamples() = 0; + virtual const Vector *GetAmbientLightDirections() = 0; + virtual void SetAmbientLightColors( const Vector4D *pAmbientOnlyColors ) = 0; + virtual void SetAmbientLightColors( const Vector *pAmbientOnlyColors ) = 0; + virtual void SetLocalLights( int numLights, const LightDesc_t *pLights ) = 0; + + // Sets information about the camera location + orientation + virtual void SetViewState( const Vector& viewOrigin, const Vector& viewRight, + const Vector& viewUp, const Vector& viewPlaneNormal ) = 0; + + // LOD stuff + virtual int GetNumLODs( const studiohwdata_t &hardwareData ) const = 0; + virtual float GetLODSwitchValue( const studiohwdata_t &hardwareData, int lod ) const = 0; + virtual void SetLODSwitchValue( studiohwdata_t &hardwareData, int lod, float switchValue ) = 0; + + // Sets the color/alpha modulation + virtual void SetColorModulation( float const* pColor ) = 0; + virtual void SetAlphaModulation( float flAlpha ) = 0; + + // Draws the model + virtual void DrawModel( DrawModelResults_t *pResults, const DrawModelInfo_t& info, + matrix3x4_t *pBoneToWorld, float *pFlexWeights, float *pFlexDelayedWeights, const Vector &modelOrigin, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ) = 0; + + // Methods related to static prop rendering + virtual void DrawModelStaticProp( const DrawModelInfo_t& drawInfo, const matrix3x4_t &modelToWorld, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ) = 0; + virtual void DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld ) = 0; + virtual void DrawStaticPropShadows( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld, int flags ) = 0; + + // Causes a material to be used instead of the materials the model was compiled with + virtual void ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType = OVERRIDE_NORMAL ) = 0; + + // Create, destroy list of decals for a particular model + virtual StudioDecalHandle_t CreateDecalList( studiohwdata_t *pHardwareData ) = 0; + virtual void DestroyDecalList( StudioDecalHandle_t handle ) = 0; + + // Add decals to a decal list by doing a planar projection along the ray + // The BoneToWorld matrices must be set before this is called + virtual void AddDecal( StudioDecalHandle_t handle, studiohdr_t *pStudioHdr, matrix3x4_t *pBoneToWorld, + const Ray_t & ray, const Vector& decalUp, IMaterial* pDecalMaterial, float radius, int body, bool noPokethru = false, int maxLODToDecal = ADDDECAL_TO_ALL_LODS ) = 0; + + // Compute the lighting at a point and normal + virtual void ComputeLighting( const Vector* pAmbient, int lightCount, + LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting ) = 0; + + // Compute the lighting at a point, constant directional component is passed + // as flDirectionalAmount + virtual void ComputeLightingConstDirectional( const Vector* pAmbient, int lightCount, + LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting, float flDirectionalAmount ) = 0; + + // Shadow state (affects the models as they are rendered) + virtual void AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *m_pFlashlightState = NULL, VMatrix *pWorldToTexture = NULL, ITexture *pFlashlightDepthTexture = NULL ) = 0; + virtual void ClearAllShadows() = 0; + + // Gets the model LOD; pass in the screen size in pixels of a sphere + // of radius 1 that has the same origin as the model to get the LOD out... + virtual int ComputeModelLod( studiohwdata_t* pHardwareData, float unitSphereSize, float *pMetric = NULL ) = 0; + + // Return a number that is usable for budgets, etc. + // Things that we care about: + // 1) effective triangle count (factors in batch sizes, state changes, etc) + // 2) texture memory usage + // Get Triangles returns the LOD used + virtual void GetPerfStats( DrawModelResults_t *pResults, const DrawModelInfo_t &info, CUtlBuffer *pSpewBuf = NULL ) const = 0; + virtual void GetTriangles( const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, GetTriangles_Output_t &out ) = 0; + + // Returns materials used by a particular model + virtual int GetMaterialList( studiohdr_t *pStudioHdr, int count, IMaterial** ppMaterials ) = 0; + virtual int GetMaterialListFromBodyAndSkin( MDLHandle_t studio, int nSkin, int nBody, int nCountOutputMaterials, IMaterial** ppOutputMaterials ) = 0; + + // no debug modes, just fastest drawing path + virtual void DrawModelArrayStaticProp( const DrawModelInfo_t& drawInfo, int nInstanceCount, const MeshInstanceData_t *pInstanceData, ColorMeshInfo_t **pColorMeshes ) = 0; + + // draw an array of models with the same state + virtual void DrawModelArray( const StudioModelArrayInfo_t &drawInfo, int nCount, + StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ) = 0; + + // draw an array of models with the same state + virtual void DrawModelShadowArray( int nCount, StudioArrayData_t *pShadowData, + int nInstanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ) = 0; + + // draw an array of models with the same state + virtual void DrawModelArray( const StudioModelArrayInfo2_t &info, int nCount, StudioArrayData_t *pArrayData, + int nInstanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ) = 0; +}; + +DECLARE_TIER3_INTERFACE( IStudioRender, g_pStudioRender ); + +#endif // ISTUDIORENDER_H diff --git a/public/ivoiceserver.h b/public/ivoiceserver.h new file mode 100644 index 0000000..168939d --- /dev/null +++ b/public/ivoiceserver.h @@ -0,0 +1,34 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This module defines the IVoiceServer interface, which is used by +// game code to control which clients are listening to which other +// clients' voice streams. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVOICESERVER_H +#define IVOICESERVER_H + + +#include "interface.h" + + +#define INTERFACEVERSION_VOICESERVER "VoiceServer002" + + +abstract_class IVoiceServer +{ +public: + virtual ~IVoiceServer() {} + + // Use these to setup who can hear whose voice. + // Pass in client indices (which are their ent indices - 1). + virtual bool GetClientListening(int iReceiver, int iSender) = 0; + virtual bool SetClientListening(int iReceiver, int iSender, bool bListen) = 0; + virtual bool SetClientProximity(int iReceiver, int iSender, bool bUseProximity) = 0; +}; + + +#endif + diff --git a/public/ivoicetweak.h b/public/ivoicetweak.h new file mode 100644 index 0000000..a456c8b --- /dev/null +++ b/public/ivoicetweak.h @@ -0,0 +1,42 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVOICETWEAK_H +#define IVOICETWEAK_H +#ifdef _WIN32 +#pragma once +#endif + +// These provide access to the voice controls. +typedef enum +{ + MicrophoneVolume=0, // values 0-1. + OtherSpeakerScale, // values 0-1. Scales how loud other players are. + MicBoost, + SpeakingVolume, // values 0-1. Current voice volume received through Voice Tweak mode + +} VoiceTweakControl; + + +typedef struct IVoiceTweak_s +{ + // These turn voice tweak mode on and off. While in voice tweak mode, the user's voice is echoed back + // without sending to the server. + int (*StartVoiceTweakMode)(); // Returns 0 on error. + void (*EndVoiceTweakMode)(); + + // Get/set control values. + void (*SetControlFloat)(VoiceTweakControl iControl, float value); + float (*GetControlFloat)(VoiceTweakControl iControl); + + bool (*IsStillTweaking)(); // This can return false if the user restarts the sound system during voice tweak mode + + bool (*IsControlFound)(VoiceTweakControl iControl); +} IVoiceTweak; + + +#endif // IVOICETWEAK_H diff --git a/public/ivraddll.h b/public/ivraddll.h new file mode 100644 index 0000000..2739272 --- /dev/null +++ b/public/ivraddll.h @@ -0,0 +1,99 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVRADDLL_H +#define IVRADDLL_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "interface.h" +#include "bspfile.h" + + +#define VRAD_INTERFACE_VERSION "vraddll_1" + + +class CBSPInfo +{ +public: + byte *dlightdata; + int lightdatasize; + + dface_t *dfaces; + unsigned char *m_pFacesTouched; // If non-null, then this has 1 byte for each face and + // tells which faces had their lightmaps updated. + int numfaces; + + dvertex_t *dvertexes; + int numvertexes; + + dedge_t *dedges; + int numedges; + + int *dsurfedges; + int numsurfedges; + + texinfo_t *texinfo; + int numtexinfo; + + dtexdata_t *dtexdata; + int numtexdata; + + ddispinfo_t *g_dispinfo; + int g_numdispinfo; + + char *texDataStringData; + int nTexDataStringData; + + int *texDataStringTable; + int nTexDataStringTable; +}; + + +// This is the DLL interface to VRAD. +class IVRadDLL +{ +public: + // All vrad.exe does is load the VRAD DLL and run this. + virtual int main( int argc, char **argv ) = 0; + + + // Load the BSP file into memory. + virtual bool Init( char const *pFilename ) = 0; + + // You must call this if you call Init(), to free resources. + virtual void Release() = 0; + + // Get some data from the BSP file that's in memory. + virtual void GetBSPInfo( CBSPInfo *pInfo ) = 0; + + // Incrementally relight the BSP file in memory given the new entity + // descriptions in pVMFFile. pVMFFile should only contain light entities. + // + // Returns true only if the lightmaps are updated. If the process is + // interrupted or there is an error, false is returned. + virtual bool DoIncrementalLight( char const *pVMFFile ) = 0; + + // Calling DoIncrementalLight doesn't actually write anything to disk. + // Calling this will write the incremental light file out and will write the + // current in-memory light data into the BSP. + // NOTE: if DoIncrementalLight never finished, this will do nothing and return false. + virtual bool Serialize() = 0; + + // Returns a 0-1 value telling how close it is to completing the task. + // This can be called from a separate thread than DoIncrementLight. + virtual float GetPercentComplete() = 0; + + // This can be called from a separate thread than the DoIncrementalLight thread. + // It asynchronously tells DoIncrementalLight to stop as soon as possible and exit. + virtual void Interrupt() = 0; +}; + + +#endif // IVRADDLL_H diff --git a/public/ivrenderview.h b/public/ivrenderview.h new file mode 100644 index 0000000..bc4a749 --- /dev/null +++ b/public/ivrenderview.h @@ -0,0 +1,334 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// +#if !defined( IVRENDERVIEW_H ) +#define IVRENDERVIEW_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "mathlib/vplane.h" +#include "interface.h" +#include "materialsystem/imaterialsystem.h" +#include "const.h" +#include "tier1/refcount.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CViewSetup; +class CEngineSprite; +class IClientEntity; +class IMaterial; +struct model_t; +class IClientRenderable; + + +//----------------------------------------------------------------------------- +// Flags used by DrawWorldLists +//----------------------------------------------------------------------------- +enum +{ + DRAWWORLDLISTS_DRAW_STRICTLYABOVEWATER = 0x001, + DRAWWORLDLISTS_DRAW_STRICTLYUNDERWATER = 0x002, + DRAWWORLDLISTS_DRAW_INTERSECTSWATER = 0x004, + DRAWWORLDLISTS_DRAW_WATERSURFACE = 0x008, + DRAWWORLDLISTS_DRAW_SKYBOX = 0x010, + DRAWWORLDLISTS_DRAW_CLIPSKYBOX = 0x020, + DRAWWORLDLISTS_DRAW_SHADOWDEPTH = 0x040, + DRAWWORLDLISTS_DRAW_REFRACTION = 0x080, + DRAWWORLDLISTS_DRAW_REFLECTION = 0x100, + DRAWWORLDLISTS_DRAW_WORLD_GEOMETRY = 0x200, + DRAWWORLDLISTS_DRAW_DECALS_AND_OVERLAYS = 0x400, +}; + +enum +{ + MAT_SORT_GROUP_STRICTLY_ABOVEWATER = 0, + MAT_SORT_GROUP_STRICTLY_UNDERWATER, + MAT_SORT_GROUP_INTERSECTS_WATER_SURFACE, + MAT_SORT_GROUP_WATERSURFACE, + + MAX_MAT_SORT_GROUPS +}; + + +typedef VPlane Frustum[FRUSTUM_NUMPLANES]; + + +//----------------------------------------------------------------------------- +// Leaf index +//----------------------------------------------------------------------------- +typedef unsigned short LeafIndex_t; +enum +{ + INVALID_LEAF_INDEX = (LeafIndex_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Describes the leaves to be rendered this view, set by BuildWorldLists + +//----------------------------------------------------------------------------- +// NOTE: This is slightly slower on x360 but saves memory +#if 1 +struct WorldListLeafData_t +{ + LeafIndex_t leafIndex; // 16 bits + int16 waterData; + uint16 firstTranslucentSurface; // engine-internal list index + uint16 translucentSurfaceCount; // count of translucent surfaces+disps +}; +#else +struct WorldListLeafData_t +{ + uint32 leafIndex; + int32 waterData; + uint32 firstTranslucentSurface; // engine-internal list index + uint32 translucentSurfaceCount; // count of translucent surfaces+disps +}; +#endif +struct WorldListInfo_t +{ + int m_ViewFogVolume; + int m_LeafCount; + bool m_bHasWater; + WorldListLeafData_t *m_pLeafDataList; +}; + +class IWorldRenderList : public IRefCounted +{ +}; + +//----------------------------------------------------------------------------- +// Describes the fog volume for a particular point +//----------------------------------------------------------------------------- +struct VisibleFogVolumeInfo_t +{ + int m_nVisibleFogVolume; + int m_nVisibleFogVolumeLeaf; + bool m_bEyeInFogVolume; + float m_flDistanceToWater; + float m_flWaterHeight; + IMaterial *m_pFogVolumeMaterial; +}; + + +//----------------------------------------------------------------------------- +// Vertex format for brush models +//----------------------------------------------------------------------------- +struct BrushVertex_t +{ + Vector m_Pos; + Vector m_Normal; + Vector m_TangentS; + Vector m_TangentT; + Vector2D m_TexCoord; + Vector2D m_LightmapCoord; + +private: + BrushVertex_t( const BrushVertex_t& src ); +}; + +//----------------------------------------------------------------------------- +// Visibility data for area portal culling +//----------------------------------------------------------------------------- +struct VisOverrideData_t +{ + Vector m_vecVisOrigin; // The point to to use as the viewpoint for area portal backface cull checks. + float m_fDistToAreaPortalTolerance; // The distance from an area portal before using the full screen as the viewable portion. +}; + + +//----------------------------------------------------------------------------- +// interface for asking about the Brush surfaces from the client DLL +//----------------------------------------------------------------------------- + +class IBrushSurface +{ +public: + // Computes texture coordinates + lightmap coordinates given a world position + virtual void ComputeTextureCoordinate( Vector const& worldPos, Vector2D& texCoord ) = 0; + virtual void ComputeLightmapCoordinate( Vector const& worldPos, Vector2D& lightmapCoord ) = 0; + + // Gets the vertex data for this surface + virtual int GetVertexCount() const = 0; + virtual void GetVertexData( BrushVertex_t* pVerts ) = 0; + + // Gets at the material properties for this surface + virtual IMaterial* GetMaterial() = 0; +}; + + +//----------------------------------------------------------------------------- +// interface for installing a new renderer for brush surfaces +//----------------------------------------------------------------------------- + +class IBrushRenderer +{ +public: + // Draws the surface; returns true if decals should be rendered on this surface + virtual bool RenderBrushModelSurface( IClientEntity* pBaseEntity, IBrushSurface* pBrushSurface ) = 0; +}; + + +#define MAX_VIS_LEAVES 32 +//----------------------------------------------------------------------------- +// Purpose: Interface to client .dll to set up a rendering pass over world +// The client .dll can call Render multiple times to overlay one or more world +// views on top of one another +//----------------------------------------------------------------------------- +class IVRenderView +{ +public: + + // Draw normal brush model. + // If pMaterialOverride is non-null, then all the faces of the bmodel will + // set this material rather than their regular material. + virtual void DrawBrushModel( + IClientEntity *baseentity, + model_t *model, + const Vector& origin, + const QAngle& angles, + bool sort ) = 0; + + // Draw brush model that has no origin/angles change ( uses identity transform ) + // FIXME, Material proxy IClientEntity *baseentity is unused right now, use DrawBrushModel for brushes with + // proxies for now. + virtual void DrawIdentityBrushModel( IWorldRenderList *pList, model_t *model ) = 0; + + // Mark this dynamic light as having changed this frame ( so light maps affected will be recomputed ) + virtual void TouchLight( struct dlight_t *light ) = 0; + // Draw 3D Overlays + virtual void Draw3DDebugOverlays( void ) = 0; + // Sets global blending fraction + virtual void SetBlend( float blend ) = 0; + virtual float GetBlend( void ) = 0; + + // Sets global color modulation + virtual void SetColorModulation( float const* blend ) = 0; + virtual void GetColorModulation( float* blend ) = 0; + + // Wrap entire scene drawing + virtual void SceneBegin( void ) = 0; + virtual void SceneEnd( void ) = 0; + + // Gets the fog volume for a particular point + virtual void GetVisibleFogVolume( const Vector& eyePoint, VisibleFogVolumeInfo_t *pInfo ) = 0; + + // Wraps world drawing + // If iForceViewLeaf is not -1, then it uses the specified leaf as your starting area for setting up area portal culling. + // This is used by water since your reflected view origin is often in solid space, but we still want to treat it as though + // the first portal we're looking out of is a water portal, so our view effectively originates under the water. + virtual IWorldRenderList * CreateWorldList() = 0; + + virtual void BuildWorldLists( IWorldRenderList *pList, WorldListInfo_t* pInfo, int iForceFViewLeaf, const VisOverrideData_t* pVisData = NULL, bool bShadowDepth = false, float *pReflectionWaterHeight = NULL ) = 0; + virtual void DrawWorldLists( IWorldRenderList *pList, unsigned long flags, float waterZAdjust ) = 0; + virtual int GetNumIndicesForWorldLists( IWorldRenderList *pList, unsigned long nFlags ) = 0; + + // Optimization for top view + virtual void DrawTopView( bool enable ) = 0; + virtual void TopViewBounds( Vector2D const& mins, Vector2D const& maxs ) = 0; + + // Draw lights + virtual void DrawLights( void ) = 0; + // FIXME: This function is a stub, doesn't do anything in the engine right now + virtual void DrawMaskEntities( void ) = 0; + + // Draw surfaces with alpha, don't call in shadow depth pass + virtual void DrawTranslucentSurfaces( IWorldRenderList *pList, int *pSortList, int sortCount, unsigned long flags ) = 0; + + // Draw Particles ( just draws the linefine for debugging map leaks ) + virtual void DrawLineFile( void ) = 0; + // Draw lightmaps + virtual void DrawLightmaps( IWorldRenderList *pList, int pageId ) = 0; + // Wraps view render sequence, sets up a view + virtual void ViewSetupVis( bool novis, int numorigins, const Vector origin[] ) = 0; + + // Return true if any of these leaves are visible in the current PVS. + virtual bool AreAnyLeavesVisible( int *leafList, int nLeaves ) = 0; + + virtual void VguiPaint( void ) = 0; + // Sets up view fade parameters + virtual void ViewDrawFade( byte *color, IMaterial *pMaterial ) = 0; + // Sets up the projection matrix for the specified field of view + virtual void OLD_SetProjectionMatrix( float fov, float zNear, float zFar ) = 0; + // Determine lighting at specified position + virtual colorVec GetLightAtPoint( Vector& pos ) = 0; + // Whose eyes are we looking through? + virtual int GetViewEntity( void ) = 0; + virtual bool IsViewEntity( int entindex ) = 0; + // Get engine field of view setting + virtual float GetFieldOfView( void ) = 0; + // 1 == ducking, 0 == not + virtual unsigned char **GetAreaBits( void ) = 0; + + // Set up fog for a particular leaf + virtual void SetFogVolumeState( int nVisibleFogVolume, bool bUseHeightFog ) = 0; + + // Installs a brush surface draw override method, null means use normal renderer + virtual void InstallBrushSurfaceRenderer( IBrushRenderer* pBrushRenderer ) = 0; + + // Draw brush model shadow + virtual void DrawBrushModelShadow( IClientRenderable *pRenderable ) = 0; + + // Does the leaf contain translucent surfaces? + virtual bool LeafContainsTranslucentSurfaces( IWorldRenderList *pList, int sortIndex, unsigned long flags ) = 0; + + virtual bool DoesBoxIntersectWaterVolume( const Vector &mins, const Vector &maxs, int leafWaterDataID ) = 0; + + virtual void SetAreaState( + unsigned char chAreaBits[MAX_AREA_STATE_BYTES], + unsigned char chAreaPortalBits[MAX_AREA_PORTAL_STATE_BYTES] ) = 0; + + // See i + virtual void VGui_Paint( int mode ) = 0; + + // Push, pop views (see PushViewFlags_t above for flags) + virtual void Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes ) = 0; + virtual void Push2DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes ) = 0; + virtual void PopView( Frustum frustumPlanes ) = 0; + + // Sets the main view + virtual void SetMainView( const Vector &vecOrigin, const QAngle &angles ) = 0; + + enum + { + VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS = 0x00000001 + }; + + // Wraps view render sequence, sets up a view + virtual void ViewSetupVisEx( bool novis, int numorigins, const Vector origin[], unsigned int &returnFlags ) = 0; + + //replaces the current view frustum with a rhyming replacement of your choice + virtual void OverrideViewFrustum( Frustum custom ) = 0; + + virtual void DrawBrushModelShadowDepth( IClientEntity *baseentity, model_t *model, const Vector& origin, const QAngle& angles, bool bSort ) = 0; + virtual void UpdateBrushModelLightmap( model_t *model, IClientRenderable *pRenderable ) = 0; + virtual void BeginUpdateLightmaps( void ) = 0; + virtual void EndUpdateLightmaps( void ) = 0; + virtual void OLD_SetOffCenterProjectionMatrix( float fov, float zNear, float zFar, float flAspectRatio, float flBottom, float flTop, float flLeft, float flRight ) = 0; + virtual void OLD_SetProjectionMatrixOrtho( float left, float top, float right, float bottom, float zNear, float zFar ) = 0; + virtual void Push3DView( const CViewSetup &view, int nFlags, ITexture* pRenderTarget, Frustum frustumPlanes, ITexture* pDepthTexture ) = 0; + virtual void GetMatricesForView( const CViewSetup &view, VMatrix *pWorldToView, VMatrix *pViewToProjection, VMatrix *pWorldToProjection, VMatrix *pWorldToPixels ) = 0; +}; + +// change this when the new version is incompatable with the old +#define VENGINE_RENDERVIEW_INTERFACE_VERSION "VEngineRenderView013" + +#if defined(_STATIC_LINKED) && defined(CLIENT_DLL) +namespace Client +{ +extern IVRenderView *render; +} +#else +extern IVRenderView *render; +#endif + +#endif // IVRENDERVIEW_H diff --git a/public/ivtex.h b/public/ivtex.h new file mode 100644 index 0000000..2bc6cf2 --- /dev/null +++ b/public/ivtex.h @@ -0,0 +1,31 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IVTEX_H +#define IVTEX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" + + +class IVTex +{ +public: + // For use by command-line tools + virtual int VTex( int argc, char **argv ) = 0; + + // For use by engine + virtual int VTex( CreateInterfaceFn filesystemFactory, const char *pGameDir, int argc, char **argv ) = 0; +}; + +#define IVTEX_VERSION_STRING "VTEX_003" + + +#endif // IVTEX_H diff --git a/public/ixboxsystem.h b/public/ixboxsystem.h new file mode 100644 index 0000000..a0a8d03 --- /dev/null +++ b/public/ixboxsystem.h @@ -0,0 +1,444 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Interface to Xbox 360 system functions. Helps deal with the async system and Live +// functions by either providing a handle for the caller to check results or handling +// automatic cleanup of the async data when the caller doesn't care about the results. +// +//===========================================================================// + +#ifndef IXBOXSYSTEM_H +#define IXBOXSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#if !defined( _X360 ) +#include "xbox/xboxstubs.h" +#endif + +typedef void* AsyncHandle_t; +typedef void* XboxHandle_t; + +#ifdef POSIX + +struct XOVERLAPPED +{ +}; + +#define ERROR_SUCCESS 0 +#define ERROR_IO_PENDING 1 +#define ERROR_IO_INCOMPLETE 2 +#define ERROR_INSUFFICIENT_BUFFER 3 +#define ERROR_NO_SUCH_USER 4 +#define ERROR_NO_SUCH_PRIVILEGE 5 +#define ERROR_ACCESS_DISABLED_BY_POLICY 6 +#endif + +//----------------------------------------------------------------------------- +// Xbox system interface +//----------------------------------------------------------------------------- +abstract_class IXboxSystem +{ +public: + virtual AsyncHandle_t CreateAsyncHandle( void ) = 0; + virtual void ReleaseAsyncHandle( AsyncHandle_t handle ) = 0; + virtual int GetOverlappedResult( AsyncHandle_t handle, uint *pResultCode, bool bWait ) = 0; + virtual void CancelOverlappedOperation( AsyncHandle_t handle ) = 0; + + // Save/Load + virtual bool GameHasSavegames( void ) = 0; + virtual void GetModSaveContainerNames( const char *pchModName, const wchar_t **ppchDisplayName, const char **ppchName ) = 0; + virtual uint GetContainerRemainingSpace( DWORD nStorageID ) = 0; + virtual bool DeviceCapacityAdequate( int iController, DWORD nStorageID, const char *pModName ) = 0; + virtual DWORD DiscoverUserData( DWORD nUserID, const char *pModName ) = 0; + + // XUI + virtual bool ShowDeviceSelector( int iController, bool bForce, uint *pStorageID, AsyncHandle_t *pHandle ) = 0; + virtual void ShowSigninUI( uint nPanes, uint nFlags ) = 0; + + // Rich Presence and Matchmaking + virtual int UserSetContext( uint nUserIdx, XUSER_CONTEXT const &xc, bool bAsync = true, AsyncHandle_t *pHandle = NULL ) = 0; + virtual int UserSetProperty( uint nUserIndex, XUSER_PROPERTY const &xp, bool bAsync = true, AsyncHandle_t *pHandle = NULL ) = 0; + virtual int UserGetContext( uint nUserIdx, uint nContextID, uint &nContextValue) = 0; + virtual int UserGetPropertyInt( uint nUserIndex, uint nPropertyId, uint &nPropertyValue) = 0; + + + // Matchmaking + virtual int CreateSession( uint nFlags, uint nUserIdx, uint nMaxPublicSlots, uint nMaxPrivateSlots, uint64 *pNonce, void *pSessionInfo, XboxHandle_t *pSessionHandle, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual uint DeleteSession( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual uint SessionSearch( uint nProcedureIndex, uint nUserIndex, uint nNumResults, uint nNumUsers, uint nNumProperties, uint nNumContexts, XUSER_PROPERTY *pSearchProperties, XUSER_CONTEXT *pSearchContexts, uint *pcbResultsBuffer, XSESSION_SEARCHRESULT_HEADER *pSearchResults, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual uint SessionStart( XboxHandle_t hSession, uint nFlags, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual uint SessionEnd( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionJoinLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, const bool *pPrivateSlots, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionJoinRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, const bool *pPrivateSlots, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionLeaveLocal( XboxHandle_t hSession, uint nUserCount, const uint *pUserIndexes, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionLeaveRemote( XboxHandle_t hSession, uint nUserCount, const XUID *pXuids, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionMigrate( XboxHandle_t hSession, uint nUserIndex, void *pSessionInfo, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int SessionArbitrationRegister( XboxHandle_t hSession, uint nFlags, uint64 nonce, uint *pBytes, void *pBuffer, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + + // Stats + virtual int WriteStats( XboxHandle_t hSession, XUID xuid, uint nViews, void* pViews, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int FlushStats( XboxHandle_t hSession, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int EnumerateStatsByRank( uint nStartingRank, uint nNumRows, uint nNumSpecs, void *pSpecs, void **ppResults, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int EnumerateStatsByXuid( XUID nUserId, uint nNumRows, uint nNumSpecs, void *pSpecs, void **ppResults, bool bAsync, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + + // Achievements + virtual int EnumerateAchievements( uint nUserIdx, uint64 xuid, uint nStartingIdx, uint nCount, void *pBuffer, uint nBufferBytes, bool bAsync = true, AsyncHandle_t *pAsyncHandle = NULL ) = 0; + virtual int AwardAchievement( uint nUserIdx, uint nAchievementId, AsyncHandle_t *ppOverlappedResult ) = 0; + virtual int AwardAvatarAsset( uint nUserIdx, uint nAwardId, AsyncHandle_t *ppOverlappedResult ) = 0; + + virtual void FinishContainerWrites( int iController ) = 0; + virtual uint GetContainerOpenResult( int iController ) = 0; + virtual uint OpenContainers( int iController ) = 0; + virtual void CloseContainers( int iController ) = 0; + + virtual void FinishAllContainerWrites( void ) = 0; + virtual void CloseAllContainers( void ) = 0; + + // + // Overlapped + // + virtual int Io_HasOverlappedIoCompleted( XOVERLAPPED *pOverlapped ) = 0; + + // + // XNet + // + virtual int NetRandom( byte *pb, unsigned numBytes ) = 0; + virtual DWORD NetGetTitleXnAddr( XNADDR *pxna ) = 0; + virtual int NetXnAddrToMachineId( const XNADDR *pxnaddr, uint64 *pqwMachineId ) = 0; + virtual int NetInAddrToXnAddr( const IN_ADDR ina, XNADDR *pxna, XNKID *pxnkid ) = 0; + virtual int NetXnAddrToInAddr( const XNADDR *pxna, const XNKID *pxnkid, IN_ADDR *pina ) = 0; + + // + // User + // + virtual XUSER_SIGNIN_STATE UserGetSigninState( int iCtrlr ) = 0; +}; + +#define XBOXSYSTEM_INTERFACE_VERSION "XboxSystemInterface001" + + + +// +// XOnline.lib abstraction +// + +#ifdef _X360 + +abstract_class IXOnline +{ +public: + virtual void RunFrame() = 0; + +public: + virtual DWORD XCancelOverlapped( PXOVERLAPPED pOverlapped ) = 0; + +public: + virtual DWORD XFriendsCreateEnumerator( + DWORD dwUserIndex, + DWORD dwStartingIndex, + DWORD dwFriendsToReturn, + DWORD *pcbBuffer, + HANDLE *ph + ) = 0; + + virtual INT XNetQosRelease( + XNQOS * pxnqos + ) = 0; + + virtual INT XNetQosServiceLookup( + DWORD dwFlags, + WSAEVENT hEvent, + XNQOS * * ppxnqos + ) = 0; + + virtual DWORD XInviteGetAcceptedInfo( + DWORD dwUserIndex, + XINVITE_INFO *pInfo + ) = 0; + + virtual DWORD XTitleServerCreateEnumerator( + LPCSTR pszServerInfo, + DWORD cItem, + PDWORD pcbBuffer, + PHANDLE phEnum + ) = 0; + + virtual INT XNetQosLookup( + UINT cxna, + const XNADDR * apxna[], + const XNKID * apxnkid[], + const XNKEY * apxnkey[], + UINT cina, + const IN_ADDR aina[], + const DWORD adwServiceId[], + UINT cProbes, + DWORD dwBitsPerSec, + DWORD dwFlags, + WSAEVENT hEvent, + XNQOS ** ppxnqos + ) = 0; + + virtual INT XNetUnregisterInAddr( + const IN_ADDR ina + ) = 0; + + virtual INT XNetServerToInAddr( + const IN_ADDR ina, + DWORD dwServiceId, + IN_ADDR *pina + ) = 0; + + virtual DWORD XSessionSearchEx( + DWORD dwProcedureIndex, + DWORD dwUserIndex, + DWORD dwNumResults, + DWORD dwNumUsers, + WORD wNumProperties, + WORD wNumContexts, + PXUSER_PROPERTY pSearchProperties, + PXUSER_CONTEXT pSearchContexts, + DWORD *pcbResultsBuffer, + PXSESSION_SEARCHRESULT_HEADER pSearchResults, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionGetDetails( + HANDLE hSession, + DWORD *pcbResultsBuffer, + XSESSION_LOCAL_DETAILS *pSessionDetails, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual INT XNetQosListen( + const XNKID * pxnkid, + const BYTE * pb, + UINT cb, + DWORD dwBitsPerSec, + DWORD dwFlags + ) = 0; + + virtual DWORD XSessionModify( + HANDLE hSession, + DWORD dwFlags, + DWORD dwMaxPublicSlots, + DWORD dwMaxPrivateSlots, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XNetGetTitleXnAddr( + XNADDR *pxna + ) = 0; + + virtual INT XNetRegisterKey( + const XNKID *pxnkid, + const XNKEY *pxnkey + ) = 0; + + virtual INT XNetUnregisterKey( + const XNKID *pxnkid + ) = 0; + + virtual INT XNetCreateKey( + XNKID *pxnkid, + XNKEY *pxnkey + ) = 0; + + virtual INT XNetReplaceKey( + const XNKID *pxnkidUnregister, + const XNKID * pxnkidReplace + ) = 0; + + virtual DWORD XSessionDelete( + HANDLE hSession, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionCreate( + DWORD dwFlags, + DWORD dwUserIndex, + DWORD dwMaxPublicSlots, + DWORD dwMaxPrivateSlots, + ULONGLONG *pqwSessionNonce, + PXSESSION_INFO pSessionInfo, + PXOVERLAPPED pXOverlapped, + HANDLE *ph + ) = 0; + + virtual DWORD XSessionSearchByID( + XNKID sessionID, + DWORD dwUserIndex, + DWORD *pcbResultsBuffer, + PXSESSION_SEARCHRESULT_HEADER pSearchResults, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionMigrateHost( + HANDLE hSession, + DWORD dwUserIndex, + XSESSION_INFO *pSessionInfo, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionJoinRemote( + HANDLE hSession, + DWORD dwXuidCount, + const XUID *pXuids, + const BOOL *pfPrivateSlots, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionJoinLocal( + HANDLE hSession, + DWORD dwUserCount, + const DWORD *pdwUserIndexes, + const BOOL *pfPrivateSlots, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionLeaveRemote( + HANDLE hSession, + DWORD dwXuidCount, + const XUID *pXuids, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionLeaveLocal( + HANDLE hSession, + DWORD dwUserCount, + const DWORD *pdwUserIndexes, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionEnd( + HANDLE hSession, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XSessionStart( + HANDLE hSession, + DWORD dwFlags, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XNetGetConnectStatus( + const IN_ADDR ina + ) = 0; + + virtual INT XNetInAddrToXnAddr( + const IN_ADDR ina, + XNADDR *pxna, + XNKID *pxnkid + ) = 0; + + virtual INT XNetXnAddrToInAddr( + const XNADDR *pxna, + const XNKID *pxnkid, + IN_ADDR *pina + ) = 0; + + virtual INT XNetConnect( + const IN_ADDR ina + ) = 0; + + virtual DWORD XUserReadProfileSettingsByXuid( + DWORD dwTitleId, + DWORD dwUserIndexRequester, + DWORD dwNumFor, + const XUID *pxuidFor, + DWORD dwNumSettingIds, + const DWORD *pdwSettingIds, + DWORD *pcbResults, + PXUSER_READ_PROFILE_SETTING_RESULT pResults, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XUserReadProfileSettings( + DWORD dwTitleId, + DWORD dwUserIndex, + DWORD dwNumSettingIds, + const DWORD *pdwSettingIds, + DWORD *pcbResults, + PXUSER_READ_PROFILE_SETTING_RESULT pResults, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XUserWriteProfileSettings( + DWORD dwUserIndex, + DWORD dwNumSettings, + const PXUSER_PROFILE_SETTING pSettings, + PXOVERLAPPED pXOverlapped + ) = 0; + + virtual DWORD XUserMuteListQuery( + DWORD dwUserIndex, + XUID XuidRemoteTalker, + BOOL *pfOnMuteList + ) = 0; + + virtual DWORD XSessionWriteStats( + HANDLE hSession, + XUID xuid, + DWORD dwNumViews, + const XSESSION_VIEW_PROPERTIES *pViews, + XOVERLAPPED *pXOverlapped + ) = 0; + + virtual DWORD XSessionFlushStats( + HANDLE hSession, + XOVERLAPPED *pXOverlapped + ) = 0; + + virtual DWORD XShowMarketplaceDownloadItemsUI( + DWORD dwUserIndex, + DWORD dwEntryPoint, + CONST ULONGLONG *pOfferIDs, + DWORD dwOfferIdCount, + HRESULT *phrResult, + PXOVERLAPPED pOverlapped + ) = 0; + + virtual DWORD XMarketplaceGetDownloadStatus( + DWORD dwUserIndex, + ULONGLONG qwOfferID, + LPDWORD pdwResult + ) = 0; + + virtual DWORD XShowMarketplaceUI( + DWORD dwUserIndex, + DWORD dwEntryPoint, + ULONGLONG qwOfferID, + DWORD dwContentCategories + ) = 0; + + +public: // party section + + virtual DWORD XShowGameInviteUI( + DWORD dwUserIndex, + CONST XUID *pXuidRecipients, + DWORD cRecipients, + LPCWSTR wszUnused + ) = 0; + + virtual HRESULT XShowPartyUI( + DWORD dwUserIndex + ) = 0; + + virtual DWORD XPartySendGameInvites( + DWORD dwUserIndex, + XOVERLAPPED *pOverlapped + ) = 0; + + virtual HRESULT XShowCommunitySessionsUI( + DWORD dwUserIndex, + DWORD dwSocialSessionsFlags + ) = 0; + +}; + +#define XONLINE_INTERFACE_VERSION "XOnlineInterface001" + +#endif + +#endif // IXBOXSYSTEM_H diff --git a/public/jigglebones.cpp b/public/jigglebones.cpp new file mode 100644 index 0000000..0848fbd --- /dev/null +++ b/public/jigglebones.cpp @@ -0,0 +1,643 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "tier1/convar.h" +#include "jigglebones.h" +#ifdef CLIENT_DLL +#include "engine/ivdebugoverlay.h" +#include "cdll_client_int.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +ConVar JiggleBoneDebug( "cl_jiggle_bone_debug", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +ConVar JiggleBoneDebugYawConstraints( "cl_jiggle_bone_debug_yaw_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +ConVar JiggleBoneDebugPitchConstraints( "cl_jiggle_bone_debug_pitch_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +ConVar JiggleBoneInvert( "cl_jiggle_bone_invert", "0", FCVAR_CHEAT ); +ConVar JiggleBoneSanity( "cl_jiggle_bone_sanity", "1", 0, "Prevent jiggle bones from pointing directly away from their target in case of numerical instability." ); + +static int s_id = 2; + +#ifdef CLIENT_DLL +char *VarArgs( const char *format, ... ); +#else +class CDummyOverlay +{ +public: + void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b, bool noDepthTest, float duration) {}; + void AddTextOverlay(const Vector &origin, float duration, const char *text) {} +}; + +SELECTANY CDummyOverlay *debugoverlay = new CDummyOverlay; + +char *VarArgs( const char *format, ... ) +{ + return NULL; +} +#endif + +//----------------------------------------------------------------------------- +JiggleData *CJiggleBones::GetJiggleData( int bone, float currenttime, const Vector &initBasePos, const Vector &initTipPos ) +{ + FOR_EACH_LL( m_jiggleBoneState, it ) + { + if ( m_jiggleBoneState[it].bone == bone ) + { + JiggleData *data = &m_jiggleBoneState[it]; + if ( !IsFinite( data->lastUpdate ) ) + { + Warning( "lastUpdate NaN\n" ); + } + if ( !data->basePos.IsValid() ) + { + Warning( "basePos NaN\n" ); + } + if ( !data->baseLastPos.IsValid() ) + { + Warning( "baseLastPos NaN\n" ); + } + if ( !data->baseVel.IsValid() ) + { + Warning( "baseVel NaN\n" ); + } + if ( !data->baseAccel.IsValid() ) + { + Warning( "baseAccel NaN\n" ); + } + if ( !data->tipPos.IsValid() ) + { + Warning( "tipPos NaN\n" ); + } + if ( !data->tipVel.IsValid() ) + { + Warning( "tipVel NaN\n" ); + } + if ( !data->tipAccel.IsValid() ) + { + Warning( "tipAccel NaN\n" ); + } + return &m_jiggleBoneState[it]; + } + } + + JiggleData data; + data.Init( bone, currenttime, initBasePos, initTipPos ); + data.id = s_id++; + + int idx = m_jiggleBoneState.AddToHead( data ); + if ( idx == m_jiggleBoneState.InvalidIndex() ) + return NULL; + + return &m_jiggleBoneState[idx]; +} + + +//----------------------------------------------------------------------------- +/** + * Do spring physics calculations and update "jiggle bone" matrix + * (Michael Booth, Turtle Rock Studios) + */ +void CJiggleBones::BuildJiggleTransformations( int boneIndex, float currenttime, const mstudiojigglebone_t *jiggleInfo, const matrix3x4_t &goalMX, matrix3x4_t &boneMX ) +{ + Vector goalBasePosition; + MatrixPosition( goalMX, goalBasePosition ); + + Vector goalForward, goalUp, goalLeft; + MatrixGetColumn( goalMX, 0, goalLeft ); + MatrixGetColumn( goalMX, 1, goalUp ); + MatrixGetColumn( goalMX, 2, goalForward ); + + // compute goal tip position + Vector goalTip = goalBasePosition + jiggleInfo->length * goalForward; + + JiggleData *data = GetJiggleData( boneIndex, currenttime, goalBasePosition, goalTip ); + if ( !data ) + { + return; + } + + if ( currenttime - data->lastUpdate > 0.5f ) + { + data->Init( boneIndex, currenttime, goalBasePosition, goalTip ); + } + + if ( JiggleBoneInvert.GetBool() ) + { + data->basePos = -data->basePos; + data->baseLastPos = -data->baseLastPos; + data->baseVel = -data->baseVel; + data->baseAccel = -data->baseAccel; + data->tipPos = -data->tipPos; + data->tipVel = -data->tipVel; + data->tipAccel = -data->tipAccel; + } + +#ifdef CLIENT_DLL + if ( data->id == JiggleBoneDebug.GetInt() ) + { + int line = 20; + engine->Con_NPrintf( line++, "basePos %f %f %f", data->basePos.x, data->basePos.y, data->basePos.z ); + engine->Con_NPrintf( line++, "baseLastPos %f %f %f", data->baseLastPos.x, data->baseLastPos.y, data->baseLastPos.z ); + engine->Con_NPrintf( line++, "baseVel %f %f %f", data->baseVel.x, data->baseVel.y, data->baseVel.z ); + engine->Con_NPrintf( line++, "baseAccel %f %f %f", data->baseAccel.x, data->baseAccel.y, data->baseAccel.z ); + engine->Con_NPrintf( line++, "tipPos %f %f %f", data->tipPos.x, data->tipPos.y, data->tipPos.z ); + engine->Con_NPrintf( line++, "tipVel %f %f %f", data->tipVel.x, data->tipVel.y, data->tipVel.z ); + engine->Con_NPrintf( line++, "tipAccel %f %f %f", data->tipAccel.x, data->tipAccel.y, data->tipAccel.z ); + } +#endif + + if ( JiggleBoneSanity.GetBool() ) + { + Vector goalDir = goalTip - goalBasePosition; + goalDir.NormalizeInPlace(); + Vector dataDir = data->tipPos - goalBasePosition; + dataDir.NormalizeInPlace(); + + float dot = goalDir.Dot( dataDir ); + if ( dot < -0.9f ) // if we end up pointing almost completely away, just reset + { + data->Init( boneIndex, currenttime, goalBasePosition, goalTip ); + } + } + + //Vector bodyVel; + //EstimateAbsVelocity( bodyVel ); + + // limit maximum deltaT to avoid simulation blowups + // if framerate gets very low, jiggle will run in slow motion + const float thirtyHZ = 0.0333f; + const float thousandHZ = 0.001f; + float deltaT = clamp( currenttime - data->lastUpdate, thousandHZ, thirtyHZ ); + data->lastUpdate = currenttime; + + // + // Bone tip flex + // + if (jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID)) + { + // apply gravity in global space + data->tipAccel.z -= jiggleInfo->tipMass; + + if (jiggleInfo->flags & JIGGLE_IS_FLEXIBLE) + { + // decompose into local coordinates + Vector error = goalTip - data->tipPos; + + Vector localError; + localError.x = DotProduct( goalLeft, error ); + localError.y = DotProduct( goalUp, error ); + localError.z = DotProduct( goalForward, error ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + localVel.z = 0.0f; // TODO: this was uninitialized, but is being used + + // yaw spring + float yawAccel = jiggleInfo->yawStiffness * localError.x - jiggleInfo->yawDamping * localVel.x; + + // pitch spring + float pitchAccel = jiggleInfo->pitchStiffness * localError.y - jiggleInfo->pitchDamping * localVel.y; + + if (jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT) + { + // drive tip towards goal tip position + data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp; + } + else + { + // allow flex along length of spring + localVel.z = DotProduct( goalForward, data->tipVel ); + + // along spring + float alongAccel = jiggleInfo->alongStiffness * localError.z - jiggleInfo->alongDamping * localVel.z; + + // drive tip towards goal tip position + data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp + alongAccel * goalForward; + } + } + + + // simple euler integration + data->tipVel += data->tipAccel * deltaT; + data->tipPos += data->tipVel * deltaT; + + // clear this timestep's accumulated accelerations + data->tipAccel = vec3_origin; + + // + // Apply optional constraints + // + if (jiggleInfo->flags & (JIGGLE_HAS_YAW_CONSTRAINT | JIGGLE_HAS_PITCH_CONSTRAINT)) + { + // find components of spring vector in local coordinate system + Vector along = data->tipPos - goalBasePosition; + Vector localAlong; + localAlong.x = DotProduct( goalLeft, along ); + localAlong.y = DotProduct( goalUp, along ); + localAlong.z = DotProduct( goalForward, along ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + localVel.z = DotProduct( goalForward, data->tipVel ); + + if (jiggleInfo->flags & JIGGLE_HAS_YAW_CONSTRAINT) + { + // enforce yaw constraints in local XZ plane + float yawError = atan2( localAlong.x, localAlong.z ); + + bool isAtLimit = false; + float yaw = 0.0f; + + if (yawError < jiggleInfo->minYaw) + { + // at angular limit + isAtLimit = true; + yaw = jiggleInfo->minYaw; + } + else if (yawError > jiggleInfo->maxYaw) + { + // at angular limit + isAtLimit = true; + yaw = jiggleInfo->maxYaw; + } + + if (isAtLimit) + { + float sy, cy; + SinCos( yaw, &sy, &cy ); + + // yaw matrix + matrix3x4_t yawMatrix; + + yawMatrix[0][0] = cy; + yawMatrix[1][0] = 0; + yawMatrix[2][0] = -sy; + + yawMatrix[0][1] = 0; + yawMatrix[1][1] = 1.0f; + yawMatrix[2][1] = 0; + + yawMatrix[0][2] = sy; + yawMatrix[1][2] = 0; + yawMatrix[2][2] = cy; + + yawMatrix[0][3] = 0; + yawMatrix[1][3] = 0; + yawMatrix[2][3] = 0; + + // global coordinates of limit + matrix3x4_t limitMatrix; + ConcatTransforms( goalMX, yawMatrix, limitMatrix ); + + Vector limitLeft( limitMatrix.m_flMatVal[0][0], + limitMatrix.m_flMatVal[1][0], + limitMatrix.m_flMatVal[2][0] ); + + Vector limitUp( limitMatrix.m_flMatVal[0][1], + limitMatrix.m_flMatVal[1][1], + limitMatrix.m_flMatVal[2][1] ); + + Vector limitForward( limitMatrix.m_flMatVal[0][2], + limitMatrix.m_flMatVal[1][2], + limitMatrix.m_flMatVal[2][2] ); + + if (JiggleBoneDebugYawConstraints.GetBool()) + { + float dT = 0.01f; + const float axisSize = 10.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); + } + + Vector limitAlong( DotProduct( limitLeft, along ), + DotProduct( limitUp, along ), + DotProduct( limitForward, along ) ); + + // clip to limit plane + data->tipPos = goalBasePosition + limitAlong.y * limitUp + limitAlong.z * limitForward; + + // yaw friction - rubbing along limit plane + Vector limitVel; + limitVel.x = 0.0f; // TODO: this was uninitialized, and is used when yawBounce is non-zero! + limitVel.y = DotProduct( limitUp, data->tipVel ); + limitVel.z = DotProduct( limitForward, data->tipVel ); + + data->tipAccel -= jiggleInfo->yawFriction * (limitVel.y * limitUp + limitVel.z * limitForward); + + // update velocity reaction to hitting constraint + data->tipVel = -jiggleInfo->yawBounce * limitVel.x * limitLeft + limitVel.y * limitUp + limitVel.z * limitForward; + + // update along vectors for use by pitch constraint + along = data->tipPos - goalBasePosition; + localAlong.x = DotProduct( goalLeft, along ); + localAlong.y = DotProduct( goalUp, along ); + localAlong.z = DotProduct( goalForward, along ); + + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + localVel.z = DotProduct( goalForward, data->tipVel ); + } + } + + + if (jiggleInfo->flags & JIGGLE_HAS_PITCH_CONSTRAINT) + { + // enforce pitch constraints in local YZ plane + float pitchError = atan2( localAlong.y, localAlong.z ); + + bool isAtLimit = false; + float pitch = 0.0f; + + if (pitchError < jiggleInfo->minPitch) + { + // at angular limit + isAtLimit = true; + pitch = jiggleInfo->minPitch; + } + else if (pitchError > jiggleInfo->maxPitch) + { + // at angular limit + isAtLimit = true; + pitch = jiggleInfo->maxPitch; + } + + if (isAtLimit) + { + float sp, cp; + SinCos( pitch, &sp, &cp ); + + // pitch matrix + matrix3x4_t pitchMatrix; + + pitchMatrix[0][0] = 1.0f; + pitchMatrix[1][0] = 0; + pitchMatrix[2][0] = 0; + + pitchMatrix[0][1] = 0; + pitchMatrix[1][1] = cp; + pitchMatrix[2][1] = -sp; + + pitchMatrix[0][2] = 0; + pitchMatrix[1][2] = sp; + pitchMatrix[2][2] = cp; + + pitchMatrix[0][3] = 0; + pitchMatrix[1][3] = 0; + pitchMatrix[2][3] = 0; + + // global coordinates of limit + matrix3x4_t limitMatrix; + ConcatTransforms( goalMX, pitchMatrix, limitMatrix ); + + Vector limitLeft( limitMatrix.m_flMatVal[0][0], + limitMatrix.m_flMatVal[1][0], + limitMatrix.m_flMatVal[2][0] ); + + Vector limitUp( limitMatrix.m_flMatVal[0][1], + limitMatrix.m_flMatVal[1][1], + limitMatrix.m_flMatVal[2][1] ); + + Vector limitForward( limitMatrix.m_flMatVal[0][2], + limitMatrix.m_flMatVal[1][2], + limitMatrix.m_flMatVal[2][2] ); + + if (JiggleBoneDebugPitchConstraints.GetBool()) + { + float dT = 0.01f; + const float axisSize = 10.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); + } + + Vector limitAlong( DotProduct( limitLeft, along ), + DotProduct( limitUp, along ), + DotProduct( limitForward, along ) ); + + // clip to limit plane + data->tipPos = goalBasePosition + limitAlong.x * limitLeft + limitAlong.z * limitForward; + + // pitch friction - rubbing along limit plane + Vector limitVel; + limitVel.x = 0.0f; // TODO: this was uninitialized, but is being used + limitVel.y = DotProduct( limitUp, data->tipVel ); + limitVel.z = DotProduct( limitForward, data->tipVel ); + + data->tipAccel -= jiggleInfo->pitchFriction * (limitVel.x * limitLeft + limitVel.z * limitForward); + + // update velocity reaction to hitting constraint + data->tipVel = limitVel.x * limitLeft - jiggleInfo->pitchBounce * limitVel.y * limitUp + limitVel.z * limitForward; + } + } + } + + // needed for matrix assembly below + Vector forward = data->tipPos - goalBasePosition; + forward.NormalizeInPlace(); + + if (jiggleInfo->flags & JIGGLE_HAS_ANGLE_CONSTRAINT) + { + // enforce max angular error + Vector error = goalTip - data->tipPos; + float dot = DotProduct( forward, goalForward ); + float angleBetween = acos( dot ); + if (dot < 0.0f) + { + angleBetween = 2.0f * M_PI - angleBetween; + } + + if (angleBetween > jiggleInfo->angleLimit) + { + // at angular limit + float maxBetween = jiggleInfo->length * sin( jiggleInfo->angleLimit ); + + Vector delta = goalTip - data->tipPos; + delta.NormalizeInPlace(); + + data->tipPos = goalTip - maxBetween * delta; + + forward = data->tipPos - goalBasePosition; + forward.NormalizeInPlace(); + } + } + + if (jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT) + { + // enforce spring length + data->tipPos = goalBasePosition + jiggleInfo->length * forward; + + // zero velocity along forward bone axis + data->tipVel -= DotProduct( data->tipVel, forward ) * forward; + } + + // + // Build bone matrix to align along current tip direction + // + Vector left = CrossProduct( goalUp, forward ); + left.NormalizeInPlace(); + + Vector up = CrossProduct( forward, left ); + + boneMX[0][0] = left.x; + boneMX[1][0] = left.y; + boneMX[2][0] = left.z; + boneMX[0][1] = up.x; + boneMX[1][1] = up.y; + boneMX[2][1] = up.z; + boneMX[0][2] = forward.x; + boneMX[1][2] = forward.y; + boneMX[2][2] = forward.z; + + boneMX[0][3] = goalBasePosition.x; + boneMX[1][3] = goalBasePosition.y; + boneMX[2][3] = goalBasePosition.z; + } + + + // + // Bone base flex + // + if (jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING) + { + // gravity + data->baseAccel.z -= jiggleInfo->baseMass; + + // simple spring + Vector error = goalBasePosition - data->basePos; + data->baseAccel += jiggleInfo->baseStiffness * error - jiggleInfo->baseDamping * data->baseVel; + + data->baseVel += data->baseAccel * deltaT; + data->basePos += data->baseVel * deltaT; + + // clear this timestep's accumulated accelerations + data->baseAccel = vec3_origin; + + // constrain to limits + error = data->basePos - goalBasePosition; + Vector localError; + localError.x = DotProduct( goalLeft, error ); + localError.y = DotProduct( goalUp, error ); + localError.z = DotProduct( goalForward, error ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->baseVel ); + localVel.y = DotProduct( goalUp, data->baseVel ); + localVel.z = DotProduct( goalForward, data->baseVel ); + + // horizontal constraint + if (localError.x < jiggleInfo->baseMinLeft) + { + localError.x = jiggleInfo->baseMinLeft; + + // friction + data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); + } + else if (localError.x > jiggleInfo->baseMaxLeft) + { + localError.x = jiggleInfo->baseMaxLeft; + + // friction + data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); + } + + if (localError.y < jiggleInfo->baseMinUp) + { + localError.y = jiggleInfo->baseMinUp; + + // friction + data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); + } + else if (localError.y > jiggleInfo->baseMaxUp) + { + localError.y = jiggleInfo->baseMaxUp; + + // friction + data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); + } + + if (localError.z < jiggleInfo->baseMinForward) + { + localError.z = jiggleInfo->baseMinForward; + + // friction + data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); + } + else if (localError.z > jiggleInfo->baseMaxForward) + { + localError.z = jiggleInfo->baseMaxForward; + + // friction + data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); + } + + data->basePos = goalBasePosition + localError.x * goalLeft + localError.y * goalUp + localError.z * goalForward; + + + // fix up velocity + data->baseVel = (data->basePos - data->baseLastPos) / deltaT; + data->baseLastPos = data->basePos; + + + if (!(jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID))) + { + // no tip flex - use bone's goal orientation + boneMX = goalMX; + } + + // update bone position + MatrixSetColumn( data->basePos, 3, boneMX ); + } + else if (!(jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID))) + { + // no flex at all - just use goal matrix + boneMX = goalMX; + } + + + // debug display + if ( JiggleBoneDebug.GetInt() == 1 || JiggleBoneDebug.GetInt() == data->id ) + { + float dT = 0.01f; + const float axisSize = 5.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalLeft, 255, 0, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalUp, 0, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalForward, 0, 0, 255, true, dT ); + + debugoverlay->AddTextOverlay( goalBasePosition, dT, VarArgs( "%d", data->id ) ); + + const float sz = 1.0f; + + if (jiggleInfo->flags & (JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID)) + { + debugoverlay->AddLineOverlay( goalBasePosition, + data->tipPos, 255, 255, 0, true, dT ); + + debugoverlay->AddLineOverlay( data->tipPos + Vector( -sz, 0, 0 ), + data->tipPos + Vector( sz, 0, 0 ), 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, -sz, 0 ), + data->tipPos + Vector( 0, sz, 0 ), 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, 0, -sz ), + data->tipPos + Vector( 0, 0, sz ), 0, 255, 255, true, dT ); + } + + if (jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING) + { + debugoverlay->AddLineOverlay( data->basePos + Vector( -sz, 0, 0 ), + data->basePos + Vector( sz, 0, 0 ), 255, 0, 255, true, dT ); + debugoverlay->AddLineOverlay( data->basePos + Vector( 0, -sz, 0 ), + data->basePos + Vector( 0, sz, 0 ), 255, 0, 255, true, dT ); + debugoverlay->AddLineOverlay( data->basePos + Vector( 0, 0, -sz ), + data->basePos + Vector( 0, 0, sz ), 255, 0, 255, true, dT ); + } + } +} + diff --git a/public/jigglebones.h b/public/jigglebones.h new file mode 100644 index 0000000..167248f --- /dev/null +++ b/public/jigglebones.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//=============================================================================// +#ifndef C_JIGGLEBONES_H +#define C_JIGGLEBONES_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "studio.h" +#include "UtlVector.h" +#include "utllinkedlist.h" + +//----------------------------------------------------------------------------- +/** + * JiggleData is the instance-specific data for a jiggle bone + */ +struct JiggleData +{ + void Init( int initBone, float currenttime, const Vector &initBasePos, const Vector &initTipPos ) + { + bone = initBone; + + lastUpdate = currenttime; + + basePos = initBasePos; + baseLastPos = basePos; + baseVel.Init(); + baseAccel.Init(); + + tipPos = initTipPos; + tipVel.Init(); + tipAccel.Init(); + } + + int bone; + int id; + + float lastUpdate; // based on gpGlobals->realtime + + Vector basePos; // position of the base of the jiggle bone + Vector baseLastPos; + Vector baseVel; + Vector baseAccel; + + Vector tipPos; // position of the tip of the jiggle bone + Vector tipVel; + Vector tipAccel; +}; + +class CJiggleBones +{ +public: + JiggleData * GetJiggleData( int bone, float currenttime, const Vector &initBasePos, const Vector &initTipPos ); + void BuildJiggleTransformations( int boneIndex, float currentime, const mstudiojigglebone_t *jiggleParams, const matrix3x4_t &goalMX, matrix3x4_t &boneMX ); + + CUtlLinkedList< JiggleData > m_jiggleBoneState; +}; + + +extern void DevMsgRT( char const* pMsg, ... ); + +#endif // C_BASEANIMATING_H diff --git a/public/jpeglib/jconfig.h b/public/jpeglib/jconfig.h new file mode 100644 index 0000000..7e291c7 --- /dev/null +++ b/public/jpeglib/jconfig.h @@ -0,0 +1,45 @@ +/* jconfig.vc --- jconfig.h for Microsoft Visual C++ on Windows 95 or NT. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* we presume a 32-bit flat memory model */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Microsoft has setmode() */ +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/public/jpeglib/jmorecfg.h b/public/jpeglib/jmorecfg.h new file mode 100644 index 0000000..4645b8a --- /dev/null +++ b/public/jpeglib/jmorecfg.h @@ -0,0 +1,374 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef _BASETSD_H_ +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +typedef long INT32; +#endif +#endif // #ifndef _BASETSD_H_ + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#ifdef GNUC +#define GLOBAL(type) extern "C" __attribute__ ((visibility("default"))) type +#else +#define GLOBAL(type) type +#endif + +/* a reference to a GLOBAL function: */ +#ifdef GNUC +#define EXTERN(type) extern "C" type +#else +#define EXTERN(type) extern type +#endif + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/public/jpeglib/jpeglib.h b/public/jpeglib/jpeglib.h new file mode 100644 index 0000000..0bf3c9e --- /dev/null +++ b/public/jpeglib/jpeglib.h @@ -0,0 +1,1113 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma warning( disable : 4100 ) // unused formal parameter +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#pragma warning( disable : 4244 ) // warning C4244: '+=' : conversion from 'int' to 'JSAMPLE', possible loss of data +#pragma warning( disable : 4267 ) // warning C4267: '+=' : conversion from 'size_t' to 'long', possible loss of data + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +#ifdef JPEGLIB_USE_STDIO // allow the use of these functions in specific cases where + // reading/writing jpeg files outside the game directory tree is required. +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); +#endif + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +} +#endif + + +#endif /* JPEGLIB_H */ diff --git a/public/kevvaluescompiler.cpp b/public/kevvaluescompiler.cpp new file mode 100644 index 0000000..1e86781 --- /dev/null +++ b/public/kevvaluescompiler.cpp @@ -0,0 +1,435 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "keyvaluescompiler.h" +#include "filesystem.h" +#include "tier1/KeyValues.h" +#include "tier2/tier2.h" + +bool CRunTimeKeyValuesStringTable::ReadStringTable( int numStrings, CUtlBuffer& buf ) +{ + Assert( m_Strings.Count() == 0 ); + + CUtlVector< int > offsets; + offsets.EnsureCapacity( numStrings ); + + offsets.CopyArray( (int *)( buf.PeekGet() ), numStrings ); + + // Skip over data + buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + numStrings * sizeof( int ) ); + + int stringSize = buf.GetInt(); + + // Read in the string table + m_Strings.EnsureCapacity( numStrings ); + int i; + for ( i = 0 ; i < numStrings; ++i ) + { + m_Strings.AddToTail( (const char *)buf.PeekGet( offsets[ i ] ) ); + } + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, buf.TellGet() + stringSize ); + + return true; +} + +void CCompiledKeyValuesWriter::BuildKVData_R( KeyValues *kv, int parent ) +{ + // Add self + KVInfo_t info; + info.key = m_StringTable.AddString( kv->GetName() ); + info.value = m_StringTable.AddString( kv->GetString() ); + + info.SetSubTree( kv->GetFirstSubKey() != NULL ? true : false ); + info.SetParent( parent ); + + int newParent = m_Data.AddToTail( info ); + + // Then add children + for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) + { + BuildKVData_R( sub, newParent ); + } + + // Then add peers + if ( parent == -1 ) + { + if ( kv->GetNextKey() ) + { + BuildKVData_R( kv->GetNextKey(), parent ); + } + } +} + +void CCompiledKeyValuesWriter::Describe( const KVFile_t& file ) +{ + Msg( "file( %s )\n", m_StringTable.String( file.filename ) ); + + int c = file.numElements; + for ( int i = 0; i < c; ++i ) + { + KVInfo_t &info = m_Data[ file.firstElement + i ]; + if ( info.IsSubTree() ) + { + Msg( "%d: %s -> subtree at parent %i\n", + file.firstElement + i, + m_StringTable.String( info.key ), + info.GetParent() ); + } + else + { + Msg( "%d: %s -> %s at parent %i\n", + file.firstElement + i, + m_StringTable.String( info.key ), + m_StringTable.String( info.value ), + info.GetParent() ); + } + } +} + +void CCompiledKeyValuesWriter::AppendKeyValuesFile( char const *filename ) +{ + KVFile_t kvf; + kvf.filename = m_StringTable.AddString( filename ); + kvf.firstElement = m_Data.Count(); + + KeyValues *kv = new KeyValues( filename ); + if ( kv->LoadFromFile( g_pFullFileSystem, filename ) ) + { + // Add to dictionary + // do a depth first traversal of the keyvalues + BuildKVData_R( kv, -1 ); + } + kv->deleteThis(); + + kvf.numElements = m_Data.Count() - kvf.firstElement; + +// Describe( kvf ); + + m_Files.AddToTail( kvf ); +} + +void CCompiledKeyValuesWriter::WriteData( CUtlBuffer& buf ) +{ + int c = m_Data.Count(); + buf.PutInt( c ); + for ( int i = 0; i < c; ++i ) + { + KVInfo_t &info = m_Data[ i ]; + buf.PutShort( info.key ); + buf.PutShort( info.value ); + buf.PutShort( info.GetParent() ); + buf.PutChar( info.IsSubTree() ? 1 : 0 ); + } +} + +void CCompiledKeyValuesWriter::WriteFiles( CUtlBuffer &buf ) +{ + int c = m_Files.Count(); + buf.PutInt( c ); + for ( int i = 0; i < c; ++i ) + { + KVFile_t &file = m_Files[ i ]; + buf.PutShort( file.filename ); + buf.PutShort( file.firstElement ); + buf.PutShort( file.numElements ); + } +} + +void CCompiledKeyValuesWriter::WriteStringTable( CUtlBuffer& buf ) +{ + int i; + CUtlVector< int > offsets; + + CUtlBuffer stringBuffer; + + offsets.AddToTail( stringBuffer.TellPut() ); + + stringBuffer.PutString( "" ); + // save all the rest + int c = m_StringTable.GetNumStrings(); + for ( i = 1; i < c; i++) + { + offsets.AddToTail( stringBuffer.TellPut() ); + stringBuffer.PutString( m_StringTable.String( i ) ); + } + + buf.Put( offsets.Base(), offsets.Count() * sizeof( int ) ); + + buf.PutInt( stringBuffer.TellPut() ); + buf.Put( stringBuffer.Base(), stringBuffer.TellPut() ); +} + +void CCompiledKeyValuesWriter::WriteFile( char const *outfile ) +{ + CUtlBuffer buf; + + // Write the data file out + KVHeader_t header; + header.fileid = COMPILED_KEYVALUES_ID; + header.version = COMPILED_KEYVALUES_VERSION; + header.numStrings = m_StringTable.GetNumStrings(); + + buf.Put( &header, sizeof( header ) ); + + WriteStringTable( buf ); + WriteData( buf ); + WriteFiles( buf ); + + g_pFullFileSystem->WriteFile( outfile, NULL, buf ); +} + +CCompiledKeyValuesReader::CCompiledKeyValuesReader() + : m_Dict( 0, 0, FileInfo_t::Less ) +{ +} + +int CCompiledKeyValuesReader::First() const +{ + return m_Dict.FirstInorder(); +} + +int CCompiledKeyValuesReader::Next( int i ) const +{ + return m_Dict.NextInorder( i ); +} + +int CCompiledKeyValuesReader::InvalidIndex() const +{ + return m_Dict.InvalidIndex(); +} + +void CCompiledKeyValuesReader::GetFileName( int index, char *buf, size_t bufsize ) +{ + Assert( buf ); + buf[ 0 ] = 0; + FileNameHandle_t& handle = m_Dict[ index ].hFile; + g_pFullFileSystem->String( handle, buf, bufsize ); +} + +bool CCompiledKeyValuesReader::LoadFile( char const *filename ) +{ + int i; + m_LoadBuffer.Purge(); + + g_pFullFileSystem->ReadFile( filename, NULL, m_LoadBuffer ); + + KVHeader_t header; + m_LoadBuffer.Get( &header, sizeof( header ) ); + + if ( header.fileid != COMPILED_KEYVALUES_ID ) + { + return false; + } + + if ( header.version != COMPILED_KEYVALUES_VERSION ) + { + return false; + } + + if ( !m_StringTable.ReadStringTable( header.numStrings, m_LoadBuffer ) ) + { + return false; + } + + // Now parse the data + int dataCount = m_LoadBuffer.GetInt(); + m_Data.EnsureCapacity( dataCount ); + for ( i = 0; i < dataCount; ++i ) + { + KVInfo_t info; + info.key = m_LoadBuffer.GetShort(); + info.value = m_LoadBuffer.GetShort(); + info.SetParent( m_LoadBuffer.GetShort() ); + info.SetSubTree( m_LoadBuffer.GetChar() == 1 ? true : false ); + m_Data.AddToTail( info ); + } + + int fileCount = m_LoadBuffer.GetInt(); + for ( i = 0; i < fileCount; ++i ) + { + FileInfo_t kvf; + short fileNameString = m_LoadBuffer.GetShort(); + + kvf.hFile = g_pFullFileSystem->FindOrAddFileName( m_StringTable.Lookup( fileNameString ) ); + kvf.nFirstIndex = m_LoadBuffer.GetShort(); + kvf.nCount = m_LoadBuffer.GetShort(); + + m_Dict.Insert( kvf ); + } + + return true; +} + +struct CreateHelper_t +{ + int index; + KeyValues *kv; + KeyValues *tail; + + static bool Less( const CreateHelper_t& lhs, const CreateHelper_t& rhs ) + { + return lhs.index < rhs.index; + } +}; + +KeyValues *CCompiledKeyValuesReader::CreateFromData( const FileInfo_t& info ) +{ + KeyValues *head = new KeyValues( "" ); + if ( CreateInPlaceFromData( *head, info ) ) + { + return head; + } + else + { + head->deleteThis(); + return NULL; + } +} + +bool CCompiledKeyValuesReader::CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ) +{ + int first = info.nFirstIndex; + int num = info.nCount; + + KeyValues *root = NULL; + KeyValues *tail = NULL; + + CUtlRBTree< CreateHelper_t, int > helper( 0, 0, CreateHelper_t::Less ); + + for ( int i = 0; i < num; ++i ) + { + int offset = first + i; + KVInfo_t& info = m_Data[ offset ]; + + if ( info.GetParent() != -1 ) + { + CreateHelper_t search; + search.index = info.GetParent(); + int idx = helper.Find( search ); + if ( idx == helper.InvalidIndex() ) + { + return false; + } + + KeyValues *parent = helper[ idx ].kv; + Assert( parent ); + + KeyValues *sub = new KeyValues( m_StringTable.Lookup( info.key ) ); + + if ( !info.IsSubTree() ) + { + sub->SetStringValue(m_StringTable.Lookup( info.value ) ); + } + + if ( !parent->GetFirstSubKey() ) + { + parent->AddSubKey( sub ); + } + else + { + KeyValues *last = helper[ idx ].tail; + last->SetNextKey( sub ); + } + + helper[ idx ].tail = sub; + + CreateHelper_t insert; + insert.index = offset; + insert.kv = sub; + insert.tail = NULL; + helper.Insert( insert ); + } + else + { + if ( !root ) + { + root = &head; + root->SetName( m_StringTable.Lookup( info.key ) ); + tail = root; + + CreateHelper_t insert; + insert.index = offset; + insert.kv = root; + insert.tail = NULL; + helper.Insert( insert ); + } + else + { + CreateHelper_t insert; + insert.index = offset; + insert.kv = new KeyValues( m_StringTable.Lookup( info.key ) ); + insert.tail = NULL; + helper.Insert( insert ); + + tail->SetNextKey( insert.kv ); + tail = insert.kv; + } + } + } + return true; +} + + +bool CCompiledKeyValuesReader::InstanceInPlace( KeyValues& head, char const *kvfilename ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return false; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + return CreateInPlaceFromData( head, info ); +} + +KeyValues *CCompiledKeyValuesReader::Instance( char const *kvfilename ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return NULL; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + return CreateFromData( info ); +} + +bool CCompiledKeyValuesReader::LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ) +{ + char sz[ 512 ]; + Q_strncpy( sz, kvfilename, sizeof( sz ) ); + Q_FixSlashes( sz ); + + FileInfo_t search; + search.hFile = g_pFullFileSystem->FindOrAddFileName( sz ); + + int idx = m_Dict.Find( search ); + if ( idx == m_Dict.InvalidIndex() ) + { + return false; + } + + const FileInfo_t& info = m_Dict[ idx ]; + + Q_strncpy( outbuf, m_StringTable.Lookup( m_Data[ info.nFirstIndex ].key ), bufsize ); + return true; +} diff --git a/public/keyframe/keyframe.cpp b/public/keyframe/keyframe.cpp new file mode 100644 index 0000000..5ad622d --- /dev/null +++ b/public/keyframe/keyframe.cpp @@ -0,0 +1,501 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include +#include + +typedef unsigned char byte; +#pragma warning(disable:4244) + +#include "tier0/dbg.h" +#include "mathlib/vector.h" +#include "keyframe.h" +#include "mathlib/mathlib.h" +#include "rope_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// +// Implementation of keyframe.h interface +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Key Frames +//----------------------------------------------------------------------------- +#define HIGHEST_KEYFRAME 3 +#define LOWEST_KEYFRAME -3 + +#define TOTAL_KEYFRAMES (HIGHEST_KEYFRAME - LOWEST_KEYFRAME + 1) + +// + +struct KeyFrame_t +{ + Vector vPos; + Quaternion qRot; +}; + + +KeyFrame_t g_KeyFrames[ TOTAL_KEYFRAMES ]; +KeyFrame_t *g_KeyFramePtr = &g_KeyFrames[ -LOWEST_KEYFRAME ]; // points to the middle keyframe, keyframe 0 + +bool Motion_SetKeyAngles( int keyNum, Quaternion &quatAngles ) +{ + if ( keyNum > HIGHEST_KEYFRAME || keyNum < LOWEST_KEYFRAME ) + return false; + + g_KeyFramePtr[keyNum].qRot = quatAngles; + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Time Modifier function enumeration & implementation +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +typedef float (*TimeModifierFunc_t)(float); + +typedef struct +{ + char *szName; + TimeModifierFunc_t pFunc; + +} TimeModifier_t; + +float TimeModifierFunc_Linear( float time ) +{ + return time; +} + +float TimeModifierFunc_Cosine( float time ) +{ + return ( cos((time+1) * M_PI) * 0.5 ) + 0.5; +} + +float TimeModifierFunc_TimeSquared( float time ) +{ + return (time * time); +} + +TimeModifier_t g_TimeModifiers[] = +{ + { "Linear", TimeModifierFunc_Linear }, + { "Accel/Deaccel (cosine)", TimeModifierFunc_Cosine }, + { "Accel (time*time)", TimeModifierFunc_TimeSquared }, +}; + +int Motion_GetNumberOfTimeModifiers( void ) +{ + return ARRAYSIZE(g_TimeModifiers); +} + +bool Motion_GetTimeModifierDetails( int timeInterpNum, char **outName ) +{ + if ( timeInterpNum < 0 || timeInterpNum >= Motion_GetNumberOfTimeModifiers() ) + { + return false; + } + + if ( !g_TimeModifiers[0].szName || !g_TimeModifiers[0].pFunc ) + { + return false; + } + + if ( outName ) + *outName = g_TimeModifiers[0].szName; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// timeModifierFuncNum - +// *outNewTime - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Motion_CalculateModifiedTime( float time, int timeModifierFuncNum, float *outNewTime ) +{ + *outNewTime = g_TimeModifiers[timeModifierFuncNum].pFunc( time ); + return true; +} + + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Position interpolator function enumeration & implementation +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------ // +// Linear position interpolator. +// ------------------------------------------------------------------------------------ // + +class CPositionInterpolator_Linear : public IPositionInterpolator +{ +public: + virtual void Release(); + virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void SetKeyPosition( int keyNum, Vector const &vPos ); + virtual void InterpolatePosition( float time, Vector &vOut ); + virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; } +}; + +CPositionInterpolator_Linear g_LinearInterpolator; + +IPositionInterpolator* GetLinearInterpolator() +{ + return &g_LinearInterpolator; +} + +void CPositionInterpolator_Linear::Release() +{ +} + +void CPositionInterpolator_Linear::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +{ + *outName = "Linear"; + *outMinKeyReq = 0; + *outMaxKeyReq = 1; +} + +void CPositionInterpolator_Linear::SetKeyPosition( int keyNum, Vector const &vPos ) +{ + Assert ( keyNum <= HIGHEST_KEYFRAME && keyNum >= LOWEST_KEYFRAME ); + VectorCopy( vPos, g_KeyFramePtr[keyNum].vPos ); +} + +void CPositionInterpolator_Linear::InterpolatePosition( float time, Vector &vOut ) +{ + VectorLerp( g_KeyFramePtr[0].vPos, g_KeyFramePtr[1].vPos, time, vOut ); +} + + + + + +// ------------------------------------------------------------------------------------ // +// Catmull-Rom position interpolator. +// ------------------------------------------------------------------------------------ // + +class CPositionInterpolator_CatmullRom : public IPositionInterpolator +{ +public: + virtual void Release(); + virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void SetKeyPosition( int keyNum, Vector const &vPos ); + virtual void InterpolatePosition( float time, Vector &vOut ); + virtual bool ProcessKey( char const *pName, char const *pValue ) { return false; } +}; + +CPositionInterpolator_CatmullRom g_CatmullRomInterpolator; + +IPositionInterpolator* GetCatmullRomInterpolator() +{ + return &g_CatmullRomInterpolator; +} + +void CPositionInterpolator_CatmullRom::Release() +{ +} + +void CPositionInterpolator_CatmullRom::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +{ + *outName = "Catmull-Rom Spline"; + *outMinKeyReq = -1; + *outMaxKeyReq = 2; +} + +void CPositionInterpolator_CatmullRom::SetKeyPosition( int keyNum, Vector const &vPos ) +{ + Assert ( keyNum <= HIGHEST_KEYFRAME && keyNum >= LOWEST_KEYFRAME ); + VectorCopy( vPos, g_KeyFramePtr[keyNum].vPos ); +} + +void CPositionInterpolator_CatmullRom::InterpolatePosition( float time, Vector &vOut ) +{ + Catmull_Rom_Spline( + g_KeyFramePtr[-1].vPos, + g_KeyFramePtr[0].vPos, + g_KeyFramePtr[1].vPos, + g_KeyFramePtr[2].vPos, + time, + vOut ); +} + + + +// ------------------------------------------------------------------------------------ // +// Rope interpolator. +// ------------------------------------------------------------------------------------ // +#include "rope_physics.h" + +class CRopeDelegate : public CSimplePhysics::IHelper +{ +public: + virtual void GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ); + virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ); + + +public: + Vector m_CurEndPoints[2]; +}; + +void CRopeDelegate::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ) +{ + // Gravity. + pAccel->Init( 0, 0, -1500 ); +} + +void CRopeDelegate::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ) +{ + if( nNodes >= 2 ) + { + pNodes[0].m_vPos = m_CurEndPoints[0]; + pNodes[nNodes-1].m_vPos = m_CurEndPoints[1]; + } +} + + +class CPositionInterpolator_Rope : public IPositionInterpolator +{ +public: + CPositionInterpolator_Rope(); + + virtual void Release(); + virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ); + virtual void SetKeyPosition( int keyNum, Vector const &vPos ); + virtual void InterpolatePosition( float time, Vector &vOut ); + virtual bool ProcessKey( char const *pName, char const *pValue ); + + +private: + CRopePhysics<10> m_RopePhysics; + CRopeDelegate m_Delegate; + + float m_flSlack; // Extra length of rope. + + bool m_bChange; + int m_nSegments; +}; + +IPositionInterpolator* GetRopeInterpolator() +{ + return new CPositionInterpolator_Rope; +} + + +CPositionInterpolator_Rope::CPositionInterpolator_Rope() +{ + m_flSlack = 0; + m_bChange = false; + m_nSegments = 5; + + for( int i=0; i < 2; i++ ) + m_Delegate.m_CurEndPoints[i] = Vector( 1e24, 1e24, 1e24 ); +} + +void CPositionInterpolator_Rope::Release() +{ + delete this; +} + +void CPositionInterpolator_Rope::GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +{ + *outName = "Rope"; + *outMinKeyReq = 0; + *outMinKeyReq = 1; +} + +void CPositionInterpolator_Rope::SetKeyPosition( int keyNum, Vector const &vPos ) +{ + if( keyNum == 0 || keyNum == 1 ) + { + if( vPos != m_Delegate.m_CurEndPoints[keyNum] ) + m_bChange = true; + + m_Delegate.m_CurEndPoints[keyNum] = vPos; + } +} + +void CPositionInterpolator_Rope::InterpolatePosition( float time, Vector &vOut ) +{ + // Check if we need to resimulate.. + if( m_bChange ) + { + m_RopePhysics.SetNumNodes( m_nSegments ); + + // Init all the nodes. + for( int i=0; i < m_RopePhysics.NumNodes(); i++ ) + m_RopePhysics.GetNode(i)->m_vPos = m_RopePhysics.GetNode(i)->m_vPrevPos = m_Delegate.m_CurEndPoints[0]; + + float flDist = (m_Delegate.m_CurEndPoints[0] - m_Delegate.m_CurEndPoints[1]).Length(); + flDist += m_flSlack; + + m_RopePhysics.Restart(); + m_RopePhysics.SetupSimulation( flDist / (m_RopePhysics.NumNodes() - 1), &m_Delegate ); + + // Run the simulation for a while to let the rope settle down.. + m_RopePhysics.Simulate( 5 ); + + m_bChange = false; + } + + // Ok, now we have all the nodes setup.. + float flNode = time * (m_RopePhysics.NumNodes()-1); + int iNode = (int)( flNode ); + VectorLerp( + m_RopePhysics.GetNode(iNode)->m_vPredicted, + m_RopePhysics.GetNode(iNode+1)->m_vPredicted, + flNode - iNode, + vOut ); +} + +bool CPositionInterpolator_Rope::ProcessKey( char const *pName, char const *pValue ) +{ + if( stricmp( pName, "Slack" ) == 0 ) + { + m_flSlack = atof( pValue ) + ROPESLACK_FUDGEFACTOR; + m_bChange = true; + return true; + } + else if( stricmp( pName, "Type" ) == 0 ) + { + int iType = atoi( pValue ); + if( iType == 0 ) + m_nSegments = ROPE_MAX_SEGMENTS; + else if( iType == 1 ) + m_nSegments = ROPE_TYPE1_NUMSEGMENTS; + else + m_nSegments = ROPE_TYPE2_NUMSEGMENTS; + + m_bChange = true; + return true; + } + + return false; +} + + + +// ------------------------------------------------------------------------------------ // +// The global table of all the position interpolators. +// ------------------------------------------------------------------------------------ // + +typedef IPositionInterpolator* (*PositionInterpolatorCreateFn)(); +PositionInterpolatorCreateFn g_PositionInterpolatorCreateFns[] = +{ + GetLinearInterpolator, + GetCatmullRomInterpolator, + GetRopeInterpolator +}; + +int Motion_GetNumberOfPositionInterpolators( void ) +{ + return ARRAYSIZE(g_PositionInterpolatorCreateFns); +} + + +IPositionInterpolator* Motion_GetPositionInterpolator( int interpNum ) +{ + Assert( interpNum >= 0 && interpNum < Motion_GetNumberOfPositionInterpolators() ); + return g_PositionInterpolatorCreateFns[clamp( interpNum, 0, Motion_GetNumberOfPositionInterpolators() - 1 )](); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Rotation interpolator function enumeration & implementation +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +typedef void (*RotationInterpolatorFunc_t)(float time, Quaternion &outRot); + +typedef struct +{ + char *szName; + RotationInterpolatorFunc_t pFunc; + + // defines the range of keys this interpolator needs to function + int iMinReqKeyFrame; + int iMaxReqKeyFrame; + +} RotationInterpolator_t; + +void RotationInterpolatorFunc_Linear( float time, Quaternion &outRot ) +{ + // basic 4D spherical linear interpolation + QuaternionSlerp( g_KeyFramePtr[0].qRot, g_KeyFramePtr[1].qRot, time, outRot ); +} + +RotationInterpolator_t g_RotationInterpolators[] = +{ + { "Linear", RotationInterpolatorFunc_Linear, 0, 1 }, +}; + +int Motion_GetNumberOfRotationInterpolators( void ) +{ + return ARRAYSIZE(g_RotationInterpolators); +} + +bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, char **outName, int *outMinKeyReq, int *outMaxKeyReq ) +{ + if ( rotInterpNum < 0 || rotInterpNum >= Motion_GetNumberOfRotationInterpolators() ) + { + return false; + } + + if ( !g_RotationInterpolators[rotInterpNum].szName || !g_RotationInterpolators[rotInterpNum].pFunc ) + { + return false; + } + + if ( outName ) + *outName = g_RotationInterpolators[rotInterpNum].szName; + + if ( outMinKeyReq ) + *outMinKeyReq = g_RotationInterpolators[rotInterpNum].iMinReqKeyFrame; + + if ( outMaxKeyReq ) + *outMaxKeyReq = g_RotationInterpolators[rotInterpNum].iMaxReqKeyFrame; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Interpolates a rotation +// Time is assumed to have already been modified by the TimeModifyFunc (above) +// Requires the keyframes be already set +// Input : time - value from 0..1 +// interpFuncNum - +// *outQuatRotation - result in quaternion form +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Motion_InterpolateRotation( float time, int interpFuncNum, Quaternion &outQuatRotation ) +{ + if ( time < 0.0f || time > 1.0f ) + return false; + + g_RotationInterpolators[interpFuncNum].pFunc( time, outQuatRotation ); + return true; +} diff --git a/public/keyframe/keyframe.h b/public/keyframe/keyframe.h new file mode 100644 index 0000000..0977543 --- /dev/null +++ b/public/keyframe/keyframe.h @@ -0,0 +1,42 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Keyframed animation header +// shared between game and tools +//=============================================================================// + +#ifndef KEYFRAME_H +#define KEYFRAME_H +#pragma once + + +class IPositionInterpolator +{ +public: + virtual void Release() = 0; + + virtual void GetDetails( char **outName, int *outMinKeyReq, int *outMaxKeyReq ) = 0; + virtual void SetKeyPosition( int keyNum, Vector const &vPos ) = 0; + virtual void InterpolatePosition( float time, Vector &vOut ) = 0; + + // Returns true if the key causes a change that changes the interpolated positions. + virtual bool ProcessKey( char const *pName, char const *pValue ) = 0; +}; + + +// Time modifiers. +int Motion_GetNumberOfTimeModifiers( void ); +bool Motion_GetTimeModifierDetails( int timeInterpNum, char **outName ); +bool Motion_CalculateModifiedTime( float time, int timeModifierFuncNum, float *outNewTime ); + +// Position interpolators. +int Motion_GetNumberOfPositionInterpolators( void ); +IPositionInterpolator* Motion_GetPositionInterpolator( int interpNum ); + +// Rotation interpolators. +int Motion_GetNumberOfRotationInterpolators( void ); +bool Motion_GetRotationInterpolatorDetails( int rotInterpNum, char **outName, int *outMinKeyReq, int *outMaxKeyReq ); +bool Motion_InterpolateRotation( float time, int interpFuncNum, Quaternion &outQuatRotation ); +bool Motion_SetKeyAngles( int keyNum, Quaternion &quatAngles ); + + +#endif // KEYFRAME_H diff --git a/public/keyvaluescompiler.h b/public/keyvaluescompiler.h new file mode 100644 index 0000000..eeba2ba --- /dev/null +++ b/public/keyvaluescompiler.h @@ -0,0 +1,188 @@ +//====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef KEYVALUESCOMPILER_H +#define KEYVALUESCOMPILER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlsymbol.h" +#include "tier1/utldict.h" + +class KeyValues; + +#define COMPILED_KEYVALUES_ID MAKEID( 'V', 'K', 'V', 'F' ) + +#define COMPILED_KEYVALUES_VERSION 1 + +struct KVHeader_t +{ + int fileid; + int version; + int numStrings; +}; + +#pragma pack(1) +struct KVFile_t +{ + KVFile_t() : + filename( 0 ), + firstElement( 0 ), + numElements( 0 ) + { + } + short filename; + short firstElement; + short numElements; +}; + +struct KVInfo_t +{ + KVInfo_t() : + key( 0 ), + value( 0 ), + parentIndex( -1 ), + issubtree( false ) + { + } + + inline void SetParent( int index ) + { + Assert( index <= 32768 ); + parentIndex = index; + } + + inline short GetParent() const + { + return parentIndex; + } + + inline void SetSubTree( bool state ) + { + issubtree = state; + } + + inline bool IsSubTree() const + { + return issubtree; + } + + short key; + short value; + +private: + + short parentIndex; + bool issubtree; +}; +#pragma pack() + +//----------------------------------------------------------------------------- +// Purpose: stringtable is a session global string table. +//----------------------------------------------------------------------------- +class CCompiledKeyValuesWriter +{ +public: + + CCompiledKeyValuesWriter() + { + m_StringTable.AddString( "" ); + } + + void AppendKeyValuesFile( char const *filename ); + void WriteFile( char const *outfile ); + +private: + + void Describe( const KVFile_t& file ); + + void BuildKVData_R( KeyValues *kv, int parent ); + + void WriteStringTable( CUtlBuffer& buf ); + void WriteData( CUtlBuffer& buf ); + void WriteFiles( CUtlBuffer &buf ); + + CUtlVector< KVFile_t > m_Files; + CUtlVector< KVInfo_t > m_Data; + + CUtlSymbolTable m_StringTable; +}; + + +class CRunTimeKeyValuesStringTable +{ +public: + + bool ReadStringTable( int numStrings, CUtlBuffer& buf ); + + inline int Count() const + { + return m_Strings.Count(); + } + + inline char const *Lookup( short index ) + { + return m_Strings[ index ]; + } + +private: + CUtlVector< const char * > m_Strings; +}; + +class CCompiledKeyValuesReader +{ +public: + + CCompiledKeyValuesReader(); + + bool LoadFile( char const *filename ); + + KeyValues *Instance( char const *kvfilename ); + bool InstanceInPlace( KeyValues& head, char const *kvfilename ); + bool LookupKeyValuesRootKeyName( char const *kvfilename, char *outbuf, size_t bufsize ); + + int First() const; + int Next( int i ) const; + int InvalidIndex() const; + + void GetFileName( int index, char *buf, size_t bufsize ); + +private: + + struct FileInfo_t + { + FileInfo_t() : + hFile( 0 ), + nFirstIndex( 0 ), + nCount( 0 ) + { + } + FileNameHandle_t hFile; + short nFirstIndex; + short nCount; + + static bool Less( const FileInfo_t& lhs, const FileInfo_t& rhs ) + { + return lhs.hFile < rhs.hFile; + } + }; + + KeyValues *CreateFromData( const FileInfo_t& info ); + bool CreateInPlaceFromData( KeyValues& head, const FileInfo_t& info ); + + // Now get the actual files + CUtlRBTree< FileInfo_t, unsigned short > m_Dict; + CUtlVector< KVInfo_t > m_Data; + + CRunTimeKeyValuesStringTable m_StringTable; + + CUtlBuffer m_LoadBuffer; +}; + +#endif // KEYVALUESCOMPILER_H diff --git a/public/list.h b/public/list.h new file mode 100644 index 0000000..d76d36d --- /dev/null +++ b/public/list.h @@ -0,0 +1,352 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef __LIST_H__ +#define __LIST_H__ + +// TODO: +// GetPositionAtIndex needs to keep a cache of the previous call so +// that it doesn't do a linear search every time. + +#include + +// FIXME: change the name of this to something more specific. +typedef struct +{ +} _Position; +typedef _Position *Position; + +template class GList; +template class GListIterator; + +// GListNode: Class decleration and definition +template class GListNode +{ +private: + T data; + GListNode *next; + GListNode *prev; + GListNode(); + GListNode( T item ); + friend class GList; + friend class GListIterator; +}; + +template +GListNode::GListNode() +{ + next = NULL; + prev = NULL; +} + +template +GListNode::GListNode( T item ) +{ + data = item; + next = NULL; + prev = NULL; +} + +// GList: Class decleration and definition +template class GList +{ +public: + // Contructors/destructors + GList(); + + // + Position InsertAtHead( T ); + Position InsertAtTail( T ); + T Remove( Position position ); + void RemoveAll( void (*DeleteItem)( T ) ); + void RemoveSelectedItems( bool (*NeedsRemovalFunc)( T ), void (*DeleteItemFunc)( T ) ); + Position InsertAfter( T item, Position position ); + Position InsertBefore( T item, Position position ); + bool IsEmpty(); + int GetNumItems(); + T GetItemAtPosition( Position position ); + Position GetPositionAtIndex( int index ); + T GetItemAtIndex( int index ); + +protected: + GListNode *head; + GListNode *tail; + int numItems; + friend class GListIterator; +}; + +template +GList::GList() +{ + // Set up a dummy head node and a dummy tail node. + head = new GListNode; + tail = new GListNode; + head->next = tail; + head->prev = head; + tail->next = tail; + tail->prev = head; + numItems = 0; +} + +template +Position GList::InsertAtHead( T item ) +{ + GListNode *newNode = new GListNode( item ); + head->next->prev = newNode; + newNode->next = head->next; + newNode->prev = head; + head->next = newNode; + numItems++; + return ( Position )( void * )newNode; +} + +template +Position GList::InsertAtTail( T item ) +{ + GListNode *newNode = new GListNode( item ); + tail->prev->next = newNode; + newNode->prev = tail->prev; + tail->prev = newNode; + newNode->next = tail; + numItems++; + return ( Position )( void * )newNode; +} + +template +T GList::Remove( Position position ) +{ + GListNode *node = ( GListNode * )( void * )position; + node->prev->next = node->next; + node->next->prev = node->prev; + T data = node->data; + numItems--; + delete node; + return data; +} + +template +void GList::RemoveAll( void (*DeleteItemFunc)( T ) ) +{ + GListNode *tmpNode; + GListNode *node = head->next; + while( node != tail ) + { + if( DeleteItemFunc ) + { + DeleteItemFunc( node->data ); + } + tmpNode = node->next; + delete node; + node = tmpNode; + } + head->next = tail; + head->prev = head; + tail->next = tail; + tail->prev = head; + numItems = 0; +} + +template +void GList::RemoveSelectedItems( bool (*NeedsRemovalFunc)( T ), void (*DeleteItemFunc)( T ) ) +{ + GListNode *tmpNode; + GListNode *node = head->next; + while( node != tail ) + { + if( NeedsRemovalFunc( node->data ) ) + { + DeleteItemFunc( node->data ); + node->prev->next = node->next; + node->next->prev = node->prev; + tmpNode = node; + node = node->next; + delete tmpNode; + numItems--; + } + else + { + node = node->next; + } + } +} + +template +Position GList::InsertAfter( T item, Position position ) +{ + GListNode *node = ( GListNode * )( void * )position; + GListNode *newNode = new GListNode( item ); + newNode->prev = node; + newNode->next = node->next; + node->next->prev = newNode; + node->next = newNode; + numItems++; + return ( Position )( void * )newNode; +} + +template +Position GList::InsertBefore( T item, Position position ) +{ + GListNode *node = ( GListNode * )( void * )position; + GListNode *newNode = new GListNode( item ); + newNode->prev = node->prev; + newNode->next = node; + node->prev->next = newNode; + node->prev = newNode; + numItems++; + return ( Position )( void * )newNode; +} + +template +bool GList::IsEmpty() +{ + return ( numItems == 0 ); +} + +template +int GList::GetNumItems() +{ + return numItems; +} + +template +T GList::GetItemAtPosition( Position position ) +{ + return ( ( GListNode * )( void * )position )->data; +} + +template +Position GList::GetPositionAtIndex( int index ) +{ + int i; + GListNode *node = head->next; + for( i = 0; i < index; i++ ) + { + node = node->next; + } + return ( Position )( void * )node; +} + +template +T GList::GetItemAtIndex( int index ) +{ + return GetItemAtPosition( GetPositionAtIndex( index ) ); +} + +// GListIterator: Class decleration and definition +template class GListIterator +{ +public: + GListIterator( GList *GList ); + void GotoHead( void ); + void GotoTail( void ); + void Goto( int index ); + void Increment(); + void Decrement(); + T GetCurrentAndIncrement(); + T GetCurrentAndDecrement(); + T IncrementAndGetCurrent(); + T DecrementAndGetCurrent(); + // postfix + T operator++( int ) { return GetCurrentAndIncrement(); } + T operator--( int ) { return GetCurrentAndDecrement(); } + // prefix + T operator++() { return IncrementAndGetCurrent(); } + T operator--() { return DecrementAndGetCurrent(); } + T GetCurrent(); + bool AtEnd( void ); + bool AtBeginning( void ); +protected: + GList *list; + GListNode *currentNode; +}; + +template +GListIterator::GListIterator( GList *list ) +{ + this->list = list; + GotoHead(); +} + +template +void GListIterator::GotoHead() +{ + this->currentNode = list->head->next; +} + +template +void GListIterator::GotoTail() +{ + this->currentNode = list->tail->prev; +} + +template +void GListIterator::Goto( int index ) +{ + currentNode = ( GListNode * )( void * )list->GetPositionAtIndex( index ); +} + +template +void GListIterator::Increment() +{ + currentNode = currentNode->next; +} + +template +void GListIterator::Decrement() +{ + currentNode = currentNode->prev; +} + +template +T GListIterator::GetCurrentAndIncrement() +{ + T retval = currentNode->data; + Increment(); + return retval; +} + +template +T GListIterator::GetCurrentAndDecrement() +{ + T retval = currentNode->data; + Decrement(); + return retval; +} + +template +T GListIterator::IncrementAndGetCurrent() +{ + Increment(); + return GetCurrent(); +} + +template +T GListIterator::DecrementAndGetCurrent() +{ + Decrement(); + return GetCurrent(); +} + +template +T GListIterator::GetCurrent() +{ + return currentNode->data; +} + +template +bool GListIterator::AtEnd( void ) +{ + return currentNode->next == currentNode; +} + +template +bool GListIterator::AtBeginning( void ) +{ + return currentNode->prev == currentNode; +} + +#endif /* __LIST_H__ */ diff --git a/public/loadcmdline.cpp b/public/loadcmdline.cpp new file mode 100644 index 0000000..b933a82 --- /dev/null +++ b/public/loadcmdline.cpp @@ -0,0 +1,123 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: loads additional command line options from a config file +// +// $NoKeywords: $ +//=============================================================================// + +#include "KeyValues.h" +#include "tier1/strtools.h" +#include "FileSystem_Tools.h" +#include "tier1/utlstring.h" + +// So we know whether or not we own argv's memory +static bool sFoundConfigArgs = false; + +//----------------------------------------------------------------------------- +// Purpose: Parses arguments and adds them to argv and argc +//----------------------------------------------------------------------------- +static void AddArguments( int &argc, char **&argv, const char *str ) +{ + char **args = 0; + char *argList = 0; + int argCt = argc; + + argList = new char[ Q_strlen( str ) + 1 ]; + Q_strcpy( argList, str ); + + // Parse the arguments out of the string + char *token = strtok( argList, " " ); + while( token ) + { + ++argCt; + token = strtok( NULL, " " ); + } + + // Make sure someting was actually found in the file + if( argCt > argc ) + { + sFoundConfigArgs = true; + + // Allocate a new array for argv + args = new char*[ argCt ]; + + // Copy original arguments, up to the last one + int i; + for( i = 0; i < argc - 1; ++i ) + { + args[ i ] = new char[ Q_strlen( argv[ i ] ) + 1 ]; + Q_strcpy( args[ i ], argv[ i ] ); + } + + // copy new arguments + Q_strcpy( argList, str ); + token = strtok( argList, " " ); + for( ; i < argCt - 1; ++i ) + { + args[ i ] = new char[ Q_strlen( token ) + 1 ]; + Q_strcpy( args[ i ], token ); + token = strtok( NULL, " " ); + } + + // Copy the last original argument + args[ i ] = new char[ Q_strlen( argv[ argc - 1 ] ) + 1 ]; + Q_strcpy( args[ i ], argv[ argc - 1 ] ); + + argc = argCt; + argv = args; + } + + delete [] argList; +} + +//----------------------------------------------------------------------------- +// Purpose: Loads additional commandline arguments from a config file for an app. +// Filesystem must be initialized before calling this function. +// keyname: Name of the block containing the key/args pairs (ie map or model name) +// appname: Keyname for the commandline arguments to be loaded - typically the exe name. +//----------------------------------------------------------------------------- +void LoadCmdLineFromFile( int &argc, char **&argv, const char *keyname, const char *appname ) +{ + sFoundConfigArgs = false; + + Assert( g_pFileSystem ); + if( !g_pFileSystem ) + return; + + // Load the cfg file, and find the keyname + KeyValues *kv = new KeyValues( "CommandLine" ); + + char filename[512]; + Q_snprintf( filename, sizeof( filename ), "%s/cfg/commandline.cfg", gamedir ); + + if ( kv->LoadFromFile( g_pFileSystem, filename ) ) + { + // Load the commandline arguments for this app + KeyValues *appKey = kv->FindKey( keyname ); + if( appKey ) + { + const char *str = appKey->GetString( appname ); + Msg( "Command Line found: %s\n", str ); + + AddArguments( argc, argv, str ); + } + } + + kv->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: Cleans up any memory allocated for the new argv. Pass in the app's +// argc and argv - this is safe even if no extra arguments were loaded. +//----------------------------------------------------------------------------- +void DeleteCmdLine( int argc, char **argv ) +{ + if( !sFoundConfigArgs ) + return; + + for( int i = 0; i < argc; ++i ) + { + delete [] argv[i]; + } + delete [] argv; +} \ No newline at end of file diff --git a/public/loadcmdline.h b/public/loadcmdline.h new file mode 100644 index 0000000..c18746e --- /dev/null +++ b/public/loadcmdline.h @@ -0,0 +1,27 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: loads additional command line options from a config file +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LOADCMDLINE_H +#define LOADCMDLINE_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Loads additional commandline arguments from a config file for an app. +// keyname: Name of the block containing the key/args pairs (ie map or model name) +// appname: Keyname for the commandline arguments to be loaded - typically the exe name. +//----------------------------------------------------------------------------- +void LoadCmdLineFromFile( int &argc, char **&argv, const char *keyname, const char *appname ); + +//----------------------------------------------------------------------------- +// Purpose: Cleans up any memory allocated for the new argv. Pass in the app's +// argc and argv - this is safe even if no extra arguments were loaded. +//----------------------------------------------------------------------------- +void DeleteCmdLine( int argc, char **argv ); + +#endif // LOADCMDLINE_H \ No newline at end of file diff --git a/public/localflexcontroller.h b/public/localflexcontroller.h new file mode 100644 index 0000000..d9f17e2 --- /dev/null +++ b/public/localflexcontroller.h @@ -0,0 +1,30 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef LOCALFLEXCONTROLLER_H +#define LOCALFLEXCONTROLLER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +enum LocalFlexController_t +{ + // this isn't really an enum - its just a typed int. gcc will not accept it as a fwd decl, so we'll define one value + DUMMY_FLEX_CONTROLLER=0x7fffffff // make take 32 bits +}; + +inline LocalFlexController_t &operator++( LocalFlexController_t &a ) { return a = LocalFlexController_t( int( a ) + 1 ); } +inline LocalFlexController_t &operator--( LocalFlexController_t &a ) { return a = LocalFlexController_t( int( a ) - 1 ); } +inline LocalFlexController_t operator++( LocalFlexController_t &a, int ) { LocalFlexController_t t = a; a = LocalFlexController_t( int( a ) + 1 ); return t; } +inline LocalFlexController_t operator--( LocalFlexController_t &a, int ) { LocalFlexController_t t = a; a = LocalFlexController_t( int( a ) - 1 ); return t; } + + +#endif // LOCALFLEXCONTROLLER_H diff --git a/public/localize/ilocalize.h b/public/localize/ilocalize.h new file mode 100644 index 0000000..3c35600 --- /dev/null +++ b/public/localize/ilocalize.h @@ -0,0 +1,130 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ILOCALIZE_H +#define ILOCALIZE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" +#include + +// unicode character type +// for more unicode manipulation functions #include +#ifndef _WCHAR_T_DEFINED +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + + +//----------------------------------------------------------------------------- +// Interface used to query text size so we can choose the longest one +//----------------------------------------------------------------------------- +abstract_class ILocalizeTextQuery +{ +public: + virtual int ComputeTextWidth( const wchar_t *pString ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Callback which is triggered when any localization string changes +// Is not called when a localization string is added +//----------------------------------------------------------------------------- +abstract_class ILocalizationChangeCallback +{ +public: + virtual void OnLocalizationChanged() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Handles localization of text +// looks up string names and returns the localized unicode text +//----------------------------------------------------------------------------- +// direct references to localized strings +typedef uint32 LocalizeStringIndex_t; +const uint32 LOCALIZE_INVALID_STRING_INDEX = (LocalizeStringIndex_t)-1; + +abstract_class ILocalize : public IAppSystem +{ +public: + // adds the contents of a file to the localization table + virtual bool AddFile( const char *fileName, const char *pPathID = NULL, bool bIncludeFallbackSearchPaths = false ) = 0; + + // Remove all strings from the table + virtual void RemoveAll() = 0; + + // Finds the localized text for tokenName. Returns NULL if none is found. + virtual wchar_t *Find(const char *tokenName) = 0; + + // Like Find(), but as a failsafe, returns an error message instead of NULL if the string isn't found. + virtual const wchar_t *FindSafe(const char *tokenName) = 0; + + // converts an english string to unicode + // returns the number of wchar_t in resulting string, including null terminator + virtual int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSizeInBytes) = 0; + + // converts an unicode string to an english string + // unrepresentable characters are converted to system default + // returns the number of characters in resulting string, including null terminator + virtual int ConvertUnicodeToANSI(const wchar_t *unicode, char *ansi, int ansiBufferSize) = 0; + + // finds the index of a token by token name, INVALID_STRING_INDEX if not found + virtual LocalizeStringIndex_t FindIndex(const char *tokenName) = 0; + + // builds a localized formatted string + // uses the format strings first: %s1, %s2, ... unicode strings (wchar_t *) + virtual void ConstructString(wchar_t *unicodeOuput, int unicodeBufferSizeInBytes, const wchar_t *formatString, int numFormatParameters, ...) = 0; + + // gets the values by the string index + virtual const char *GetNameByIndex(LocalizeStringIndex_t index) = 0; + virtual wchar_t *GetValueByIndex(LocalizeStringIndex_t index) = 0; + + /////////////////////////////////////////////////////////////////// + // the following functions should only be used by localization editors + + // iteration functions + virtual LocalizeStringIndex_t GetFirstStringIndex() = 0; + // returns the next index, or INVALID_STRING_INDEX if no more strings available + virtual LocalizeStringIndex_t GetNextStringIndex(LocalizeStringIndex_t index) = 0; + + // adds a single name/unicode string pair to the table + virtual void AddString( const char *tokenName, wchar_t *unicodeString, const char *fileName ) = 0; + + // changes the value of a string + virtual void SetValueByIndex(LocalizeStringIndex_t index, wchar_t *newValue) = 0; + + // saves the entire contents of the token tree to the file + virtual bool SaveToFile( const char *fileName ) = 0; + + // iterates the filenames + virtual int GetLocalizationFileCount() = 0; + virtual const char *GetLocalizationFileName(int index) = 0; + + // returns the name of the file the specified localized string is stored in + virtual const char *GetFileNameByIndex(LocalizeStringIndex_t index) = 0; + + // for development only, reloads localization files + virtual void ReloadLocalizationFiles( ) = 0; + + // need to replace the existing ConstructString with this + virtual void ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, const char *tokenName, KeyValues *localizationVariables) = 0; + virtual void ConstructString(wchar_t *unicodeOutput, int unicodeBufferSizeInBytes, LocalizeStringIndex_t unlocalizedTextSymbol, KeyValues *localizationVariables) = 0; + + // Used to install a callback to query which localized strings are the longest + virtual void SetTextQuery( ILocalizeTextQuery *pQuery ) = 0; + + // Is called when any localization strings change + virtual void InstallChangeCallback( ILocalizationChangeCallback *pCallback ) = 0; + virtual void RemoveChangeCallback( ILocalizationChangeCallback *pCallback ) = 0; +}; + + +#endif // ILOCALIZE_H diff --git a/public/lumpfiles.cpp b/public/lumpfiles.cpp new file mode 100644 index 0000000..06227cd --- /dev/null +++ b/public/lumpfiles.cpp @@ -0,0 +1,27 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "bspfile.h" +#include "strtools.h" +#include "filesystem.h" +#include "lumpfiles.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Generate a lump file name for a given bsp & index +// Input : *bspfilename - +// *lumpfilename - +// iIndex - +//----------------------------------------------------------------------------- +void GenerateLumpFileName( const char *bspfilename, char *lumpfilename, int iBufferSize, int iIndex ) +{ + char lumppre[MAX_PATH]; + V_StripExtension( bspfilename, lumppre, MAX_PATH ); + Q_snprintf( lumpfilename, iBufferSize, "%s_l_%d.lmp", lumppre, iIndex ); +} + diff --git a/public/lumpfiles.h b/public/lumpfiles.h new file mode 100644 index 0000000..770df78 --- /dev/null +++ b/public/lumpfiles.h @@ -0,0 +1,20 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef LUMPFILES_H +#define LUMPFILES_H +#ifdef _WIN32 +#pragma once +#endif + +#define MAX_LUMPFILES 128 + +//----------------------------------------------------------------------------- +// Lump files +//----------------------------------------------------------------------------- +void GenerateLumpFileName( const char *bspfilename, char *lumpfilename, int iBufferSize, int iIndex ); + +#endif // LUMPFILES_H diff --git a/public/map_utils.cpp b/public/map_utils.cpp new file mode 100644 index 0000000..01336dd --- /dev/null +++ b/public/map_utils.cpp @@ -0,0 +1,48 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "mathlib/vector.h" +#include "bspfile.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void SetupLightNormalFromProps( const QAngle &angles, float angle, float pitch, Vector &output ) +{ + if (angle == ANGLE_UP) + { + output[0] = output[1] = 0; + output[2] = 1; + } + else if (angle == ANGLE_DOWN) + { + output[0] = output[1] = 0; + output[2] = -1; + } + else + { + // if we don't have a specific "angle" use the "angles" YAW + if ( !angle ) + { + angle = angles[YAW]; + } + + output[2] = 0; + output[0] = (float)cos (angle/180*M_PI); + output[1] = (float)sin (angle/180*M_PI); + } + + if ( !pitch ) + { + // if we don't have a specific "pitch" use the "angles" PITCH + pitch = angles[PITCH]; + } + + output[2] = (float)sin(pitch/180*M_PI); + output[0] *= (float)cos(pitch/180*M_PI); + output[1] *= (float)cos(pitch/180*M_PI); +} + diff --git a/public/map_utils.h b/public/map_utils.h new file mode 100644 index 0000000..75976e4 --- /dev/null +++ b/public/map_utils.h @@ -0,0 +1,25 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MAP_UTILS_H +#define MAP_UTILS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/vector.h" + + +// angles comes from the "angles" property +// +// yaw and pitch will override the values in angles if they are nonzero +// yaw comes from the (obsolete) "angle" property +// pitch comes from the "pitch" property +void SetupLightNormalFromProps( const QAngle &angles, float yaw, float pitch, Vector &output ); + + +#endif // MAP_UTILS_H diff --git a/public/matchmaking/idatacenter.h b/public/matchmaking/idatacenter.h new file mode 100644 index 0000000..3b4f0bc --- /dev/null +++ b/public/matchmaking/idatacenter.h @@ -0,0 +1,19 @@ +#ifndef _IDATACENTER_H_ +#define _IDATACENTER_H_ + +class IDatacenter; + +#include "imatchsystem.h" + +abstract_class IDatacenter +{ +public: + // + // GetStats + // retrieves the last received datacenter stats + // + virtual KeyValues * GetStats() = 0; +}; + + +#endif // _IDATACENTER_H_ diff --git a/public/matchmaking/imatchasync.h b/public/matchmaking/imatchasync.h new file mode 100644 index 0000000..18cc01b --- /dev/null +++ b/public/matchmaking/imatchasync.h @@ -0,0 +1,62 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHASYNC_H +#define IMATCHASYNC_H + +#ifdef _WIN32 +#pragma once +#endif + +// +// Describes possible states of an async operation +// +enum AsyncOperationState_t +{ + AOS_RUNNING, + AOS_ABORTING, + AOS_ABORTED, + AOS_FAILED, + AOS_SUCCEEDED, +}; + +// +// Interface of an async operation +// +abstract_class IMatchAsyncOperation +{ +public: + // Poll if operation has completed + virtual bool IsFinished() = 0; + + // Operation state + virtual AsyncOperationState_t GetState() = 0; + + // Retrieve a generic completion result for simple operations + // that return simple results upon success, + // results are operation-specific, may result in undefined behavior + // if operation is still in progress. + virtual uint64 GetResult() = 0; + + // Request operation to be aborted + virtual void Abort() = 0; + + // Release the operation interface and all resources + // associated with the operation. Operation callbacks + // will not be called after Release. Operation object + // cannot be accessed after Release. + virtual void Release() = 0; +}; + +abstract_class IMatchAsyncOperationCallback +{ +public: + // Signals when operation has finished + virtual void OnOperationFinished( IMatchAsyncOperation *pOperation ) = 0; +}; + +#endif // IMATCHASYNC_H diff --git a/public/matchmaking/imatchevents.h b/public/matchmaking/imatchevents.h new file mode 100644 index 0000000..e92dee0 --- /dev/null +++ b/public/matchmaking/imatchevents.h @@ -0,0 +1,259 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IMATCHEVENTS_H +#define IMATCHEVENTS_H +#ifdef _WIN32 +#pragma once +#endif + + +abstract_class IMatchEventsSink +{ +public: + virtual void OnEvent( KeyValues *pEvent ) {} + + // + // List of events + // + +/* + "OnSysStorageDevicesChanged" + Signalled when system storage device change is detected. + params: + void + + "OnSysSigninChange" + Signalled when one or more users sign out. + params: + string "action" - signin change event: "signin", "signout" + int "numUsers" - how many users signed in/out (defines valid user0 - userN-1 fields) + int "mask" - bitmask of controllers affected + int "user%d" - controller index affected + + "OnSysXUIEvent" + Signalled when an XUI event occurs. + params: + string "action" - XUI action type: "opening", "closed" + + "OnSysMuteListChanged" + Signalled when system mute list change occurs. + params: + void + + "OnSysInputDevicesChanged" + Signalled when input device disconnection is detected. + params: + int "mask" - bitmask of which slot's controller was disconnected [0-1] + + + + "OnEngineLevelLoadingStarted" + Signalled when a level starts loading. + params: + string "name" - level name + + "OnEngineListenServerStarted" + Signalled when a listen server level loads enough to accept client connections. + params: + void + + "OnEngineLevelLoadingTick" + Signalled periodically while a level is loading, + after loading started and before loading finished. + params: + void + + "OnEngineLevelLoadingFinished" + Signalled when a level is finished loading. + params: + int "error" - whether an extended error occurred + string "reason" - reason description + + "OnEngineClientSignonStateChange" + Signalled when client's signon state is changing. + params: + int "slot" - client ss slot + int "old" - old state + int "new" - new state + int "count" - count + + "OnEngineDisconnectReason" + Signalled before a disconnect is going to occur and a reason + for disconnect is available. + params: + string "reason" - reason description + + "OnEngineEndGame" + Signalled before a disconnect is going to occur and notifies the members + of the game that the game has reached a conclusion or a vote to end the + game has passed and the game should terminate and return to lobby if possible. + params: + string "reason" - reason description + + + + "OnMatchPlayerMgrUpdate" + Signalled when a player manager update occurs. + params: + string "update" = - update type + "searchstarted" - search started + "searchfinished" - search finished + "friend" - friend details updated + uint64 "xuid" - xuid of a player if applicable + + "OnMatchPlayerMgrReset" + Signalled when the game needs to go into attract mode. + params: + string "reason" - one of the following reasons: + "GuestSignedIn" - guest user signed in + "GameUserSignedOut" - user involved in game has signed out + + "OnMatchServerMgrUpdate" + Signalled when a server manager update occurs. + params: + string "update" = - update type + "searchstarted" - search started + "searchfinished" - search finished + "server" - server details updated + uint64 "xuid" - xuid of a server if applicable + + "OnMatchSessionUpdate" + Signalled when a session changes. + params: + strings "state" = - new state of the session + "ready" - session is completely initialized and ready + "updated" - session settings have been updated + + + + "OnNetLanConnectionlessPacket" + Signalled when a lan network packet is received. + params: + string "from" - netadr of sender as recorded by network layer + subkey - packet message + + + + "OnProfilesChanged" + Signalled when new number of game users are set for the game. + params: + int "numProfiles" - number of game users set for the game + + "OnProfileDataLoaded" + Signalled when a user profile title data is loaded. + params: + int "iController" - index of controller whose title data is now loaded + + "OnProfileStorageAvailable" + Signalled when a user profile storage device is selected. + params: + int "iController" - index of controller whose storage device is now selected + + "OnProfileUnavailable" + Signalled when a user profile is detected as unavailable. + params: + int "iController" - index of controller whose profile was detected as unavailable + + + + "OnPlayerUpdated" + Signalled when information about a player changes. + params: + uint64 "xuid" - XUID of the player updated + + "OnPlayerRemoved" + Signalled when a player is removed from the game. + params: + uint64 "xuid" - XUID of the player removed + + "OnPlayerMachinesConnected" + Signalled when new machines become part of the session, they will be last + in the list of connected machines. + params: + int "numMachines" - number of new machines connected + + "OnPlayerActivity" + Signalled when a player activity is detected. + params: + uint64 "xuid" - XUID of the player + string "act" - type of activity: + "voice" - player is voice chatting + + + + "OnMuteChanged" + Signalled when a mute list is updated. + params: + void + + "OnInvite" + Signalled when game invite event occurs. + params: + int "user" - controller index accepting the invite or causing invite error + string "sessioninfo" - session info of the invite host + string "action" = - invite action + "accepted" - when an invite is accepted by user + "storage" - when a storage device needs to be validated + "error" - when an error occurs that prevents invite from being accepted + "join" - when destructive actions or storage devices are confirmed by user + "deny" - when invite is rejected by user + string "error" - error description: "NotOnline", "NoMultiplayer", etc. + ptr int "confirmed" - handler should set pointed int to 0 if confirmation is pending + and send a "join" action OnInvite event after destructive + actions are confirmed by user, storage devices are mounted, etc. +*/ + +}; + +abstract_class IMatchEventsSubscription +{ +public: + virtual void Subscribe( IMatchEventsSink *pSink ) = 0; + virtual void Unsubscribe( IMatchEventsSink *pSink ) = 0; + + virtual void BroadcastEvent( KeyValues *pEvent ) = 0; + + virtual void RegisterEventData( KeyValues *pEventData ) = 0; + virtual KeyValues * GetEventData( char const *szEventDataKey ) = 0; +}; + + +// +// Renamer for the match events event-handler function +// Usage: +// class MyClass : public CMatchEventsSinkFn< MyClass > +// { +// public: +// MyClass() : MatchEventsSinkFnClass( &MyClass::HandleMatchSinkEvent ) {} +// void HandleMatchSinkEvent( KeyValues *pEvent ); +// }; +// + +template < typename TDerived > +class CMatchEventsSinkFn : public IMatchEventsSink +{ +protected: + typedef TDerived DerivedClass; + typedef void ( TDerived::*PFnDerivedHandler_t )( KeyValues *pEvent ); + typedef CMatchEventsSinkFn< TDerived > MatchEventsSinkFnClass; + +protected: + explicit CMatchEventsSinkFn( PFnDerivedHandler_t pfn ) : m_pfnDerived( pfn ) {} + +public: + virtual void OnEvent( KeyValues *pEvent ) + { + ( static_cast< TDerived * >( this )->*m_pfnDerived )( pEvent ); + } + +private: + PFnDerivedHandler_t m_pfnDerived; +}; + + +#endif // IMATCHEVENTS_H + diff --git a/public/matchmaking/imatchextensions.h b/public/matchmaking/imatchextensions.h new file mode 100644 index 0000000..2f37764 --- /dev/null +++ b/public/matchmaking/imatchextensions.h @@ -0,0 +1,28 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHEXTENSIONS_H +#define IMATCHEXTENSIONS_H + +#ifdef _WIN32 +#pragma once +#endif + +abstract_class IMatchExtensions +{ +public: + // Registers an extension interface + virtual void RegisterExtensionInterface( char const *szInterfaceString, void *pvInterface ) = 0; + + // Unregisters an extension interface + virtual void UnregisterExtensionInterface( char const *szInterfaceString, void *pvInterface ) = 0; + + // Gets a pointer to a registered extension interface + virtual void * GetRegisteredExtensionInterface( char const *szInterfaceString ) = 0; +}; + +#endif // IMATCHEXTENSIONS_H diff --git a/public/matchmaking/imatchframework.h b/public/matchmaking/imatchframework.h new file mode 100644 index 0000000..bd77364 --- /dev/null +++ b/public/matchmaking/imatchframework.h @@ -0,0 +1,103 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHFRAMEWORK_H +#define IMATCHFRAMEWORK_H + +#ifdef _WIN32 +#pragma once +#endif + +class IMatchFramework; +class IMatchSession; + +#include "appframework/iAppSystem.h" + +#include "tier1/interface.h" +#include "keyvalues.h" + +#if !defined( _X360 ) +#include "xbox/xboxstubs.h" +#endif + +#include "inetchannel.h" + +#include "imatchasync.h" +#include "imatchtitle.h" +#include "imatchnetworkmsg.h" +#include "imatchextensions.h" +#include "imatchevents.h" +#include "imatchsystem.h" +#include "iplayermanager.h" +#include "iplayer.h" +#include "iservermanager.h" +#include "imatchvoice.h" +#include "isearchmanager.h" +#include "idatacenter.h" + +abstract_class IMatchFramework : public IAppSystem +{ +public: + // Run frame of the matchmaking framework + virtual void RunFrame() = 0; + + + // Get matchmaking extensions + virtual IMatchExtensions * GetMatchExtensions() = 0; + + // Get events container + virtual IMatchEventsSubscription * GetEventsSubscription() = 0; + + // Get the matchmaking title interface + virtual IMatchTitle * GetMatchTitle() = 0; + + // Get the match session interface of the current match framework type + virtual IMatchSession * GetMatchSession() = 0; + + // Get the network msg encode/decode factory + virtual IMatchNetworkMsgController * GetMatchNetworkMsgController() = 0; + + // Get the match system + virtual IMatchSystem * GetMatchSystem() = 0; + + + // Entry point to create session + virtual void CreateSession( KeyValues *pSettings ) = 0; + + // Entry point to match into a session + virtual void MatchSession( KeyValues *pSettings ) = 0; + + // Accept invite + virtual void AcceptInvite( int iController ) = 0; + + // Close the session + virtual void CloseSession() = 0; +}; + +#define IMATCHFRAMEWORK_VERSION_STRING "MATCHFRAMEWORK_001" + + + +abstract_class IMatchSession +{ +public: + // Get an internal pointer to session system-specific data + virtual KeyValues * GetSessionSystemData() = 0; + + // Get an internal pointer to session settings + virtual KeyValues * GetSessionSettings() = 0; + + // Update session settings, only changing keys and values need + // to be passed and they will be updated + virtual void UpdateSessionSettings( KeyValues *pSettings ) = 0; + + // Issue a session command + virtual void Command( KeyValues *pCommand ) = 0; +}; + + +#endif // IMATCHFRAMEWORK_H diff --git a/public/matchmaking/imatchnetworkmsg.h b/public/matchmaking/imatchnetworkmsg.h new file mode 100644 index 0000000..e629d33 --- /dev/null +++ b/public/matchmaking/imatchnetworkmsg.h @@ -0,0 +1,49 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHNETWORKMSG_H +#define IMATCHNETWORKMSG_H + +#ifdef _WIN32 +#pragma once +#endif + + +struct MM_QOS_t +{ + int nPingMsMin; // Minimum round-trip time in ms + int nPingMsMed; // Median round-trip time in ms + float flBwUpKbs; // Bandwidth upstream in kilobytes/s + float flBwDnKbs; // Bandwidth downstream in kilobytes/s + float flLoss; // Average packet loss in percents +}; + +struct MM_GameDetails_QOS_t +{ + void *m_pvData; // Encoded game details + int m_numDataBytes; // Length of game details + + int m_nPing; // Average ping in ms +}; + +abstract_class IMatchNetworkMsgController +{ +public: + // To determine host Quality-of-Service + virtual MM_QOS_t GetQOS() = 0; + + virtual KeyValues * GetActiveServerGameDetails( KeyValues *pRequest ) = 0; + + virtual KeyValues * UnpackGameDetailsFromQOS( MM_GameDetails_QOS_t const *pvQosReply ) = 0; + virtual KeyValues * UnpackGameDetailsFromSteamLobby( uint64 uiLobbyID ) = 0; + + virtual void PackageGameDetailsForQOS( KeyValues *pSettings, CUtlBuffer &buf ) = 0; + + virtual KeyValues * PackageGameDetailsForReservation( KeyValues *pSettings ) = 0; +}; + +#endif // IMATCHNETWORKMSG_H \ No newline at end of file diff --git a/public/matchmaking/imatchsystem.h b/public/matchmaking/imatchsystem.h new file mode 100644 index 0000000..2e9d687 --- /dev/null +++ b/public/matchmaking/imatchsystem.h @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// + +#ifndef _IMATCHSYSTEM_H_ +#define _IMATCHSYSTEM_H_ + +class IPlayerManager; +class IGameManager; +class IServerManager; +class ISearchManager; +class IMatchVoice; +class IDatacenter; + +class IMatchSystem +{ +public: + virtual IPlayerManager * GetPlayerManager() = 0; + + virtual IMatchVoice * GetMatchVoice() = 0; + + virtual IServerManager * GetUserGroupsServerManager() = 0; + + virtual ISearchManager * CreateGameSearchManager( KeyValues *pParams ) = 0; + + virtual IDatacenter * GetDatacenter() = 0; +}; + +#endif + diff --git a/public/matchmaking/imatchtitle.h b/public/matchmaking/imatchtitle.h new file mode 100644 index 0000000..74b404a --- /dev/null +++ b/public/matchmaking/imatchtitle.h @@ -0,0 +1,162 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHTITLE_H +#define IMATCHTITLE_H + +#ifdef _WIN32 +#pragma once +#endif + +struct TitleDataFieldsDescription_t +{ + enum DataType_t + { + DT_U8 = 8, + DT_U16 = 16, + DT_U32 = 32, + DT_FLOAT = 33, + DT_U64 = 64 + }; + + enum DataBlock_t + { + DB_TD1 = 0, + DB_TD2 = 1, + DB_TD3 = 2 + }; + + char const *m_szFieldName; + DataBlock_t m_iTitleDataBlock; + DataType_t m_eDataType; + int m_numBytesOffset; +}; + +struct TitleAchievementsDescription_t +{ + char const *m_szAchievementName; + int m_idAchievement; +}; + +struct TitleAvatarAwardsDescription_t +{ + char const *m_szAvatarAwardName; + int m_idAvatarAward; +}; + +abstract_class IMatchTitle +{ +public: + // Title ID + virtual uint64 GetTitleID() = 0; + + // Service ID for XLSP + virtual uint64 GetTitleServiceID() = 0; + + // Whether we are a single-player title or multi-player title + virtual bool IsMultiplayer() = 0; + + // Prepare network startup params for the title + virtual void PrepareNetStartupParams( void *pNetStartupParams ) = 0; + + // Get total number of players supported by the title + virtual int GetTotalNumPlayersSupported() = 0; + + // Get a guest player name + virtual char const * GetGuestPlayerName( int iUserIndex ) = 0; + + // Decipher title data fields + virtual TitleDataFieldsDescription_t const * DescribeTitleDataStorage() = 0; + + // Title achievements + virtual TitleAchievementsDescription_t const * DescribeTitleAchievements() = 0; + + // Title avatar awards + virtual TitleAvatarAwardsDescription_t const * DescribeTitleAvatarAwards() = 0; + + // Title leaderboards + virtual KeyValues * DescribeTitleLeaderboard( char const *szLeaderboardView ) = 0; + + // Sets up all necessary client-side convars and user info before + // connecting to server + virtual void PrepareClientForConnect( KeyValues *pSettings ) = 0; + + // Start up a listen server with the given settings + virtual bool StartServerMap( KeyValues *pSettings ) = 0; +}; + +// +// Matchmaking title settings extension interface +// + +abstract_class IMatchTitleGameSettingsMgr +{ +public: + // Extends server game details + virtual void ExtendServerDetails( KeyValues *pDetails, KeyValues *pRequest ) = 0; + + // Adds the essential part of game details to be broadcast + virtual void ExtendLobbyDetailsTemplate( KeyValues *pDetails, char const *szReason, KeyValues *pFullSettings ) = 0; + + // Extends game settings update packet for lobby transition, + // either due to a migration or due to an endgame condition + virtual void ExtendGameSettingsForLobbyTransition( KeyValues *pSettings, KeyValues *pSettingsUpdate, bool bEndGame ) = 0; + + + // Rolls up game details for matches grouping + // valid pDetails, null pRollup + // returns a rollup representation of pDetails to be used as an indexing key + // valid pDetails, valid pRollup (usually called second time) + // rolls the details into the rollup, aggregates some values, when + // the aggregate values are missing in pRollup, then this is the first + // details entry being aggregated and would establish the first rollup + // returns pRollup + // null pDetails, valid pRollup + // tries to determine if the rollup should remain even though no details + // matched it, adjusts pRollup to represent no aggregated data + // returns null to drop pRollup, returns pRollup to keep rollup + virtual KeyValues * RollupGameDetails( KeyValues *pDetails, KeyValues *pRollup, KeyValues *pQuery ) = 0; + + + // Defines session search keys for matchmaking + virtual KeyValues * DefineSessionSearchKeys( KeyValues *pSettings ) = 0; + + // Defines dedicated server search key + virtual KeyValues * DefineDedicatedSearchKeys( KeyValues *pSettings ) = 0; + + + // Initializes full game settings from potentially abbreviated game settings + virtual void InitializeGameSettings( KeyValues *pSettings ) = 0; + + // Extends game settings update packet before it gets merged with + // session settings and networked to remote clients + virtual void ExtendGameSettingsUpdateKeys( KeyValues *pSettings, KeyValues *pUpdateDeleteKeys ) = 0; + + // Prepares system for session creation + virtual KeyValues * PrepareForSessionCreate( KeyValues *pSettings ) = 0; + + + // Executes the command on the session settings, this function on host + // is allowed to modify Members/Game subkeys and has to fill in modified players KeyValues + // When running on a remote client "ppPlayersUpdated" is NULL and players cannot + // be modified + virtual void ExecuteCommand( KeyValues *pCommand, KeyValues *pSessionSystemData, KeyValues *pSettings, KeyValues **ppPlayersUpdated ) = 0; + + // Prepares the host lobby for game or adjust settings of new players who + // join a game in progress, this function is allowed to modify + // Members/Game subkeys and has to fill in modified players KeyValues + virtual void PrepareLobbyForGame( KeyValues *pSettings, KeyValues **ppPlayersUpdated ) = 0; + + // Prepares the host team lobby for game adjusting the game settings + // this function is allowed to prepare modification package to update + // Game subkeys. + // Returns the update/delete package to be applied to session settings + // and pushed to dependent two sesssion of the two teams. + virtual KeyValues * PrepareTeamLinkForGame( KeyValues *pSettingsLocal, KeyValues *pSettingsRemote ) = 0; +}; + +#endif // IMATCHTITLE_H diff --git a/public/matchmaking/imatchvoice.h b/public/matchmaking/imatchvoice.h new file mode 100644 index 0000000..82b8113 --- /dev/null +++ b/public/matchmaking/imatchvoice.h @@ -0,0 +1,38 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHVOICE_H +#define IMATCHVOICE_H + +#ifdef _WIN32 +#pragma once +#endif + +abstract_class IMatchVoice +{ +public: + // Whether remote player talking can be visualized / audible + virtual bool CanPlaybackTalker( XUID xuidTalker ) = 0; + + // Whether we are explicitly muting a remote player + virtual bool IsTalkerMuted( XUID xuidTalker ) = 0; + + // Whether we are muting any player on the player's machine + virtual bool IsMachineMuted( XUID xuidPlayer ) = 0; + + // Whether voice recording mode is currently active + virtual bool IsVoiceRecording() = 0; + + // Enable or disable voice recording + virtual void SetVoiceRecording( bool bRecordingEnabled ) = 0; + + // Enable or disable voice mute for a given talker + virtual void MuteTalker( XUID xuidTalker, bool bMute ) = 0; +}; + +#endif + diff --git a/public/matchmaking/iplayer.h b/public/matchmaking/iplayer.h new file mode 100644 index 0000000..c0032f5 --- /dev/null +++ b/public/matchmaking/iplayer.h @@ -0,0 +1,77 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + + +#ifndef _IPLAYER_H_ +#define _IPLAYER_H_ + +#include "tier1/KeyValues.h" + +struct UserProfileData +{ + float reputation; + int32 difficulty; + int32 sensitivity; + int32 yaxis; + int32 vibration; + int32 color1, color2; + int32 action_autoaim; + int32 action_autocenter; + int32 action_movementcontrol; + int32 region; + int32 achearned; + int32 cred; + int32 zone; + int32 titlesplayed; + int32 titleachearned; + int32 titlecred; +}; + +//Players are a wrapper or a networked player, as such they may not have all the information current, particularly when first created. +abstract_class IPlayer +{ +public: + enum OnlineState_t + { + STATE_OFFLINE, + STATE_NO_MULTIPLAYER, + STATE_ONLINE, + }; + +public: + //Info + virtual XUID GetXUID() = 0; + virtual int GetPlayerIndex() = 0; + + virtual char const * GetName() = 0; + + virtual OnlineState_t GetOnlineState() = 0; +}; + +abstract_class IPlayerFriend : public IPlayer +{ +public: + virtual wchar_t const * GetRichPresence() = 0; + + virtual KeyValues *GetGameDetails() = 0; + + virtual bool IsJoinable() = 0; + virtual void Join() = 0; +}; + +abstract_class IPlayerLocal : public IPlayer +{ +public: + virtual const UserProfileData& GetPlayerProfileData() = 0; + + virtual const void * GetPlayerTitleData( int iTitleDataIndex ) = 0; + virtual void UpdatePlayerTitleData( int iTitleDataIndex, const void *pvNewTitleData, int numBytesOffset, int numNewBytes ) = 0; + + virtual void GetLeaderboardData( KeyValues *pLeaderboardInfo ) = 0; + virtual void UpdateLeaderboardData( KeyValues *pLeaderboardInfo ) = 0; +}; + +#endif diff --git a/public/matchmaking/iplayermanager.h b/public/matchmaking/iplayermanager.h new file mode 100644 index 0000000..c6ae4cf --- /dev/null +++ b/public/matchmaking/iplayermanager.h @@ -0,0 +1,46 @@ +//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============// + +#ifndef _IPLAYERMANAGER_H_ +#define _IPLAYERMANAGER_H_ + +class IPlayer; +class IPlayerFriend; +class IPlayerLocal; + +abstract_class IPlayerManager +{ +public: + // + // EnableServersUpdate + // controls whether friends data is being updated in the background + // + virtual void EnableFriendsUpdate( bool bEnable ) = 0; + + // + // GetLocalPlayer + // returns a local player interface for a given controller index + // + virtual IPlayerLocal * GetLocalPlayer( int iController ) = 0; + + // + // GetNumFriends + // returns number of friends discovered and for which data is available + // + virtual int GetNumFriends() = 0; + + // + // GetFriend + // returns player interface to the given friend or NULL if friend not found or not available + // + virtual IPlayerFriend * GetFriendByIndex( int index ) = 0; + virtual IPlayerFriend * GetFriendByXUID( XUID xuid ) = 0; + + // + // FindPlayer + // returns player interface by player's XUID or NULL if player not found or not available + // + virtual IPlayer * FindPlayer( XUID xuid ) = 0; +}; + + +#endif diff --git a/public/matchmaking/isearchmanager.h b/public/matchmaking/isearchmanager.h new file mode 100644 index 0000000..e75e761 --- /dev/null +++ b/public/matchmaking/isearchmanager.h @@ -0,0 +1,62 @@ +#ifndef _ISEARCHMANAGER_H_ +#define _ISEARCHMANAGER_H_ + +class IMatchSearchResult; +class ISearchManager; + +#include "imatchsystem.h" + +abstract_class IMatchSearchResult +{ +public: + // + // GetOnlineId + // returns result online id to store as reference + // + virtual XUID GetOnlineId() = 0; + + // + // GetGameDetails + // returns result game details + // + virtual KeyValues *GetGameDetails() = 0; + + // + // IsJoinable and Join + // returns whether result is joinable and initiates join to the result + // + virtual bool IsJoinable() = 0; + virtual void Join() = 0; +}; + +abstract_class ISearchManager +{ +public: + // + // EnableResultsUpdate + // controls whether server data is being updated in the background + // + virtual void EnableResultsUpdate( bool bEnable, KeyValues *pSearchParams = NULL ) = 0; + + // + // GetNumResults + // returns number of results discovered and for which data is available + // + virtual int GetNumResults() = 0; + + // + // GetResultByIndex / GetResultByOnlineId + // returns result interface to the given result or NULL if result not found or not available + // + virtual IMatchSearchResult* GetResultByIndex( int iResultIdx ) = 0; + virtual IMatchSearchResult* GetResultByOnlineId( XUID xuidResultOnline ) = 0; + + // + // Destroy + // destroys the search manager and all its results + // + virtual void Destroy() = 0; +}; + + +#endif // _ISEARCHMANAGER_H_ diff --git a/public/matchmaking/iservermanager.h b/public/matchmaking/iservermanager.h new file mode 100644 index 0000000..24c4033 --- /dev/null +++ b/public/matchmaking/iservermanager.h @@ -0,0 +1,56 @@ +#ifndef _ISERVERMANAGER_H_ +#define _ISERVERMANAGER_H_ + +class IServer; +class IServerManager; + +#include "imatchsystem.h" + +abstract_class IMatchServer +{ +public: + // + // GetOnlineId + // returns server online id to store as reference + // + virtual XUID GetOnlineId() = 0; + + // + // GetGameDetails + // returns server game details + // + virtual KeyValues *GetGameDetails() = 0; + + // + // IsJoinable and Join + // returns whether server is joinable and initiates join to the server + // + virtual bool IsJoinable() = 0; + virtual void Join() = 0; +}; + +abstract_class IServerManager +{ +public: + // + // EnableServersUpdate + // controls whether server data is being updated in the background + // + virtual void EnableServersUpdate( bool bEnable ) = 0; + + // + // GetNumServers + // returns number of servers discovered and for which data is available + // + virtual int GetNumServers() = 0; + + // + // GetServerByIndex / GetServerByOnlineId + // returns server interface to the given server or NULL if server not found or not available + // + virtual IMatchServer* GetServerByIndex( int iServerIdx ) = 0; + virtual IMatchServer* GetServerByOnlineId( XUID xuidServerOnline ) = 0; +}; + + +#endif // _ISERVERMANAGER_H_ diff --git a/public/matchmaking/swarm/imatchext_swarm.h b/public/matchmaking/swarm/imatchext_swarm.h new file mode 100644 index 0000000..9a7fa77 --- /dev/null +++ b/public/matchmaking/swarm/imatchext_swarm.h @@ -0,0 +1,83 @@ +//===== Copyright c 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATCHEXT_SWARM_H +#define IMATCHEXT_SWARM_H + +#ifdef _WIN32 +#pragma once +#endif + + +// +// +// WARNING!! WARNING!! WARNING!! WARNING!! +// This structure TitleData1 should remain +// intact after we ship otherwise +// users profiles will be busted. +// You are allowed to add fields at the end +// as long as structure size stays under +// XPROFILE_SETTING_MAX_SIZE = 1000 bytes. +// WARNING!! WARNING!! WARNING!! WARNING!! +// +struct TitleData1 +{ + uint64 unused; +}; + +// +// +// WARNING!! WARNING!! WARNING!! WARNING!! +// This structure TitleData2 should remain +// intact after we ship otherwise +// users profiles will be busted. +// You are allowed to add fields at the end +// as long as structure size stays under +// XPROFILE_SETTING_MAX_SIZE = 1000 bytes. +// WARNING!! WARNING!! WARNING!! WARNING!! +// +struct TitleData2 +{ + // Achievement Counters + uint8 iCountXxx; // ACHIEVEMENT_XXX + uint8 iCountYyy; // ACHIEVEMENT_YYY + + // Achievement Component Bitfields + uint8 iCompXxx; // ACHIEVEMENT_XXX +}; + +// +// +// WARNING!! WARNING!! WARNING!! WARNING!! +// This structure TitleData3 should remain +// intact after we ship otherwise +// users profiles will be busted. +// You are allowed to add fields at the end +// as long as structure size stays under +// XPROFILE_SETTING_MAX_SIZE = 1000 bytes. +// WARNING!! WARNING!! WARNING!! WARNING!! +// +struct TitleData3 +{ + uint64 unused; // unused, free for taking +}; + + +abstract_class IMatchExtSwarm +{ +public: + // Get server map information for the session settings + virtual KeyValues * GetAllMissions() = 0; + virtual KeyValues * GetMapInfo( KeyValues *pSettings, KeyValues **ppMissionInfo = NULL ) = 0; + virtual KeyValues * GetMapInfoByBspName( KeyValues *pSettings, char const *szBspMapName, KeyValues **ppMissionInfo = NULL ) = 0; +}; + +#define IMATCHEXT_SWARM_INTERFACE "IMATCHEXT_SWARM_INTERFACE_001" + +// #define SWARM_DLC_NUMBER_XXX 20 + +#endif // IMATCHEXT_L4D_H diff --git a/public/materialsystem/IColorCorrection.h b/public/materialsystem/IColorCorrection.h new file mode 100644 index 0000000..d4691b2 --- /dev/null +++ b/public/materialsystem/IColorCorrection.h @@ -0,0 +1,75 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =====// +// +// Purpose: +// +//===========================================================================// + +#ifndef ICOLORCORRECTION_H +#define ICOLORCORRECTION_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "tier0/basetypes.h" +#include "bitmap/imageformat.h" + +typedef unsigned int ColorCorrectionHandle_t; +struct ShaderColorCorrectionInfo_t; + +abstract_class IColorCorrectionSystem +{ +public: + virtual void Init() = 0; + virtual void Shutdown() = 0; + + virtual ColorCorrectionHandle_t AddLookup( const char *pName ) = 0; + virtual bool RemoveLookup( ColorCorrectionHandle_t handle ) = 0; + + virtual void SetLookupWeight( ColorCorrectionHandle_t handle, float flWeight ) = 0; + virtual float GetLookupWeight( ColorCorrectionHandle_t handle ) = 0; + virtual float GetLookupWeight( int i ) = 0; + + virtual void LockLookup() = 0; + virtual void LockLookup( ColorCorrectionHandle_t handle ) = 0; + + virtual void UnlockLookup() = 0; + virtual void UnlockLookup( ColorCorrectionHandle_t handle, bool bDownload = true ) = 0; + + virtual void SetLookup( RGBX5551_t inColor, color24 outColor ) = 0; + virtual void SetLookup( ColorCorrectionHandle_t handle, RGBX5551_t inColor, color24 outColor ) = 0; + + virtual color24 GetLookup( RGBX5551_t inColor ) = 0; + virtual color24 GetLookup( ColorCorrectionHandle_t handle, RGBX5551_t inColor ) = 0; + + virtual void LoadLookup( const char *pLookupName ) = 0; + virtual void LoadLookup( ColorCorrectionHandle_t handle, const char *pLookupName ) = 0; + + virtual void CopyLookup( const color24 *pSrcColorCorrection ) = 0; + virtual void CopyLookup( ColorCorrectionHandle_t handle, const color24 *pSrcColorCorrection ) = 0; + + virtual void ResetLookup( ColorCorrectionHandle_t handle ) = 0; + virtual void ResetLookup( ) = 0; + + virtual void ReleaseTextures( ) = 0; + virtual void RestoreTextures( ) = 0; + + virtual void ResetLookupWeights( ) = 0; + + virtual int GetNumLookups( ) = 0; + + virtual color24 ConvertToColor24( RGBX5551_t inColor ) = 0; + + virtual void SetResetable( ColorCorrectionHandle_t handle, bool bResetable ) = 0; + + virtual void EnableColorCorrection( bool bEnable ) = 0; + + // FIXME: Move this to a private interface only the material system can see? + virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ) = 0; + + virtual void OnProceduralRegenComplete( ColorCorrectionHandle_t handle ) = 0; +}; + +#endif + diff --git a/public/materialsystem/IShader.h b/public/materialsystem/IShader.h new file mode 100644 index 0000000..aad2820 --- /dev/null +++ b/public/materialsystem/IShader.h @@ -0,0 +1,218 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADER_H +#define ISHADER_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/ishaderapi.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IMaterialVar; +class IShaderShadow; +class IShaderDynamicAPI; +class IShaderInit; +class CBasePerMaterialContextData; + + +//----------------------------------------------------------------------------- +// Shader flags +//----------------------------------------------------------------------------- +enum ShaderFlags_t +{ + SHADER_NOT_EDITABLE = 0x1 +}; + + +//----------------------------------------------------------------------------- +// Shader parameter flags +//----------------------------------------------------------------------------- +enum ShaderParamFlags_t +{ + SHADER_PARAM_NOT_EDITABLE = 0x1 +}; + + +//----------------------------------------------------------------------------- +// Information about each shader parameter +//----------------------------------------------------------------------------- +struct ShaderParamInfo_t +{ + const char *m_pName; + const char *m_pHelp; + ShaderParamType_t m_Type; + const char *m_pDefaultValue; + int m_nFlags; +}; + + + +//----------------------------------------------------------------------------- +// shaders can keep per material data in classes descended from this +//----------------------------------------------------------------------------- +class CBasePerMaterialContextData +{ +public: + uint32 m_nVarChangeID; + bool m_bMaterialVarsChanged; // set by mat system when material vars change. shader should rehtink and then clear the var + + FORCEINLINE CBasePerMaterialContextData( void ) + { + m_bMaterialVarsChanged = true; + m_nVarChangeID = 0xffffffff; + } + + // virtual destructor so that derived classes can have their own data to be cleaned up on + // delete of material + virtual ~CBasePerMaterialContextData( void ) + { + } +}; + + +//----------------------------------------------------------------------------- +// shaders can keep per instance data in classes descended from this +//----------------------------------------------------------------------------- +class CBasePerInstanceContextData +{ +public: + // virtual destructor so that derived classes can have their own data + // to be cleaned up on delete of material + virtual ~CBasePerInstanceContextData( void ) + { + } +}; + + +//----------------------------------------------------------------------------- +// Standard vertex shader constants +//----------------------------------------------------------------------------- +enum +{ + // Standard vertex shader constants + VERTEX_SHADER_MATH_CONSTANTS0 = 0, + VERTEX_SHADER_MATH_CONSTANTS1 = 1, + VERTEX_SHADER_CAMERA_POS = 2, + VERTEX_SHADER_LIGHT_INDEX = 3, + VERTEX_SHADER_MODELVIEWPROJ = 4, + VERTEX_SHADER_VIEWPROJ = 8, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_12 = 12, + VERTEX_SHADER_FLEXSCALE = 13, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_10 = 14, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_11 = 15, + VERTEX_SHADER_FOG_PARAMS = 16, + VERTEX_SHADER_VIEWMODEL = 17, + VERTEX_SHADER_AMBIENT_LIGHT = 21, + VERTEX_SHADER_LIGHTS = 27, + VERTEX_SHADER_LIGHT0_POSITION = 29, + VERTEX_SHADER_MODULATION_COLOR = 47, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_0 = 48, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_1 = 49, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_2 = 50, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_3 = 51, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_4 = 52, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_5 = 53, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_6 = 54, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_7 = 55, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_8 = 56, + VERTEX_SHADER_SHADER_SPECIFIC_CONST_9 = 57, + VERTEX_SHADER_MODEL = 58, + // + // We reserve up through 217 for the 53 bones supported on DX9 + // + + VERTEX_SHADER_FLEX_WEIGHTS = 1024, + VERTEX_SHADER_MAX_FLEX_WEIGHT_COUNT = 512, +}; + +#define VERTEX_SHADER_BONE_TRANSFORM( k ) ( VERTEX_SHADER_MODEL + 3 * (k) ) + +//----------------------------------------------------------------------------- +// Standard vertex shader constants +//----------------------------------------------------------------------------- +enum +{ + // Standard vertex shader constants + VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST = 0, + VERTEX_SHADER_LIGHT_ENABLE_BOOL_CONST_COUNT = 4, + + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_0 = 4, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_1 = 5, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_2 = 6, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_3 = 7, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_4 = 8, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_5 = 9, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_6 = 10, + VERTEX_SHADER_SHADER_SPECIFIC_BOOL_CONST_7 = 11, +}; + + +//----------------------------------------------------------------------------- +// The public methods exposed by each shader +//----------------------------------------------------------------------------- +abstract_class IShader +{ +public: + // Returns the shader name + virtual char const* GetName( ) const = 0; + + // returns the shader fallbacks + virtual char const* GetFallbackShader( IMaterialVar** params ) const = 0; + + // Shader parameters + virtual int GetParamCount( ) const = 0; + virtual const ShaderParamInfo_t& GetParamInfo( int paramIndex ) const = 0; + + // These functions must be implemented by the shader + virtual void InitShaderParams( IMaterialVar** ppParams, const char *pMaterialName ) = 0; + virtual void InitShaderInstance( IMaterialVar** ppParams, IShaderInit *pShaderInit, const char *pMaterialName, const char *pTextureGroupName ) = 0; + virtual void DrawElements( IMaterialVar **params, int nModulationFlags, + IShaderShadow* pShaderShadow, IShaderDynamicAPI* pShaderAPI, + VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContextDataPtr, CBasePerInstanceContextData** pInstanceDataPtr ) = 0; + + // FIXME: Figure out a better way to do this? + virtual int ComputeModulationFlags( IMaterialVar** params, IShaderDynamicAPI* pShaderAPI ) = 0; + virtual bool NeedsPowerOfTwoFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame = true ) const = 0; + virtual bool NeedsFullFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame ) const = 0; + virtual bool IsTranslucent( IMaterialVar **params ) const = 0; + + virtual int GetFlags() const = 0; +}; + + +//----------------------------------------------------------------------------- +// Shader dictionaries defined in DLLs +//----------------------------------------------------------------------------- +enum PrecompiledShaderType_t +{ + PRECOMPILED_VERTEX_SHADER = 0, + PRECOMPILED_PIXEL_SHADER, + + PRECOMPILED_SHADER_TYPE_COUNT, +}; + + +//----------------------------------------------------------------------------- +// Flags field of PrecompiledShader_t +//----------------------------------------------------------------------------- +enum +{ + // runtime flags + SHADER_IS_ASM = 0x1, + SHADER_FAILED_LOAD = 0x2, +}; + +#endif // ISHADER_H diff --git a/public/materialsystem/MaterialSystemUtil.cpp b/public/materialsystem/MaterialSystemUtil.cpp new file mode 100644 index 0000000..7aa5456 --- /dev/null +++ b/public/materialsystem/MaterialSystemUtil.cpp @@ -0,0 +1,253 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// + +#include "materialsystem/MaterialSystemUtil.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/itexture.h" +#include "materialsystem/imaterialsystem.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Little utility class to deal with material references +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CMaterialReference::CMaterialReference( char const* pMaterialName, const char *pTextureGroupName, bool bComplain ) : m_pMaterial( 0 ) +{ + if ( pMaterialName ) + { + Assert( pTextureGroupName ); + Init( pMaterialName, pTextureGroupName, bComplain ); + } +} + +const CMaterialReference& CMaterialReference::operator=( const CMaterialReference &ref ) +{ + Init( ref.m_pMaterial ); + return *this; +} + +CMaterialReference::~CMaterialReference() +{ + Shutdown(); +} + +//----------------------------------------------------------------------------- +// Attach to a material +//----------------------------------------------------------------------------- +void CMaterialReference::Init( char const* pMaterialName, const char *pTextureGroupName, bool bComplain ) +{ + IMaterial *pMaterial = materials->FindMaterial( pMaterialName, pTextureGroupName, bComplain); + Assert( pMaterial ); + Init( pMaterial ); +} + +void CMaterialReference::Init( const char *pMaterialName, KeyValues *pVMTKeyValues ) +{ + // CreateMaterial has a refcount of 1 + Shutdown(); + m_pMaterial = materials->CreateMaterial( pMaterialName, pVMTKeyValues ); +} + +void CMaterialReference::Init( const char *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues ) +{ + IMaterial *pMaterial = materials->FindProceduralMaterial( pMaterialName, pTextureGroupName, pVMTKeyValues ); + Assert( pMaterial ); + Init( pMaterial ); +} + +void CMaterialReference::Init( IMaterial* pMaterial ) +{ + if ( m_pMaterial != pMaterial ) + { + Shutdown(); + m_pMaterial = pMaterial; + if ( m_pMaterial ) + { + m_pMaterial->IncrementReferenceCount(); + } + } +} + +void CMaterialReference::Init( CMaterialReference& ref ) +{ + if ( m_pMaterial != ref.m_pMaterial ) + { + Shutdown(); + m_pMaterial = ref.m_pMaterial; + if (m_pMaterial) + { + m_pMaterial->IncrementReferenceCount(); + } + } +} + +//----------------------------------------------------------------------------- +// Detach from a material +//----------------------------------------------------------------------------- +void CMaterialReference::Shutdown( bool bDeleteIfUnreferenced /*=false*/ ) +{ + if ( m_pMaterial && materials ) + { + m_pMaterial->DecrementReferenceCount(); + if ( bDeleteIfUnreferenced ) + { + m_pMaterial->DeleteIfUnreferenced(); + } + m_pMaterial = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Little utility class to deal with texture references +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CTextureReference::CTextureReference( ) : m_pTexture(NULL) +{ +} + +CTextureReference::CTextureReference( const CTextureReference &ref ) : m_pTexture( NULL ) +{ + Init( ref.m_pTexture ); +} + +const CTextureReference& CTextureReference::operator=( CTextureReference &ref ) +{ + Init( ref.m_pTexture ); + return *this; +} + +CTextureReference::~CTextureReference( ) +{ + Shutdown(); +} + +//----------------------------------------------------------------------------- +// Attach to a texture +//----------------------------------------------------------------------------- +void CTextureReference::Init( char const* pTextureName, const char *pTextureGroupName, bool bComplain ) +{ + Shutdown(); + m_pTexture = materials->FindTexture( pTextureName, pTextureGroupName, bComplain ); + if ( m_pTexture ) + { + m_pTexture->IncrementReferenceCount(); + } +} + +void CTextureReference::Init( ITexture* pTexture ) +{ + if ( m_pTexture != pTexture ) + { + Shutdown(); + + m_pTexture = pTexture; + if (m_pTexture) + { + m_pTexture->IncrementReferenceCount(); + } + } +} + +void CTextureReference::InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, ImageFormat fmt, int nFlags ) +{ + Shutdown(); + + m_pTexture = materials->CreateProceduralTexture( pTextureName, pTextureGroupName, w, h, fmt, nFlags ); + + // NOTE: The texture reference is already incremented internally above! + /* + if ( m_pTexture ) + { + m_pTexture->IncrementReferenceCount(); + } + */ +} + +void CTextureReference::InitRenderTarget( int w, int h, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName /* = NULL */ ) +{ + Shutdown(); + + int textureFlags = TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT; + if ( depth == MATERIAL_RT_DEPTH_ONLY ) + textureFlags |= TEXTUREFLAGS_POINTSAMPLE; + + int renderTargetFlags = bHDR ? CREATERENDERTARGETFLAGS_HDR : 0; + + // NOTE: Refcount returned by CreateRenderTargetTexture is 1 + m_pTexture = materials->CreateNamedRenderTargetTextureEx( pStrOptionalName, w, h, sizeMode, fmt, + depth, textureFlags, renderTargetFlags ); + + Assert( m_pTexture ); +} + +//----------------------------------------------------------------------------- +// Detach from a texture +//----------------------------------------------------------------------------- +void CTextureReference::Shutdown( bool bDeleteIfUnReferenced ) +{ + if ( m_pTexture && materials ) + { + m_pTexture->DecrementReferenceCount(); + if ( bDeleteIfUnReferenced ) + { + m_pTexture->DeleteIfUnreferenced(); + } + m_pTexture = NULL; + } +} + +//----------------------------------------------------------------------------- +// Builds ONLY the system ram render target. Used when caller is explicitly managing. +// The paired EDRAM surface can be built in an alternate format. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void CTextureReference::InitRenderTargetTexture( int w, int h, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName ) +{ + // other variants not implemented yet + Assert( depth == MATERIAL_RT_DEPTH_NONE || depth == MATERIAL_RT_DEPTH_SHARED ); + Assert( !bHDR ); + + int renderTargetFlags = CREATERENDERTARGETFLAGS_NOEDRAM; + + m_pTexture = materials->CreateNamedRenderTargetTextureEx( + pStrOptionalName, + w, + h, + sizeMode, + fmt, + depth, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + renderTargetFlags ); + Assert( m_pTexture ); +} +#endif + +//----------------------------------------------------------------------------- +// Builds ONLY the EDRAM render target surface. Used when caller is explicitly managing. +// The paired system memory texture can be built in an alternate format. +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void CTextureReference::InitRenderTargetSurface( int width, int height, ImageFormat fmt, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount ) +{ + // texture has to be created first + Assert( m_pTexture && m_pTexture->IsRenderTarget() ); + + m_pTexture->CreateRenderTargetSurface( width, height, fmt, bSameAsTexture, multiSampleCount ); +} +#endif + diff --git a/public/materialsystem/MaterialSystemUtil.h b/public/materialsystem/MaterialSystemUtil.h new file mode 100644 index 0000000..b67d7ea --- /dev/null +++ b/public/materialsystem/MaterialSystemUtil.h @@ -0,0 +1,103 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// +#ifndef MATERIALSYSTEMUTIL_H +#define MATERIALSYSTEMUTIL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "bitmap/imageformat.h" //ImageFormat enum definition +#include "materialsystem/imaterialsystem.h" // RenderTargetSizeMode_t and MaterialRenderTargetDepth_t definition +#include "materialsystem/itexture.h" +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class KeyValues; + +class KeyValues; + + +//----------------------------------------------------------------------------- +// Little utility class to deal with material references +//----------------------------------------------------------------------------- +class CMaterialReference +{ +public: + // constructor, destructor + CMaterialReference( char const* pMaterialName = 0, const char *pTextureGroupName = 0, bool bComplain = true ); + ~CMaterialReference(); + + // Attach to a material + void Init( const char* pMaterialName, const char *pTextureGroupName, bool bComplain = true ); + void Init( const char *pMaterialName, KeyValues *pVMTKeyValues ); + void Init( IMaterial* pMaterial ); + void Init( CMaterialReference& ref ); + void Init( const char *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues ); + + // Detach from a material + void Shutdown( bool bDeleteIfUnreferenced = false ); + bool IsValid() { return m_pMaterial != 0; } + + // Automatic casts to IMaterial + operator IMaterial*() { return m_pMaterial; } + operator IMaterial const*() const { return m_pMaterial; } + IMaterial* operator->() { return m_pMaterial; } + + // Assignment operator + const CMaterialReference& operator=( const CMaterialReference &ref ); + + +private: + CMaterialReference( CMaterialReference &ref ) { } + + IMaterial* m_pMaterial; +}; + +//----------------------------------------------------------------------------- +// Little utility class to deal with texture references +//----------------------------------------------------------------------------- +class CTextureReference +{ +public: + // constructor, destructor + CTextureReference( ); + CTextureReference( const CTextureReference &ref ); + ~CTextureReference(); + + // Attach to a texture + void Init( char const* pTexture, const char *pTextureGroupName, bool bComplain = true ); + void InitProceduralTexture( const char *pTextureName, const char *pTextureGroupName, int w, int h, ImageFormat fmt, int nFlags ); + void InitRenderTarget( int w, int h, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName = NULL ); +#if defined( _X360 ) + // used when RT coupling is disparate (texture is DDR based, surface is EDRAM based) + void InitRenderTargetTexture( int width, int height, RenderTargetSizeMode_t sizeMode, ImageFormat fmt, MaterialRenderTargetDepth_t depth, bool bHDR, char *pStrOptionalName = NULL ); + void InitRenderTargetSurface( int width, int height, ImageFormat fmt, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount = RT_MULTISAMPLE_NONE ); +#endif + void Init( ITexture* pTexture ); + + // Detach from a texture + void Shutdown( bool bDeleteIfUnReferenced = false ); + bool IsValid() { return m_pTexture != 0; } + + // Automatic casts to ITexture + operator ITexture*() { return m_pTexture; } + operator ITexture const*() const { return m_pTexture; } + ITexture* operator->() { return m_pTexture; } + + // Assignment operator + const CTextureReference& operator=( CTextureReference &ref ); + +private: + ITexture* m_pTexture; +}; + + +#endif // !MATERIALSYSTEMUTIL_H diff --git a/public/materialsystem/deformations.h b/public/materialsystem/deformations.h new file mode 100644 index 0000000..09c4474 --- /dev/null +++ b/public/materialsystem/deformations.h @@ -0,0 +1,58 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =====// +// +// Purpose: +// +//===========================================================================// + +#ifndef DEFORMATIONS_H +#define DEFORMATIONS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +// nonlinear transformations which may be applied to model vertices when rendering. must be powers of two +enum DeformationType_t +{ + DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE = 1, // minxyz.minsoftness / maxxyz.maxsoftness +}; + + +struct DeformationBase_t // base class. don't use this +{ + DeformationType_t m_eType; +}; + + +struct BoxDeformation_t : DeformationBase_t +{ + // don't change the layout without changing code in shaderapidx8!!!! + Vector m_SourceMins; // cube to clamp within + float m_flPad0; + Vector m_SourceMaxes; + float m_flPad1; + + Vector m_ClampMins; + float m_flPad2; + Vector m_ClampMaxes; + float m_flPad3; + + FORCEINLINE BoxDeformation_t( void ) + { + m_eType = DEFORMATION_CLAMP_TO_BOX_IN_WORLDSPACE; + // invalid cube + m_SourceMins.Init( 0,0,0 ); + m_SourceMaxes.Init( -1, -1, -1 ); + + // no clamp + m_ClampMins.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + m_ClampMaxes.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + } + +}; + + + +#endif diff --git a/public/materialsystem/hardwareverts.h b/public/materialsystem/hardwareverts.h new file mode 100644 index 0000000..aeb60d7 --- /dev/null +++ b/public/materialsystem/hardwareverts.h @@ -0,0 +1,86 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Hardware Verts +// +// Contains data purposely formatted for a dma copy into a D3D Vertex Buffer. +// The file is divided into two partitions, the foremost contains the static +// portion (header), the latter contains the streamable compliant portion. +// The streamable component starts and ends on a sector (512) aligned boundary. +// The header identifies the vertex format of the data and the atomic sizes of each component. +// The hierarchial mesh is flattened for dma but the vertex counts are available +// per mesh to transfer each mesh individually. +//=============================================================================// + +#ifndef HARDWAREVERTS_H +#define HARDWAREVERTS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "datamap.h" + +// valve hardware vertexes +#define VHV_VERSION 2 + +namespace HardwareVerts +{ + +#pragma pack(1) + +struct MeshHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + + // this mesh is part of this lod + unsigned int m_nLod; + + // this mesh has this many vertexes + unsigned int m_nVertexes; + + // starting at this offset + unsigned int m_nOffset; + + unsigned int m_nUnused[4]; +}; + +struct FileHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + + // file version as defined by VHV_VERSION + int m_nVersion; + + // must match checkSum in the .mdl header + unsigned int m_nChecksum; + + // a vertex consists of these components + VertexFormatFlags_t m_nVertexFlags; + + // the byte size of a single vertex + // this won't be adequate, need some concept of byte format i.e. rgbexp32 vs rgba8888 + unsigned int m_nVertexSize; + + // total number of vertexes + unsigned int m_nVertexes; + + int m_nMeshes; + inline MeshHeader_t *pMesh( int nMesh ) const + { + return (MeshHeader_t *)(((byte *)this) + sizeof(FileHeader_t)) + nMesh; + }; + + inline void *pVertexBase( int nMesh ) const + { + return (void *)((byte *)this + pMesh( nMesh )->m_nOffset); + }; + + unsigned int m_nUnused[4]; +}; + +#pragma pack() + +}; // end namespace + +#endif // HARDWAREVERTS_H + diff --git a/public/materialsystem/idebugtextureinfo.h b/public/materialsystem/idebugtextureinfo.h new file mode 100644 index 0000000..47ff3c9 --- /dev/null +++ b/public/materialsystem/idebugtextureinfo.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef IDEBUGTEXTUREINFO_H +#define IDEBUGTEXTUREINFO_H +#ifdef _WIN32 +#pragma once +#endif + + +class KeyValues; + + +// This interface is actually exported by the shader API DLL. + + +abstract_class IDebugTextureInfo +{ +public: + + // Use this to turn on the mode where it builds the debug texture list. + // At the end of the next frame, GetDebugTextureList() will return a valid list of the textures. + virtual void EnableDebugTextureList( bool bEnable ) = 0; + + // If this is on, then it will return all textures that exist, not just the ones that were bound in the last frame. + // It is required to enable debug texture list to get this. + virtual void EnableGetAllTextures( bool bEnable ) = 0; + + // Use this to get the results of the texture list. + // Do NOT release the KeyValues after using them. + // There will be a bunch of subkeys, each with these values: + // Name - the texture's filename + // Binds - how many times the texture was bound + // Format - ImageFormat of the texture + // Width - Width of the texture + // Height - Height of the texture + // It is required to enable debug texture list to get this. + virtual KeyValues* GetDebugTextureList() = 0; + + // Texture memory usage + enum TextureMemoryType + { + MEMORY_RESERVED_MIN = 0, + MEMORY_BOUND_LAST_FRAME, // sums up textures bound last frame + MEMORY_TOTAL_LOADED, // total texture memory used + MEMORY_ESTIMATE_PICMIP_1, // estimate of running with "picmip 1" + MEMORY_ESTIMATE_PICMIP_2, // estimate of running with "picmip 2" + MEMORY_RESERVED_MAX + }; + + // This returns how much memory was used. + virtual int GetTextureMemoryUsed( TextureMemoryType eTextureMemory ) = 0; + + // Use this to determine if texture debug info was computed within last numFramesAllowed frames. + virtual bool IsDebugTextureListFresh( int numFramesAllowed = 1 ) = 0; + + // Enable debug texture rendering when texture binds should not count towards textures + // used during a frame. Returns the old state of debug texture rendering flag to use + // it for restoring the mode. + virtual bool SetDebugTextureRendering( bool bEnable ) = 0; + +}; + + +#endif // IDEBUGTEXTUREINFO_H diff --git a/public/materialsystem/imaterial.h b/public/materialsystem/imaterial.h new file mode 100644 index 0000000..57fa3c2 --- /dev/null +++ b/public/materialsystem/imaterial.h @@ -0,0 +1,663 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef IMATERIAL_H +#define IMATERIAL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "bitmap/imageformat.h" +#include "materialsystem/imaterialsystem.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + +class IMaterialVar; +class ITexture; +class IMaterialProxy; +class Vector; + +//----------------------------------------------------------------------------- +// Flags for GetVertexFormat +//----------------------------------------------------------------------------- +enum VertexFormatFlags_t +{ + // Indicates an uninitialized VertexFormat_t value + VERTEX_FORMAT_INVALID = 0xFFFFFFFFFFFFFFFFull, + + VERTEX_POSITION = 0x0001, + VERTEX_NORMAL = 0x0002, + VERTEX_COLOR = 0x0004, + VERTEX_SPECULAR = 0x0008, + + VERTEX_TANGENT_S = 0x0010, + VERTEX_TANGENT_T = 0x0020, + VERTEX_TANGENT_SPACE= VERTEX_TANGENT_S | VERTEX_TANGENT_T, + + // Indicates we're using wrinkle + VERTEX_WRINKLE = 0x0040, + + // Indicates we're using bone indices + VERTEX_BONE_INDEX = 0x0080, + + // Indicates this expects a color stream on stream 1 + VERTEX_COLOR_STREAM_1 = 0x0100, + + // Indicates this format shouldn't be bloated to cache align it + // (only used for VertexUsage) + VERTEX_FORMAT_USE_EXACT_FORMAT = 0x0200, + + // Indicates that compressed vertex elements are to be used (see also VertexCompressionType_t) + VERTEX_FORMAT_COMPRESSED = 0x400, + + // Position or normal (if present) should be 4D not 3D + VERTEX_FORMAT_PAD_POS_NORM = 0x800, + + // Update this if you add or remove bits... + VERTEX_LAST_BIT = 11, + + VERTEX_BONE_WEIGHT_BIT = VERTEX_LAST_BIT + 1, + USER_DATA_SIZE_BIT = VERTEX_LAST_BIT + 4, + TEX_COORD_SIZE_BIT = VERTEX_LAST_BIT + 7, + + VERTEX_BONE_WEIGHT_MASK = ( 0x7 << VERTEX_BONE_WEIGHT_BIT ), + USER_DATA_SIZE_MASK = ( 0x7 << USER_DATA_SIZE_BIT ), + + VERTEX_FORMAT_FIELD_MASK = 0x0FF, + + // If everything is off, it's an unknown vertex format + VERTEX_FORMAT_UNKNOWN = 0, +}; + +//----------------------------------------------------------------------------- +// Macros for construction.. +//----------------------------------------------------------------------------- +#define VERTEX_BONEWEIGHT( _n ) ((_n) << VERTEX_BONE_WEIGHT_BIT) +#define VERTEX_USERDATA_SIZE( _n ) ((_n) << USER_DATA_SIZE_BIT) +#define VERTEX_TEXCOORD_MASK( _coord ) (( 0x7ULL ) << ( TEX_COORD_SIZE_BIT + 3 * (_coord) )) + +inline VertexFormat_t VERTEX_TEXCOORD_SIZE( int nIndex, int nNumCoords ) +{ + uint64 n64=nNumCoords; + uint64 nShift=TEX_COORD_SIZE_BIT + (3*nIndex); + return n64 << nShift; +} + + + +//----------------------------------------------------------------------------- +// Gets at various vertex format info... +//----------------------------------------------------------------------------- +inline int VertexFlags( VertexFormat_t vertexFormat ) +{ + return static_cast ( vertexFormat & ( (1 << (VERTEX_LAST_BIT+1)) - 1 ) ); +} + +inline int NumBoneWeights( VertexFormat_t vertexFormat ) +{ + return static_cast ( (vertexFormat >> VERTEX_BONE_WEIGHT_BIT) & 0x7 ); +} + +inline int UserDataSize( VertexFormat_t vertexFormat ) +{ + return static_cast ( (vertexFormat >> USER_DATA_SIZE_BIT) & 0x7 ); +} + +inline int TexCoordSize( int nTexCoordIndex, VertexFormat_t vertexFormat ) +{ + return static_cast ( (vertexFormat >> (TEX_COORD_SIZE_BIT + 3*nTexCoordIndex) ) & 0x7 ); +} + +inline VertexCompressionType_t CompressionType( VertexFormat_t vertexFormat ) +{ + // This is trivial now, but we may add multiple flavors of compressed vertex later on + if ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) + return VERTEX_COMPRESSION_ON; + else + return VERTEX_COMPRESSION_NONE; +} + + +//----------------------------------------------------------------------------- +// VertexElement_t (enumerates all usable vertex elements) +//----------------------------------------------------------------------------- +// FIXME: unify this with VertexFormat_t (i.e. construct the lower bits of VertexFormat_t with "1 << (VertexElement_t)element") +enum VertexElement_t +{ + VERTEX_ELEMENT_NONE = -1, + + // Deliberately explicitly numbered so it's a pain in the ass to change, so you read this: + // #!#!#NOTE#!#!# update GetVertexElementSize, VertexElementToDeclType and + // CVBAllocTracker (elementTable) when you update this! + VERTEX_ELEMENT_POSITION = 0, + VERTEX_ELEMENT_POSITION4D = 1, + VERTEX_ELEMENT_NORMAL = 2, + VERTEX_ELEMENT_NORMAL4D = 3, + VERTEX_ELEMENT_COLOR = 4, + VERTEX_ELEMENT_SPECULAR = 5, + VERTEX_ELEMENT_TANGENT_S = 6, + VERTEX_ELEMENT_TANGENT_T = 7, + VERTEX_ELEMENT_WRINKLE = 8, + VERTEX_ELEMENT_BONEINDEX = 9, + VERTEX_ELEMENT_BONEWEIGHTS1 = 10, + VERTEX_ELEMENT_BONEWEIGHTS2 = 11, + VERTEX_ELEMENT_BONEWEIGHTS3 = 12, + VERTEX_ELEMENT_BONEWEIGHTS4 = 13, + VERTEX_ELEMENT_USERDATA1 = 14, + VERTEX_ELEMENT_USERDATA2 = 15, + VERTEX_ELEMENT_USERDATA3 = 16, + VERTEX_ELEMENT_USERDATA4 = 17, + VERTEX_ELEMENT_TEXCOORD1D_0 = 18, + VERTEX_ELEMENT_TEXCOORD1D_1 = 19, + VERTEX_ELEMENT_TEXCOORD1D_2 = 20, + VERTEX_ELEMENT_TEXCOORD1D_3 = 21, + VERTEX_ELEMENT_TEXCOORD1D_4 = 22, + VERTEX_ELEMENT_TEXCOORD1D_5 = 23, + VERTEX_ELEMENT_TEXCOORD1D_6 = 24, + VERTEX_ELEMENT_TEXCOORD1D_7 = 25, + VERTEX_ELEMENT_TEXCOORD2D_0 = 26, + VERTEX_ELEMENT_TEXCOORD2D_1 = 27, + VERTEX_ELEMENT_TEXCOORD2D_2 = 28, + VERTEX_ELEMENT_TEXCOORD2D_3 = 29, + VERTEX_ELEMENT_TEXCOORD2D_4 = 30, + VERTEX_ELEMENT_TEXCOORD2D_5 = 31, + VERTEX_ELEMENT_TEXCOORD2D_6 = 32, + VERTEX_ELEMENT_TEXCOORD2D_7 = 33, + VERTEX_ELEMENT_TEXCOORD3D_0 = 34, + VERTEX_ELEMENT_TEXCOORD3D_1 = 35, + VERTEX_ELEMENT_TEXCOORD3D_2 = 36, + VERTEX_ELEMENT_TEXCOORD3D_3 = 37, + VERTEX_ELEMENT_TEXCOORD3D_4 = 38, + VERTEX_ELEMENT_TEXCOORD3D_5 = 39, + VERTEX_ELEMENT_TEXCOORD3D_6 = 40, + VERTEX_ELEMENT_TEXCOORD3D_7 = 41, + VERTEX_ELEMENT_TEXCOORD4D_0 = 42, + VERTEX_ELEMENT_TEXCOORD4D_1 = 43, + VERTEX_ELEMENT_TEXCOORD4D_2 = 44, + VERTEX_ELEMENT_TEXCOORD4D_3 = 45, + VERTEX_ELEMENT_TEXCOORD4D_4 = 46, + VERTEX_ELEMENT_TEXCOORD4D_5 = 47, + VERTEX_ELEMENT_TEXCOORD4D_6 = 48, + VERTEX_ELEMENT_TEXCOORD4D_7 = 49, + + VERTEX_ELEMENT_NUMELEMENTS = 50 +}; + +inline void Detect_VertexElement_t_Changes( VertexElement_t element ) // GREPs for VertexElement_t will hit this +{ + // Make it harder for someone to change VertexElement_t without noticing that dependent code + // (GetVertexElementSize, VertexElementToDeclType, CVBAllocTracker) needs updating + Assert( VERTEX_ELEMENT_NUMELEMENTS == 50 ); + switch ( element ) + { + case VERTEX_ELEMENT_POSITION: Assert( VERTEX_ELEMENT_POSITION == 0 ); break; + case VERTEX_ELEMENT_POSITION4D: Assert( VERTEX_ELEMENT_POSITION4D == 1 ); break; + case VERTEX_ELEMENT_NORMAL: Assert( VERTEX_ELEMENT_NORMAL == 2 ); break; + case VERTEX_ELEMENT_NORMAL4D: Assert( VERTEX_ELEMENT_NORMAL4D == 3 ); break; + case VERTEX_ELEMENT_COLOR: Assert( VERTEX_ELEMENT_COLOR == 4 ); break; + case VERTEX_ELEMENT_SPECULAR: Assert( VERTEX_ELEMENT_SPECULAR == 5 ); break; + case VERTEX_ELEMENT_TANGENT_S: Assert( VERTEX_ELEMENT_TANGENT_S == 6 ); break; + case VERTEX_ELEMENT_TANGENT_T: Assert( VERTEX_ELEMENT_TANGENT_T == 7 ); break; + case VERTEX_ELEMENT_WRINKLE: Assert( VERTEX_ELEMENT_WRINKLE == 8 ); break; + case VERTEX_ELEMENT_BONEINDEX: Assert( VERTEX_ELEMENT_BONEINDEX == 9 ); break; + case VERTEX_ELEMENT_BONEWEIGHTS1: Assert( VERTEX_ELEMENT_BONEWEIGHTS1 == 10 ); break; + case VERTEX_ELEMENT_BONEWEIGHTS2: Assert( VERTEX_ELEMENT_BONEWEIGHTS2 == 11 ); break; + case VERTEX_ELEMENT_BONEWEIGHTS3: Assert( VERTEX_ELEMENT_BONEWEIGHTS3 == 12 ); break; + case VERTEX_ELEMENT_BONEWEIGHTS4: Assert( VERTEX_ELEMENT_BONEWEIGHTS4 == 13 ); break; + case VERTEX_ELEMENT_USERDATA1: Assert( VERTEX_ELEMENT_USERDATA1 == 14 ); break; + case VERTEX_ELEMENT_USERDATA2: Assert( VERTEX_ELEMENT_USERDATA2 == 15 ); break; + case VERTEX_ELEMENT_USERDATA3: Assert( VERTEX_ELEMENT_USERDATA3 == 16 ); break; + case VERTEX_ELEMENT_USERDATA4: Assert( VERTEX_ELEMENT_USERDATA4 == 17 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_0: Assert( VERTEX_ELEMENT_TEXCOORD1D_0 == 18 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_1: Assert( VERTEX_ELEMENT_TEXCOORD1D_1 == 19 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_2: Assert( VERTEX_ELEMENT_TEXCOORD1D_2 == 20 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_3: Assert( VERTEX_ELEMENT_TEXCOORD1D_3 == 21 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_4: Assert( VERTEX_ELEMENT_TEXCOORD1D_4 == 22 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_5: Assert( VERTEX_ELEMENT_TEXCOORD1D_5 == 23 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_6: Assert( VERTEX_ELEMENT_TEXCOORD1D_6 == 24 ); break; + case VERTEX_ELEMENT_TEXCOORD1D_7: Assert( VERTEX_ELEMENT_TEXCOORD1D_7 == 25 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_0: Assert( VERTEX_ELEMENT_TEXCOORD2D_0 == 26 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_1: Assert( VERTEX_ELEMENT_TEXCOORD2D_1 == 27 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_2: Assert( VERTEX_ELEMENT_TEXCOORD2D_2 == 28 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_3: Assert( VERTEX_ELEMENT_TEXCOORD2D_3 == 29 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_4: Assert( VERTEX_ELEMENT_TEXCOORD2D_4 == 30 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_5: Assert( VERTEX_ELEMENT_TEXCOORD2D_5 == 31 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_6: Assert( VERTEX_ELEMENT_TEXCOORD2D_6 == 32 ); break; + case VERTEX_ELEMENT_TEXCOORD2D_7: Assert( VERTEX_ELEMENT_TEXCOORD2D_7 == 33 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_0: Assert( VERTEX_ELEMENT_TEXCOORD3D_0 == 34 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_1: Assert( VERTEX_ELEMENT_TEXCOORD3D_1 == 35 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_2: Assert( VERTEX_ELEMENT_TEXCOORD3D_2 == 36 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_3: Assert( VERTEX_ELEMENT_TEXCOORD3D_3 == 37 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_4: Assert( VERTEX_ELEMENT_TEXCOORD3D_4 == 38 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_5: Assert( VERTEX_ELEMENT_TEXCOORD3D_5 == 39 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_6: Assert( VERTEX_ELEMENT_TEXCOORD3D_6 == 40 ); break; + case VERTEX_ELEMENT_TEXCOORD3D_7: Assert( VERTEX_ELEMENT_TEXCOORD3D_7 == 41 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_0: Assert( VERTEX_ELEMENT_TEXCOORD4D_0 == 42 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_1: Assert( VERTEX_ELEMENT_TEXCOORD4D_1 == 43 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_2: Assert( VERTEX_ELEMENT_TEXCOORD4D_2 == 44 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_3: Assert( VERTEX_ELEMENT_TEXCOORD4D_3 == 45 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_4: Assert( VERTEX_ELEMENT_TEXCOORD4D_4 == 46 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_5: Assert( VERTEX_ELEMENT_TEXCOORD4D_5 == 47 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_6: Assert( VERTEX_ELEMENT_TEXCOORD4D_6 == 48 ); break; + case VERTEX_ELEMENT_TEXCOORD4D_7: Assert( VERTEX_ELEMENT_TEXCOORD4D_7 == 49 ); break; + default: + Assert( 0 ); // Invalid input or VertexElement_t has definitely changed + break; + } +} + +// We're testing 2 normal compression methods +// One compressed normals+tangents into a SHORT2 each (8 bytes total) +// The other compresses them together, into a single UBYTE4 (4 bytes total) +// FIXME: pick one or the other, compare lighting quality in important cases +#define COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 0 +#define COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 1 +//#define COMPRESSED_NORMALS_TYPE COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 +#define COMPRESSED_NORMALS_TYPE COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 + +inline int GetVertexElementSize( VertexElement_t element, VertexCompressionType_t compressionType ) +{ + Detect_VertexElement_t_Changes( element ); + + if ( compressionType == VERTEX_COMPRESSION_ON ) + { + // Compressed-vertex element sizes + switch ( element ) + { +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) + case VERTEX_ELEMENT_NORMAL: + return ( 2 * sizeof( short ) ); + case VERTEX_ELEMENT_USERDATA4: + return ( 2 * sizeof( short ) ); +#else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) + // Normals and tangents (userdata4) are combined into a single UBYTE4 vertex element + case VERTEX_ELEMENT_NORMAL: + return ( 4 * sizeof( unsigned char ) ); + case VERTEX_ELEMENT_USERDATA4: + return ( 0 ); +#endif + // Compressed bone weights use a SHORT2 vertex element: + case VERTEX_ELEMENT_BONEWEIGHTS1: + case VERTEX_ELEMENT_BONEWEIGHTS2: + return ( 2 * sizeof( short ) ); + default: + break; + } + } + + // Uncompressed-vertex element sizes + switch ( element ) + { + case VERTEX_ELEMENT_POSITION: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_POSITION4D: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_NORMAL: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_NORMAL4D: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_COLOR: return ( 4 * sizeof( unsigned char ) ); + case VERTEX_ELEMENT_SPECULAR: return ( 4 * sizeof( unsigned char ) ); + case VERTEX_ELEMENT_TANGENT_S: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TANGENT_T: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_WRINKLE: return ( 1 * sizeof( float ) ); // Packed into Position.W + case VERTEX_ELEMENT_BONEINDEX: return ( 4 * sizeof( unsigned char ) ); + case VERTEX_ELEMENT_BONEWEIGHTS1: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_BONEWEIGHTS2: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_BONEWEIGHTS3: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_BONEWEIGHTS4: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_USERDATA1: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_USERDATA2: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_USERDATA3: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_USERDATA4: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_0: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_1: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_2: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_3: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_4: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_5: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_6: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD1D_7: return ( 1 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_0: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_1: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_2: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_3: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_4: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_5: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_6: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD2D_7: return ( 2 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_0: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_1: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_2: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_3: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_4: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_5: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_6: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD3D_7: return ( 3 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_0: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_1: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_2: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_3: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_4: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_5: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_6: return ( 4 * sizeof( float ) ); + case VERTEX_ELEMENT_TEXCOORD4D_7: return ( 4 * sizeof( float ) ); + default: + Assert(0); + return 0; + }; +} + + +//----------------------------------------------------------------------------- +// Shader state flags can be read from the FLAGS materialvar +// Also can be read or written to with the Set/GetMaterialVarFlags() call +// Also make sure you add/remove a string associated with each flag below to CShaderSystem::ShaderStateString in ShaderSystem.cpp +//----------------------------------------------------------------------------- +enum MaterialVarFlags_t +{ + MATERIAL_VAR_DEBUG = (1 << 0), + MATERIAL_VAR_NO_DEBUG_OVERRIDE = (1 << 1), + MATERIAL_VAR_NO_DRAW = (1 << 2), + MATERIAL_VAR_USE_IN_FILLRATE_MODE = (1 << 3), + + MATERIAL_VAR_VERTEXCOLOR = (1 << 4), + MATERIAL_VAR_VERTEXALPHA = (1 << 5), + MATERIAL_VAR_SELFILLUM = (1 << 6), + MATERIAL_VAR_ADDITIVE = (1 << 7), + MATERIAL_VAR_ALPHATEST = (1 << 8), +// MATERIAL_VAR_UNUSED = (1 << 9), + MATERIAL_VAR_ZNEARER = (1 << 10), + MATERIAL_VAR_MODEL = (1 << 11), + MATERIAL_VAR_FLAT = (1 << 12), + MATERIAL_VAR_NOCULL = (1 << 13), + MATERIAL_VAR_NOFOG = (1 << 14), + MATERIAL_VAR_IGNOREZ = (1 << 15), + MATERIAL_VAR_DECAL = (1 << 16), + MATERIAL_VAR_ENVMAPSPHERE = (1 << 17), // OBSOLETE +// MATERIAL_VAR_UNUSED = (1 << 18), + MATERIAL_VAR_ENVMAPCAMERASPACE = (1 << 19), // OBSOLETE + MATERIAL_VAR_BASEALPHAENVMAPMASK = (1 << 20), + MATERIAL_VAR_TRANSLUCENT = (1 << 21), + MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK = (1 << 22), + MATERIAL_VAR_NEEDS_SOFTWARE_SKINNING = (1 << 23), // OBSOLETE + MATERIAL_VAR_OPAQUETEXTURE = (1 << 24), + MATERIAL_VAR_ENVMAPMODE = (1 << 25), // OBSOLETE + MATERIAL_VAR_SUPPRESS_DECALS = (1 << 26), + MATERIAL_VAR_HALFLAMBERT = (1 << 27), + MATERIAL_VAR_WIREFRAME = (1 << 28), + MATERIAL_VAR_ALLOWALPHATOCOVERAGE = (1 << 29), + MATERIAL_VAR_ALPHA_MODIFIED_BY_PROXY = (1 << 30), + MATERIAL_VAR_VERTEXFOG = (1 << 31), + + // NOTE: Only add flags here that either should be read from + // .vmts or can be set directly from client code. Other, internal + // flags should to into the flag enum in IMaterialInternal.h +}; + + +//----------------------------------------------------------------------------- +// Internal flags not accessible from outside the material system. Stored in Flags2 +//----------------------------------------------------------------------------- +enum MaterialVarFlags2_t +{ + // NOTE: These are for $flags2!!!!! +// UNUSED = (1 << 0), + + MATERIAL_VAR2_LIGHTING_UNLIT = 0, + MATERIAL_VAR2_LIGHTING_VERTEX_LIT = (1 << 1), + MATERIAL_VAR2_LIGHTING_LIGHTMAP = (1 << 2), + MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP = (1 << 3), + MATERIAL_VAR2_LIGHTING_MASK = + ( MATERIAL_VAR2_LIGHTING_VERTEX_LIT | + MATERIAL_VAR2_LIGHTING_LIGHTMAP | + MATERIAL_VAR2_LIGHTING_BUMPED_LIGHTMAP ), + + // FIXME: Should this be a part of the above lighting enums? + MATERIAL_VAR2_DIFFUSE_BUMPMAPPED_MODEL = (1 << 4), + MATERIAL_VAR2_USES_ENV_CUBEMAP = (1 << 5), + MATERIAL_VAR2_NEEDS_TANGENT_SPACES = (1 << 6), + MATERIAL_VAR2_NEEDS_SOFTWARE_LIGHTING = (1 << 7), + // GR - HDR path puts lightmap alpha in separate texture... + MATERIAL_VAR2_BLEND_WITH_LIGHTMAP_ALPHA = (1 << 8), + MATERIAL_VAR2_NEEDS_BAKED_LIGHTING_SNAPSHOTS = (1 << 9), + MATERIAL_VAR2_USE_FLASHLIGHT = (1 << 10), + MATERIAL_VAR2_USE_FIXED_FUNCTION_BAKED_LIGHTING = (1 << 11), + MATERIAL_VAR2_NEEDS_FIXED_FUNCTION_FLASHLIGHT = (1 << 12), + MATERIAL_VAR2_USE_EDITOR = (1 << 13), + MATERIAL_VAR2_NEEDS_POWER_OF_TWO_FRAME_BUFFER_TEXTURE = (1 << 14), + MATERIAL_VAR2_NEEDS_FULL_FRAME_BUFFER_TEXTURE = (1 << 15), + MATERIAL_VAR2_IS_SPRITECARD = (1 << 16), + MATERIAL_VAR2_USES_VERTEXID = (1 << 17), + MATERIAL_VAR2_SUPPORTS_HW_SKINNING = (1 << 18), + MATERIAL_VAR2_SUPPORTS_FLASHLIGHT = (1 << 19), + MATERIAL_VAR2_USE_GBUFFER0 = (1 << 20), + MATERIAL_VAR2_USE_GBUFFER1 = (1 << 21), + MATERIAL_VAR2_SELFILLUMMASK = (1 << 22), + MATERIAL_VAR2_SUPPORTS_TESSELLATION = (1 << 23) +}; + + +//----------------------------------------------------------------------------- +// Preview image return values +//----------------------------------------------------------------------------- +enum PreviewImageRetVal_t +{ + MATERIAL_PREVIEW_IMAGE_BAD = 0, + MATERIAL_PREVIEW_IMAGE_OK, + MATERIAL_NO_PREVIEW_IMAGE, +}; + + +//----------------------------------------------------------------------------- +// material interface +//----------------------------------------------------------------------------- +abstract_class IMaterial +{ +public: + // Get the name of the material. This is a full path to + // the vmt file starting from "hl2/materials" (or equivalent) without + // a file extension. + virtual const char * GetName() const = 0; + virtual const char * GetTextureGroupName() const = 0; + + // Get the preferred size/bitDepth of a preview image of a material. + // This is the sort of image that you would use for a thumbnail view + // of a material, or in WorldCraft until it uses materials to render. + // separate this for the tools maybe + virtual PreviewImageRetVal_t GetPreviewImageProperties( int *width, int *height, + ImageFormat *imageFormat, bool* isTranslucent ) const = 0; + + // Get a preview image at the specified width/height and bitDepth. + // Will do resampling if necessary.(not yet!!! :) ) + // Will do color format conversion. (works now.) + virtual PreviewImageRetVal_t GetPreviewImage( unsigned char *data, + int width, int height, + ImageFormat imageFormat ) const = 0; + // + virtual int GetMappingWidth( ) = 0; + virtual int GetMappingHeight( ) = 0; + + virtual int GetNumAnimationFrames( ) = 0; + + // For material subrects (material pages). Offset(u,v) and scale(u,v) are normalized to texture. + virtual bool InMaterialPage( void ) = 0; + virtual void GetMaterialOffset( float *pOffset ) = 0; + virtual void GetMaterialScale( float *pScale ) = 0; + virtual IMaterial *GetMaterialPage( void ) = 0; + + // find a vmt variable. + // This is how game code affects how a material is rendered. + // The game code must know about the params that are used by + // the shader for the material that it is trying to affect. + virtual IMaterialVar * FindVar( const char *varName, bool *found, bool complain = true ) = 0; + + // The user never allocates or deallocates materials. Reference counting is + // used instead. Garbage collection is done upon a call to + // IMaterialSystem::UncacheUnusedMaterials. + virtual void IncrementReferenceCount( void ) = 0; + virtual void DecrementReferenceCount( void ) = 0; + + inline void AddRef() { IncrementReferenceCount(); } + inline void Release() { DecrementReferenceCount(); } + + // Each material is assigned a number that groups it with like materials + // for sorting in the application. + virtual int GetEnumerationID( void ) const = 0; + + virtual void GetLowResColorSample( float s, float t, float *color ) const = 0; + + // This computes the state snapshots for this material + virtual void RecomputeStateSnapshots() = 0; + + // Are we translucent? + virtual bool IsTranslucent() = 0; + + // Are we alphatested? + virtual bool IsAlphaTested() = 0; + + // Are we vertex lit? + virtual bool IsVertexLit() = 0; + + // Gets the vertex format + virtual VertexFormat_t GetVertexFormat() const = 0; + + // returns true if this material uses a material proxy + virtual bool HasProxy( void ) const = 0; + + virtual bool UsesEnvCubemap( void ) = 0; + + virtual bool NeedsTangentSpace( void ) = 0; + + virtual bool NeedsPowerOfTwoFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) = 0; + virtual bool NeedsFullFrameBufferTexture( bool bCheckSpecificToThisFrame = true ) = 0; + + // returns true if the shader doesn't do skinning itself and requires + // the data that is sent to it to be preskinned. + virtual bool NeedsSoftwareSkinning( void ) = 0; + + // Apply constant color or alpha modulation + virtual void AlphaModulate( float alpha ) = 0; + virtual void ColorModulate( float r, float g, float b ) = 0; + + // Material Var flags... + virtual void SetMaterialVarFlag( MaterialVarFlags_t flag, bool on ) = 0; + virtual bool GetMaterialVarFlag( MaterialVarFlags_t flag ) const = 0; + + // Gets material reflectivity + virtual void GetReflectivity( Vector& reflect ) = 0; + + // Gets material property flags + virtual bool GetPropertyFlag( MaterialPropertyTypes_t type ) = 0; + + // Is the material visible from both sides? + virtual bool IsTwoSided() = 0; + + // Sets the shader associated with the material + virtual void SetShader( const char *pShaderName ) = 0; + + // Can't be const because the material might have to precache itself. + virtual int GetNumPasses( void ) = 0; + + // Can't be const because the material might have to precache itself. + virtual int GetTextureMemoryBytes( void ) = 0; + + // Meant to be used with materials created using CreateMaterial + // It updates the materials to reflect the current values stored in the material vars + virtual void Refresh() = 0; + + // GR - returns true is material uses lightmap alpha for blending + virtual bool NeedsLightmapBlendAlpha( void ) = 0; + + // returns true if the shader doesn't do lighting itself and requires + // the data that is sent to it to be prelighted + virtual bool NeedsSoftwareLighting( void ) = 0; + + // Gets at the shader parameters + virtual int ShaderParamCount() const = 0; + virtual IMaterialVar **GetShaderParams( void ) = 0; + + // Returns true if this is the error material you get back from IMaterialSystem::FindMaterial if + // the material can't be found. + virtual bool IsErrorMaterial() const = 0; + + virtual void SetUseFixedFunctionBakedLighting( bool bEnable ) = 0; + + // Gets the current alpha modulation + virtual float GetAlphaModulation() = 0; + virtual void GetColorModulation( float *r, float *g, float *b ) = 0; + + // Is this translucent given a particular alpha modulation? + virtual bool IsTranslucentUnderModulation( float fAlphaModulation = 1.0f ) const = 0; + + // fast find that stores the index of the found var in the string table in local cache + virtual IMaterialVar * FindVarFast( char const *pVarName, unsigned int *pToken ) = 0; + + // Sets new VMT shader parameters for the material + virtual void SetShaderAndParams( KeyValues *pKeyValues ) = 0; + virtual const char * GetShaderName() const = 0; + + virtual void DeleteIfUnreferenced() = 0; + + virtual bool IsSpriteCard() = 0; + + virtual void CallBindProxy( void *proxyData ) = 0; + + virtual void RefreshPreservingMaterialVars() = 0; + + virtual bool WasReloadedFromWhitelist() = 0; +}; + + +inline bool IsErrorMaterial( IMaterial *pMat ) +{ + return !pMat || pMat->IsErrorMaterial(); +} + + +// +// Vertex stream specifications +// + +struct VertexStreamSpec_t +{ + enum StreamSpec_t + { + STREAM_DEFAULT, // stream 0: with position, normal, etc. + STREAM_SPECULAR1, // stream 1: following specular vhv lighting + STREAM_FLEXDELTA, // stream 2: flex deltas + STREAM_MORPH, // stream 3: morph + STREAM_UNIQUE_A, // unique stream 4 + STREAM_UNIQUE_B, // unique stream 5 + STREAM_UNIQUE_C, // unique stream 6 + STREAM_UNIQUE_D, // unique stream 7 + STREAM_SUBDQUADS, // stream 8: quad buffer for subd's + }; + + enum + { + MAX_UNIQUE_STREAMS = 4 + }; + + VertexFormatFlags_t iVertexDataElement; + StreamSpec_t iStreamSpec; +}; + +inline VertexStreamSpec_t * FindVertexStreamSpec( VertexFormat_t iElement, VertexStreamSpec_t *arrVertexStreamSpec ) +{ + for ( ; arrVertexStreamSpec && + arrVertexStreamSpec->iVertexDataElement != VERTEX_FORMAT_UNKNOWN ; + ++ arrVertexStreamSpec ) + { + if ( arrVertexStreamSpec->iVertexDataElement == iElement ) + return arrVertexStreamSpec; + } + return NULL; +} + + +#endif // IMATERIAL_H diff --git a/public/materialsystem/imaterialproxy.h b/public/materialsystem/imaterialproxy.h new file mode 100644 index 0000000..5b9e954 --- /dev/null +++ b/public/materialsystem/imaterialproxy.h @@ -0,0 +1,32 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMATERIALPROXY_H +#define IMATERIALPROXY_H +#pragma once + +#include "interface.h" + +#define IMATERIAL_PROXY_INTERFACE_VERSION "_IMaterialProxy003" + +class IMaterial; +class KeyValues; + +abstract_class IMaterialProxy +{ +public: + virtual bool Init( IMaterial* pMaterial, KeyValues *pKeyValues ) = 0; + virtual void OnBind( void * ) = 0; + virtual void Release() = 0; + virtual IMaterial * GetMaterial() = 0; + +protected: + // no one should call this directly + virtual ~IMaterialProxy() {} +}; + +#endif // IMATERIALPROXY_H diff --git a/public/materialsystem/imaterialproxyfactory.h b/public/materialsystem/imaterialproxyfactory.h new file mode 100644 index 0000000..25486e5 --- /dev/null +++ b/public/materialsystem/imaterialproxyfactory.h @@ -0,0 +1,26 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMATERIALPROXYFACTORY_H +#define IMATERIALPROXYFACTORY_H +#pragma once + +#include "interface.h" + +#define IMATERIAL_PROXY_FACTOR_INTERFACE_VERSION "IMaterialProxyFactory001" + +class IMaterialProxy; + +abstract_class IMaterialProxyFactory +{ +public: + virtual IMaterialProxy *CreateProxy( const char *proxyName ) = 0; + virtual void DeleteProxy( IMaterialProxy *pProxy ) = 0; + virtual CreateInterfaceFn GetFactory() = 0; +}; + +#endif // IMATERIALPROXYFACTORY_H diff --git a/public/materialsystem/imaterialsystem.h b/public/materialsystem/imaterialsystem.h new file mode 100644 index 0000000..9aaf29f --- /dev/null +++ b/public/materialsystem/imaterialsystem.h @@ -0,0 +1,1792 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef IMATERIALSYSTEM_H +#define IMATERIALSYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#define OVERBRIGHT 2.0f +#define OO_OVERBRIGHT ( 1.0f / 2.0f ) +#define GAMMA 2.2f +#define TEXGAMMA 2.2f + +#include "tier1/interface.h" +#include "tier1/refcount.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "mathlib/vmatrix.h" +#include "appframework/IAppSystem.h" +#include "bitmap/imageformat.h" +#include "texture_group_names.h" +#include "vtf/vtf.h" +#include "materialsystem/deformations.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/IColorCorrection.h" + +#if !defined( _X360 ) +// NOTE: Disable this for l4d2 in general!!! It allocates 4mb of rendertargets and causes Release/Reallocation of rendertargets. +//#define FEATURE_SUBD_SUPPORT +#endif + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class IMesh; +class IVertexBuffer; +class IIndexBuffer; +struct MaterialSystem_Config_t; +class VMatrix; +struct matrix3x4_t; +class ITexture; +struct MaterialSystemHardwareIdentifier_t; +class KeyValues; +class IShader; +class IVertexTexture; +class IMorph; +class IMatRenderContext; +class ICallQueue; +struct MorphWeight_t; +class IFileList; +struct VertexStreamSpec_t; +struct ShaderStencilState_t; +struct MeshInstanceData_t; +class IClientMaterialSystem; +class CPaintMaterial; +class IPaintMapDataManager; +class IPaintMapTextureManager; + + +//----------------------------------------------------------------------------- +// The vertex format type +//----------------------------------------------------------------------------- +typedef uint64 VertexFormat_t; + +//----------------------------------------------------------------------------- +// important enumeration +//----------------------------------------------------------------------------- +enum ShaderParamType_t +{ + SHADER_PARAM_TYPE_TEXTURE, + SHADER_PARAM_TYPE_INTEGER, + SHADER_PARAM_TYPE_COLOR, + SHADER_PARAM_TYPE_VEC2, + SHADER_PARAM_TYPE_VEC3, + SHADER_PARAM_TYPE_VEC4, + SHADER_PARAM_TYPE_ENVMAP, // obsolete + SHADER_PARAM_TYPE_FLOAT, + SHADER_PARAM_TYPE_BOOL, + SHADER_PARAM_TYPE_FOURCC, + SHADER_PARAM_TYPE_MATRIX, + SHADER_PARAM_TYPE_MATERIAL, + SHADER_PARAM_TYPE_STRING, +}; + +enum MaterialMatrixMode_t +{ + MATERIAL_VIEW = 0, + MATERIAL_PROJECTION, + + MATERIAL_MATRIX_UNUSED0, + MATERIAL_MATRIX_UNUSED1, + MATERIAL_MATRIX_UNUSED2, + MATERIAL_MATRIX_UNUSED3, + MATERIAL_MATRIX_UNUSED4, + MATERIAL_MATRIX_UNUSED5, + MATERIAL_MATRIX_UNUSED6, + MATERIAL_MATRIX_UNUSED7, + + MATERIAL_MODEL, + + // Total number of matrices + NUM_MATRIX_MODES = MATERIAL_MODEL+1, +}; + +// FIXME: How do I specify the actual number of matrix modes? +const int NUM_MODEL_TRANSFORMS = 53; +const int MATERIAL_MODEL_MAX = MATERIAL_MODEL + NUM_MODEL_TRANSFORMS; + +enum MaterialPrimitiveType_t +{ + MATERIAL_POINTS = 0x0, + MATERIAL_LINES, + MATERIAL_TRIANGLES, + MATERIAL_TRIANGLE_STRIP, + MATERIAL_LINE_STRIP, + MATERIAL_LINE_LOOP, // a single line loop + MATERIAL_POLYGON, // this is a *single* polygon + MATERIAL_QUADS, + MATERIAL_SUBD_QUADS_EXTRA, // Extraordinary sub-d quads + MATERIAL_SUBD_QUADS_REG, // Regular sub-d quads + MATERIAL_INSTANCED_QUADS, // (X360) like MATERIAL_QUADS, but uses vertex instancing + + // This is used for static meshes that contain multiple types of + // primitive types. When calling draw, you'll need to specify + // a primitive type. + MATERIAL_HETEROGENOUS +}; + +enum TessellationMode_t +{ + TESSELLATION_MODE_DISABLED = 0, + TESSELLATION_MODE_ACC_PATCHES_EXTRA, + TESSELLATION_MODE_ACC_PATCHES_REG +}; + +enum MaterialPropertyTypes_t +{ + MATERIAL_PROPERTY_NEEDS_LIGHTMAP = 0, // bool + MATERIAL_PROPERTY_OPACITY, // int (enum MaterialPropertyOpacityTypes_t) + MATERIAL_PROPERTY_REFLECTIVITY, // vec3_t + MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS // bool +}; + +// acceptable property values for MATERIAL_PROPERTY_OPACITY +enum MaterialPropertyOpacityTypes_t +{ + MATERIAL_ALPHATEST = 0, + MATERIAL_OPAQUE, + MATERIAL_TRANSLUCENT +}; + +enum MaterialBufferTypes_t +{ + MATERIAL_FRONT = 0, + MATERIAL_BACK +}; + +enum MaterialCullMode_t +{ + MATERIAL_CULLMODE_CCW, // this culls polygons with counterclockwise winding + MATERIAL_CULLMODE_CW // this culls polygons with clockwise winding +}; + +enum MaterialIndexFormat_t +{ + MATERIAL_INDEX_FORMAT_UNKNOWN = -1, + MATERIAL_INDEX_FORMAT_16BIT = 0, + MATERIAL_INDEX_FORMAT_32BIT, +}; + +enum MaterialFogMode_t +{ + MATERIAL_FOG_NONE, + MATERIAL_FOG_LINEAR, + MATERIAL_FOG_LINEAR_BELOW_FOG_Z, +}; + +enum MaterialHeightClipMode_t +{ + MATERIAL_HEIGHTCLIPMODE_DISABLE, + MATERIAL_HEIGHTCLIPMODE_RENDER_ABOVE_HEIGHT, + MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT +}; + +enum MaterialNonInteractiveMode_t +{ + MATERIAL_NON_INTERACTIVE_MODE_NONE = -1, + MATERIAL_NON_INTERACTIVE_MODE_STARTUP = 0, + MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD, + + MATERIAL_NON_INTERACTIVE_MODE_COUNT, +}; + + +//----------------------------------------------------------------------------- +// Special morph used in decalling pass +//----------------------------------------------------------------------------- +#define MATERIAL_MORPH_DECAL ( (IMorph*)1 ) + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum MaterialThreadMode_t +{ + MATERIAL_SINGLE_THREADED, + MATERIAL_QUEUED_SINGLE_THREADED, + MATERIAL_QUEUED_THREADED +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum MaterialContextType_t +{ + MATERIAL_HARDWARE_CONTEXT, + MATERIAL_QUEUED_CONTEXT, + MATERIAL_NULL_CONTEXT +}; + + +//----------------------------------------------------------------------------- +// Light structure +//----------------------------------------------------------------------------- +#include "mathlib/lightdesc.h" + +enum +{ + MATERIAL_MAX_LIGHT_COUNT = 4, +}; + +struct MaterialLightingState_t +{ + Vector m_vecAmbientCube[6]; // ambient, and lights that aren't in locallight[] + Vector m_vecLightingOrigin; // The position from which lighting state was computed + int m_nLocalLightCount; + LightDesc_t m_pLocalLightDesc[MATERIAL_MAX_LIGHT_COUNT]; + + MaterialLightingState_t &operator=( const MaterialLightingState_t &src ) + { + memcpy( this, &src, sizeof(MaterialLightingState_t) - MATERIAL_MAX_LIGHT_COUNT * sizeof(LightDesc_t) ); + memcpy( m_pLocalLightDesc, &src.m_pLocalLightDesc, src.m_nLocalLightCount * sizeof(LightDesc_t) ); + return *this; + } +}; + + + +#define CREATERENDERTARGETFLAGS_HDR 0x00000001 +#define CREATERENDERTARGETFLAGS_AUTOMIPMAP 0x00000002 +#define CREATERENDERTARGETFLAGS_UNFILTERABLE_OK 0x00000004 +// XBOX ONLY: +#define CREATERENDERTARGETFLAGS_NOEDRAM 0x00000008 // inhibit allocation in 360 EDRAM +#define CREATERENDERTARGETFLAGS_TEMP 0x00000010 // only allocates memory upon first resolve, destroyed at level end + + +//----------------------------------------------------------------------------- +// Enumeration for the various fields capable of being morphed +//----------------------------------------------------------------------------- +enum MorphFormatFlags_t +{ + MORPH_POSITION = 0x0001, // 3D + MORPH_NORMAL = 0x0002, // 3D + MORPH_WRINKLE = 0x0004, // 1D + MORPH_SPEED = 0x0008, // 1D + MORPH_SIDE = 0x0010, // 1D +}; + + +//----------------------------------------------------------------------------- +// The morph format type +//----------------------------------------------------------------------------- +typedef unsigned int MorphFormat_t; + + +//----------------------------------------------------------------------------- +// Standard lightmaps +//----------------------------------------------------------------------------- +enum StandardLightmap_t +{ + MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE = -1, + MATERIAL_SYSTEM_LIGHTMAP_PAGE_WHITE_BUMP = -2, + MATERIAL_SYSTEM_LIGHTMAP_PAGE_USER_DEFINED = -3 +}; + + +struct MaterialSystem_SortInfo_t +{ + IMaterial *material; + int lightmapPageID; +}; + + +#define MAX_FB_TEXTURES 4 + +//----------------------------------------------------------------------------- +// Information about each adapter +//----------------------------------------------------------------------------- +enum +{ + MATERIAL_ADAPTER_NAME_LENGTH = 512 +}; + +struct MaterialAdapterInfo_t +{ + char m_pDriverName[MATERIAL_ADAPTER_NAME_LENGTH]; + unsigned int m_VendorID; + unsigned int m_DeviceID; + unsigned int m_SubSysID; + unsigned int m_Revision; + int m_nDXSupportLevel; // This is the *preferred* dx support level + int m_nMinDXSupportLevel; + int m_nMaxDXSupportLevel; + unsigned int m_nDriverVersionHigh; + unsigned int m_nDriverVersionLow; +}; + + +//----------------------------------------------------------------------------- +// Video mode info.. +//----------------------------------------------------------------------------- +struct MaterialVideoMode_t +{ + int m_Width; // if width and height are 0 and you select + int m_Height; // windowed mode, it'll use the window size + ImageFormat m_Format; // use ImageFormats (ignored for windowed mode) + int m_RefreshRate; // 0 == default (ignored for windowed mode) +}; + + +//-------------------------------------------------------------------------------- +// Uberlight parameters +//-------------------------------------------------------------------------------- +struct UberlightState_t +{ + UberlightState_t() + { + m_fNearEdge = 2.0f; + m_fFarEdge = 100.0f; + m_fCutOn = 10.0f; + m_fCutOff = 650.0f; + m_fShearx = 0.0f; + m_fSheary = 0.0f; + m_fWidth = 0.3f; + m_fWedge = 0.05f; + m_fHeight = 0.3f; + m_fHedge = 0.05f; + m_fRoundness = 0.8f; + } + + float m_fNearEdge; + float m_fFarEdge; + float m_fCutOn; + float m_fCutOff; + float m_fShearx; + float m_fSheary; + float m_fWidth; + float m_fWedge; + float m_fHeight; + float m_fHedge; + float m_fRoundness; + + IMPLEMENT_OPERATOR_EQUAL( UberlightState_t ); +}; + +// fixme: should move this into something else. +struct FlashlightState_t +{ + FlashlightState_t() + { + m_bEnableShadows = false; // Provide reasonable defaults for shadow depth mapping parameters + m_bDrawShadowFrustum = false; + m_flShadowMapResolution = 1024.0f; + m_flShadowFilterSize = 3.0f; + m_flShadowSlopeScaleDepthBias = 16.0f; + m_flShadowDepthBias = 0.0005f; + m_flShadowJitterSeed = 0.0f; + m_flShadowAtten = 0.0f; + m_flAmbientOcclusion = 0.0f; + m_nShadowQuality = 0; + m_bShadowHighRes = false; + + m_bScissor = false; + m_nLeft = -1; + m_nTop = -1; + m_nRight = -1; + m_nBottom = -1; + + m_bUberlight = false; + + m_bVolumetric = false; + m_flNoiseStrength = 0.8f; + m_flFlashlightTime = 0.0f; + m_nNumPlanes = 64; + m_flPlaneOffset = 0.0f; + m_flVolumetricIntensity = 1.0f; + + m_bOrtho = false; + m_fOrthoLeft = -1.0f; + m_fOrthoRight = 1.0f; + m_fOrthoTop = -1.0f; + m_fOrthoBottom = 1.0f; + + m_fBrightnessScale = 1.0f; + m_pSpotlightTexture = NULL; + m_pProjectedMaterial = NULL; + m_bGlobalLight = false; + + m_bSimpleProjection = false; + m_flProjectionSize = 500.0f; + m_flProjectionRotation = 0.0f; + } + + Vector m_vecLightOrigin; + Quaternion m_quatOrientation; + float m_NearZ; + float m_FarZ; + float m_fHorizontalFOVDegrees; + float m_fVerticalFOVDegrees; + + bool m_bOrtho; + float m_fOrthoLeft; + float m_fOrthoRight; + float m_fOrthoTop; + float m_fOrthoBottom; + + float m_fQuadraticAtten; + float m_fLinearAtten; + float m_fConstantAtten; + float m_FarZAtten; + float m_Color[4]; + float m_fBrightnessScale; + ITexture *m_pSpotlightTexture; + IMaterial *m_pProjectedMaterial; + int m_nSpotlightTextureFrame; + bool m_bGlobalLight; + + // Shadow depth mapping parameters + bool m_bEnableShadows; + bool m_bDrawShadowFrustum; + float m_flShadowMapResolution; + float m_flShadowFilterSize; + float m_flShadowSlopeScaleDepthBias; + float m_flShadowDepthBias; + float m_flShadowJitterSeed; + float m_flShadowAtten; + float m_flAmbientOcclusion; + int m_nShadowQuality; + bool m_bShadowHighRes; + + // simple projection + bool m_bSimpleProjection; + float m_flProjectionSize; + float m_flProjectionRotation; + + // Uberlight parameters + bool m_bUberlight; + UberlightState_t m_uberlightState; + + bool m_bVolumetric; + float m_flNoiseStrength; + float m_flFlashlightTime; + int m_nNumPlanes; + float m_flPlaneOffset; + float m_flVolumetricIntensity; + + // Getters for scissor members + bool DoScissor() const { return m_bScissor; } + int GetLeft() const { return m_nLeft; } + int GetTop() const { return m_nTop; } + int GetRight() const { return m_nRight; } + int GetBottom() const { return m_nBottom; } + +private: + + friend class CShadowMgr; + + bool m_bScissor; + int m_nLeft; + int m_nTop; + int m_nRight; + int m_nBottom; + + IMPLEMENT_OPERATOR_EQUAL( FlashlightState_t ); +}; + +//----------------------------------------------------------------------------- +// Flags to be used with the Init call +//----------------------------------------------------------------------------- +enum MaterialInitFlags_t +{ + MATERIAL_INIT_ALLOCATE_FULLSCREEN_TEXTURE = 0x2, + MATERIAL_INIT_REFERENCE_RASTERIZER = 0x4, +}; + +//----------------------------------------------------------------------------- +// Flags to specify type of depth buffer used with RT +//----------------------------------------------------------------------------- + +// GR - this is to add RT with no depth buffer bound + +enum MaterialRenderTargetDepth_t +{ + MATERIAL_RT_DEPTH_SHARED = 0x0, + MATERIAL_RT_DEPTH_SEPARATE = 0x1, + MATERIAL_RT_DEPTH_NONE = 0x2, + MATERIAL_RT_DEPTH_ONLY = 0x3, +}; + +//----------------------------------------------------------------------------- +// A function to be called when we need to release all vertex buffers +// NOTE: The restore function will tell the caller if all the vertex formats +// changed so that it can flush caches, etc. if it needs to (for dxlevel support) +//----------------------------------------------------------------------------- +enum RestoreChangeFlags_t +{ + MATERIAL_RESTORE_VERTEX_FORMAT_CHANGED = 0x1, + MATERIAL_RESTORE_RELEASE_MANAGED_RESOURCES = 0x2, +}; + + +// NOTE: All size modes will force the render target to be smaller than or equal to +// the size of the framebuffer. +enum RenderTargetSizeMode_t +{ + RT_SIZE_NO_CHANGE=0, // Only allowed for render targets that don't want a depth buffer + // (because if they have a depth buffer, the render target must be less than or equal to the size of the framebuffer). + RT_SIZE_DEFAULT=1, // Don't play with the specified width and height other than making sure it fits in the framebuffer. + RT_SIZE_PICMIP=2, // Apply picmip to the render target's width and height. + RT_SIZE_HDR=3, // frame_buffer_width / 4 + RT_SIZE_FULL_FRAME_BUFFER=4, // Same size as frame buffer, or next lower power of 2 if we can't do that. + RT_SIZE_OFFSCREEN=5, // Target of specified size, don't mess with dimensions + RT_SIZE_FULL_FRAME_BUFFER_ROUNDED_UP=6 // Same size as the frame buffer, rounded up if necessary for systems that can't do non-power of two textures. +}; + +typedef void (*MaterialBufferReleaseFunc_t)( int nChangeFlags ); // see RestoreChangeFlags_t +typedef void (*MaterialBufferRestoreFunc_t)( int nChangeFlags ); // see RestoreChangeFlags_t +typedef void (*ModeChangeCallbackFunc_t)( void ); +typedef void (*EndFrameCleanupFunc_t)( void ); + +//typedef int VertexBufferHandle_t; +typedef unsigned short MaterialHandle_t; + +DECLARE_POINTER_HANDLE( OcclusionQueryObjectHandle_t ); +#define INVALID_OCCLUSION_QUERY_OBJECT_HANDLE ( (OcclusionQueryObjectHandle_t)0 ) + +class IMaterialProxyFactory; +class ITexture; +class IMaterialSystemHardwareConfig; +class CShadowMgr; + +DECLARE_POINTER_HANDLE( MaterialLock_t ); + +//----------------------------------------------------------------------------- +// Information about a material texture +//----------------------------------------------------------------------------- + +struct MaterialTextureInfo_t +{ + // Exclude information: + // -1 texture is not subject to exclude-handling + // 0 texture is completely excluded + // >0 texture is clamped according to exclude-instruction + int iExcludeInformation; +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +abstract_class IMaterialSystem : public IAppSystem +{ +public: + + // Placeholder for API revision + virtual bool Connect( CreateInterfaceFn factory ) = 0; + virtual void Disconnect() = 0; + virtual void *QueryInterface( const char *pInterfaceName ) = 0; + virtual InitReturnVal_t Init() = 0; + virtual void Shutdown() = 0; + + //--------------------------------------------------------- + // Initialization and shutdown + //--------------------------------------------------------- + + // Call this to initialize the material system + // returns a method to create interfaces in the shader dll + virtual CreateInterfaceFn Init( char const* pShaderAPIDLL, + IMaterialProxyFactory *pMaterialProxyFactory, + CreateInterfaceFn fileSystemFactory, + CreateInterfaceFn cvarFactory=NULL ) = 0; + + // Call this to set an explicit shader version to use + // Must be called before Init(). + virtual void SetShaderAPI( char const *pShaderAPIDLL ) = 0; + + // Must be called before Init(), if you're going to call it at all... + virtual void SetAdapter( int nAdapter, int nFlags ) = 0; + + // Call this when the mod has been set up, which may occur after init + // At this point, the game + gamebin paths have been set up + virtual void ModInit() = 0; + virtual void ModShutdown() = 0; + + //--------------------------------------------------------- + // + //--------------------------------------------------------- + virtual void SetThreadMode( MaterialThreadMode_t mode, int nServiceThread = -1 ) = 0; + virtual MaterialThreadMode_t GetThreadMode() = 0; + virtual void ExecuteQueued() = 0; + + //--------------------------------------------------------- + // Config management + //--------------------------------------------------------- + + virtual IMaterialSystemHardwareConfig *GetHardwareConfig( const char *pVersion, int *returnCode ) = 0; + + + // Call this before rendering each frame with the current config + // for the material system. + // Will do whatever is necessary to get the material system into the correct state + // upon configuration change. .doesn't much else otherwise. + virtual bool UpdateConfig( bool bForceUpdate ) = 0; + + // Force this to be the config; update all material system convars to match the state + // return true if lightmaps need to be redownloaded + virtual bool OverrideConfig( const MaterialSystem_Config_t &config, bool bForceUpdate ) = 0; + + // Get the current config for this video card (as last set by UpdateConfig) + virtual const MaterialSystem_Config_t &GetCurrentConfigForVideoCard() const = 0; + + // Gets *recommended* configuration information associated with the display card, + // given a particular dx level to run under. + // Use dxlevel 0 to use the recommended dx level. + // The function returns false if an invalid dxlevel was specified + + // UNDONE: To find out all convars affected by configuration, we'll need to change + // the dxsupport.pl program to output all column headers into a single keyvalue block + // and then we would read that in, and send it back to the client + virtual bool GetRecommendedConfigurationInfo( int nDXLevel, KeyValues * pKeyValues ) = 0; + + + // ----------------------------------------------------------- + // Device methods + // ----------------------------------------------------------- + + // Gets the number of adapters... + virtual int GetDisplayAdapterCount() const = 0; + + // Returns the current adapter in use + virtual int GetCurrentAdapter() const = 0; + + // Returns info about each adapter + virtual void GetDisplayAdapterInfo( int adapter, MaterialAdapterInfo_t& info ) const = 0; + + // Returns the number of modes + virtual int GetModeCount( int adapter ) const = 0; + + // Returns mode information.. + virtual void GetModeInfo( int adapter, int mode, MaterialVideoMode_t& info ) const = 0; + + virtual void AddModeChangeCallBack( ModeChangeCallbackFunc_t func ) = 0; + + // Returns the mode info for the current display device + virtual void GetDisplayMode( MaterialVideoMode_t& mode ) const = 0; + + // Sets the mode... + virtual bool SetMode( void* hwnd, const MaterialSystem_Config_t &config ) = 0; + + virtual bool SupportsMSAAMode( int nMSAAMode ) = 0; + + // FIXME: REMOVE! Get video card identitier + virtual const MaterialSystemHardwareIdentifier_t &GetVideoCardIdentifier( void ) const = 0; + + // Use this to spew information about the 3D layer + virtual void SpewDriverInfo() const = 0; + + // Get the image format of the back buffer. . useful when creating render targets, etc. + virtual void GetBackBufferDimensions( int &width, int &height) const = 0; + virtual ImageFormat GetBackBufferFormat() const = 0; + + virtual bool SupportsHDRMode( HDRType_t nHDRModede ) = 0; + + + // ----------------------------------------------------------- + // Window methods + // ----------------------------------------------------------- + + // Creates/ destroys a child window + virtual bool AddView( void* hwnd ) = 0; + virtual void RemoveView( void* hwnd ) = 0; + + // Sets the view + virtual void SetView( void* hwnd ) = 0; + + + // ----------------------------------------------------------- + // Control flow + // ----------------------------------------------------------- + + virtual void BeginFrame( float frameTime ) = 0; + virtual void EndFrame( ) = 0; + virtual void Flush( bool flushHardware = false ) = 0; + + /// FIXME: This stuff needs to be cleaned up and abstracted. + // Stuff that gets exported to the launcher through the engine + virtual void SwapBuffers( ) = 0; + + // Flushes managed textures from the texture cacher + virtual void EvictManagedResources() = 0; + + virtual void ReleaseResources(void) = 0; + virtual void ReacquireResources(void ) = 0; + + + // ----------------------------------------------------------- + // Device loss/restore + // ----------------------------------------------------------- + + // Installs a function to be called when we need to release vertex buffers + textures + virtual void AddReleaseFunc( MaterialBufferReleaseFunc_t func ) = 0; + virtual void RemoveReleaseFunc( MaterialBufferReleaseFunc_t func ) = 0; + + // Installs a function to be called when we need to restore vertex buffers + virtual void AddRestoreFunc( MaterialBufferRestoreFunc_t func ) = 0; + virtual void RemoveRestoreFunc( MaterialBufferRestoreFunc_t func ) = 0; + + // Installs a function to be called when we need to delete objects at the end of the render frame + virtual void AddEndFrameCleanupFunc( EndFrameCleanupFunc_t func ) = 0; + virtual void RemoveEndFrameCleanupFunc( EndFrameCleanupFunc_t func ) = 0; + + // Release temporary HW memory... + virtual void ResetTempHWMemory( bool bExitingLevel = false ) = 0; + + // For dealing with device lost in cases where SwapBuffers isn't called all the time (Hammer) + virtual void HandleDeviceLost() = 0; + + + // ----------------------------------------------------------- + // Shaders + // ----------------------------------------------------------- + + // Used to iterate over all shaders for editing purposes + // GetShaders returns the number of shaders it actually found + virtual int ShaderCount() const = 0; + virtual int GetShaders( int nFirstShader, int nMaxCount, IShader **ppShaderList ) const = 0; + + // FIXME: Is there a better way of doing this? + // Returns shader flag names for editors to be able to edit them + virtual int ShaderFlagCount() const = 0; + virtual const char * ShaderFlagName( int nIndex ) const = 0; + + // Gets the actual shader fallback for a particular shader + virtual void GetShaderFallback( const char *pShaderName, char *pFallbackShader, int nFallbackLength ) = 0; + + + // ----------------------------------------------------------- + // Material proxies + // ----------------------------------------------------------- + + virtual IMaterialProxyFactory *GetMaterialProxyFactory() = 0; + + // Sets the material proxy factory. Calling this causes all materials to be uncached. + virtual void SetMaterialProxyFactory( IMaterialProxyFactory* pFactory ) = 0; + + + // ----------------------------------------------------------- + // Editor mode + // ----------------------------------------------------------- + + // Used to enable editor materials. Must be called before Init. + virtual void EnableEditorMaterials() = 0; + virtual void EnableGBuffers() = 0; + + // ----------------------------------------------------------- + // Stub mode mode + // ----------------------------------------------------------- + + // Force it to ignore Draw calls. + virtual void SetInStubMode( bool bInStubMode ) = 0; + + + //--------------------------------------------------------- + // Debug support + //--------------------------------------------------------- + + virtual void DebugPrintUsedMaterials( const char *pSearchSubString, bool bVerbose ) = 0; + virtual void DebugPrintUsedTextures( void ) = 0; + + virtual void ToggleSuppressMaterial( char const* pMaterialName ) = 0; + virtual void ToggleDebugMaterial( char const* pMaterialName ) = 0; + + + //--------------------------------------------------------- + // Misc features + //--------------------------------------------------------- + //returns whether fast clipping is being used or not - needed to be exposed for better per-object clip behavior + virtual bool UsingFastClipping( void ) = 0; + + virtual int StencilBufferBits( void ) = 0; //number of bits per pixel in the stencil buffer + + + //--------------------------------------------------------- + // Material and texture management + //--------------------------------------------------------- + + // uncache all materials. . good for forcing reload of materials. + virtual void UncacheAllMaterials( ) = 0; + + // Remove any materials from memory that aren't in use as determined + // by the IMaterial's reference count. + virtual void UncacheUnusedMaterials( bool bRecomputeStateSnapshots = false ) = 0; + + // Load any materials into memory that are to be used as determined + // by the IMaterial's reference count. + virtual void CacheUsedMaterials( ) = 0; + + // Force all textures to be reloaded from disk. + virtual void ReloadTextures( ) = 0; + + // Reloads materials + virtual void ReloadMaterials( const char *pSubString = NULL ) = 0; + + // Create a procedural material. The keyvalues looks like a VMT file + virtual IMaterial * CreateMaterial( const char *pMaterialName, KeyValues *pVMTKeyValues ) = 0; + + // Find a material by name. + // The name of a material is a full path to + // the vmt file starting from "hl2/materials" (or equivalent) without + // a file extension. + // eg. "dev/dev_bumptest" refers to somethign similar to: + // "d:/hl2/hl2/materials/dev/dev_bumptest.vmt" + // + // Most of the texture groups for pTextureGroupName are listed in texture_group_names.h. + // + // Note: if the material can't be found, this returns a checkerboard material. You can + // find out if you have that material by calling IMaterial::IsErrorMaterial(). + // (Or use the global IsErrorMaterial function, which checks if it's null too). + virtual IMaterial * FindMaterial( char const* pMaterialName, const char *pTextureGroupName, bool complain = true, const char *pComplainPrefix = NULL ) = 0; + + //--------------------------------- + // This is the interface for knowing what materials are available + // is to use the following functions to get a list of materials. The + // material names will have the full path to the material, and that is the + // only way that the directory structure of the materials will be seen through this + // interface. + // NOTE: This is mostly for worldcraft to get a list of materials to put + // in the "texture" browser.in Worldcraft + virtual MaterialHandle_t FirstMaterial() const = 0; + + // returns InvalidMaterial if there isn't another material. + // WARNING: you must call GetNextMaterial until it returns NULL, + // otherwise there will be a memory leak. + virtual MaterialHandle_t NextMaterial( MaterialHandle_t h ) const = 0; + + // This is the invalid material + virtual MaterialHandle_t InvalidMaterial() const = 0; + + // Returns a particular material + virtual IMaterial* GetMaterial( MaterialHandle_t h ) const = 0; + + // Get the total number of materials in the system. These aren't just the used + // materials, but the complete collection. + virtual int GetNumMaterials( ) const = 0; + + //--------------------------------- + + virtual ITexture * FindTexture( char const* pTextureName, const char *pTextureGroupName, bool complain = true ) = 0; + + // Checks to see if a particular texture is loaded + virtual bool IsTextureLoaded( char const* pTextureName ) const = 0; + + // Creates a procedural texture + virtual ITexture * CreateProceduralTexture( const char *pTextureName, + const char *pTextureGroupName, + int w, + int h, + ImageFormat fmt, + int nFlags ) = 0; + + // + // Render targets + // + virtual void BeginRenderTargetAllocation() = 0; + virtual void EndRenderTargetAllocation() = 0; // Simulate an Alt-Tab in here, which causes a release/restore of all resources + + // Creates a render target + // If depth == true, a depth buffer is also allocated. If not, then + // the screen's depth buffer is used. + // Creates a texture for use as a render target + virtual ITexture * CreateRenderTargetTexture( int w, + int h, + RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change). + ImageFormat format, + MaterialRenderTargetDepth_t depth = MATERIAL_RT_DEPTH_SHARED ) = 0; + + virtual ITexture * CreateNamedRenderTargetTextureEx( const char *pRTName, // Pass in NULL here for an unnamed render target. + int w, + int h, + RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change). + ImageFormat format, + MaterialRenderTargetDepth_t depth = MATERIAL_RT_DEPTH_SHARED, + unsigned int textureFlags = TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + unsigned int renderTargetFlags = 0 ) = 0; + + virtual ITexture * CreateNamedRenderTargetTexture( const char *pRTName, + int w, + int h, + RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change). + ImageFormat format, + MaterialRenderTargetDepth_t depth = MATERIAL_RT_DEPTH_SHARED, + bool bClampTexCoords = true, + bool bAutoMipMap = false ) = 0; + + // Must be called between the above Begin-End calls! + virtual ITexture * CreateNamedRenderTargetTextureEx2( const char *pRTName, // Pass in NULL here for an unnamed render target. + int w, + int h, + RenderTargetSizeMode_t sizeMode, // Controls how size is generated (and regenerated on video mode change). + ImageFormat format, + MaterialRenderTargetDepth_t depth = MATERIAL_RT_DEPTH_SHARED, + unsigned int textureFlags = TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + unsigned int renderTargetFlags = 0 ) = 0; + + // ----------------------------------------------------------- + // Lightmaps + // ----------------------------------------------------------- + + // To allocate lightmaps, sort the whole world by material twice. + // The first time through, call AllocateLightmap for every surface. + // that has a lightmap. + // The second time through, call AllocateWhiteLightmap for every + // surface that expects to use shaders that expect lightmaps. + virtual void BeginLightmapAllocation( ) = 0; + virtual void EndLightmapAllocation( ) = 0; + + // returns the sorting id for this surface + virtual int AllocateLightmap( int width, int height, + int offsetIntoLightmapPage[2], + IMaterial *pMaterial ) = 0; + // returns the sorting id for this surface + virtual int AllocateWhiteLightmap( IMaterial *pMaterial ) = 0; + + // lightmaps are in linear color space + // lightmapPageID is returned by GetLightmapPageIDForSortID + // lightmapSize and offsetIntoLightmapPage are returned by AllocateLightmap. + // You should never call UpdateLightmap for a lightmap allocated through + // AllocateWhiteLightmap. + virtual void UpdateLightmap( int lightmapPageID, int lightmapSize[2], + int offsetIntoLightmapPage[2], + float *pFloatImage, float *pFloatImageBump1, + float *pFloatImageBump2, float *pFloatImageBump3 ) = 0; + + // fixme: could just be an array of ints for lightmapPageIDs since the material + // for a surface is already known. + virtual int GetNumSortIDs( ) = 0; + virtual void GetSortInfo( MaterialSystem_SortInfo_t *sortInfoArray ) = 0; + + // Read the page size of an existing lightmap by sort id (returned from AllocateLightmap()) + virtual void GetLightmapPageSize( int lightmap, int *width, int *height ) const = 0; + + virtual void ResetMaterialLightmapPageInfo() = 0; + + + + virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil = false ) = 0; + + // ----------------------------------------------------------- + // X360 specifics + // ----------------------------------------------------------- + +#if defined( _X360 ) + virtual void ListUsedMaterials( void ) = 0; + virtual HXUIFONT OpenTrueTypeFont( const char *pFontname, int tall, int style ) = 0; + virtual void CloseTrueTypeFont( HXUIFONT hFont ) = 0; + virtual bool GetTrueTypeFontMetrics( HXUIFONT hFont, wchar_t wchFirst, wchar_t wchLast, XUIFontMetrics *pFontMetrics, XUICharMetrics *pCharMetrics ) = 0; + // Render a sequence of characters and extract the data into a buffer + // For each character, provide the width+height of the font texture subrect, + // an offset to apply when rendering the glyph, and an offset into a buffer to receive the RGBA data + virtual bool GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset ) = 0; + virtual void PersistDisplay() = 0; + virtual void *GetD3DDevice() = 0; + virtual bool OwnGPUResources( bool bEnable ) = 0; +#endif + + // ----------------------------------------------------------- + // Access the render contexts + // ----------------------------------------------------------- + virtual IMatRenderContext * GetRenderContext() = 0; + + virtual void BeginUpdateLightmaps( void ) = 0; + virtual void EndUpdateLightmaps( void ) = 0; + + // ----------------------------------------------------------- + // Methods to force the material system into non-threaded, non-queued mode + // ----------------------------------------------------------- + virtual MaterialLock_t Lock() = 0; + virtual void Unlock( MaterialLock_t ) = 0; + + // Create a custom render context. Cannot be used to create MATERIAL_HARDWARE_CONTEXT + virtual IMatRenderContext *CreateRenderContext( MaterialContextType_t type ) = 0; + + // Set a specified render context to be the global context for the thread. Returns the prior context. + virtual IMatRenderContext *SetRenderContext( IMatRenderContext * ) = 0; + + virtual bool SupportsCSAAMode( int nNumSamples, int nQualityLevel ) = 0; + + virtual void RemoveModeChangeCallBack( ModeChangeCallbackFunc_t func ) = 0; + + // Finds or create a procedural material. + virtual IMaterial * FindProceduralMaterial( const char *pMaterialName, const char *pTextureGroupName, KeyValues *pVMTKeyValues ) = 0; + + virtual void AddTextureAlias( const char *pAlias, const char *pRealName ) = 0; + virtual void RemoveTextureAlias( const char *pAlias ) = 0; + + // returns a lightmap page ID for this allocation, -1 if none available + // frameID is a number that should be changed every frame to prevent locking any textures that are + // being used to draw in the previous frame + virtual int AllocateDynamicLightmap( int lightmapSize[2], int *pOutOffsetIntoPage, int frameID ) = 0; + + virtual void SetExcludedTextures( const char *pScriptName ) = 0; + virtual void UpdateExcludedTextures( void ) = 0; + + virtual bool IsInFrame( ) const = 0; + + virtual void CompactMemory() = 0; + + // For sv_pure mode. The filesystem figures out which files the client needs to reload to be "pure" ala the server's preferences. + virtual void ReloadFilesInList( IFileList *pFilesToReload ) = 0; + + // Get information about the texture for texture management tools + virtual bool GetTextureInformation( char const *szTextureName, MaterialTextureInfo_t &info ) const = 0; + + // call this once the render targets are allocated permanently at the beginning of the game + virtual void FinishRenderTargetAllocation( void ) = 0; + + virtual void ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly( void ) = 0; + virtual bool AllowThreading( bool bAllow, int nServiceThread ) = 0; + + virtual bool GetRecommendedVideoConfig( KeyValues *pKeyValues ) = 0; + + virtual IClientMaterialSystem* GetClientMaterialSystemInterface() = 0; + + virtual bool CanDownloadTextures() const = 0; + virtual int GetNumLightmapPages() const = 0; + + virtual IPaintMapTextureManager *RegisterPaintMapDataManager( IPaintMapDataManager *pDataManager ) = 0; //You supply an interface we can query for bits, it gives back an interface you can use to drive updates +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +abstract_class IMatRenderContext : public IRefCounted +{ +public: + virtual void BeginRender() = 0; + virtual void EndRender() = 0; + + virtual void Flush( bool flushHardware = false ) = 0; + + virtual void BindLocalCubemap( ITexture *pTexture ) = 0; + + // pass in an ITexture (that is build with "rendertarget" "1") or + // pass in NULL for the regular backbuffer. + virtual void SetRenderTarget( ITexture *pTexture ) = 0; + virtual ITexture * GetRenderTarget( void ) = 0; + + virtual void GetRenderTargetDimensions( int &width, int &height) const = 0; + + // Bind a material is current for rendering. + virtual void Bind( IMaterial *material, void *proxyData = 0 ) = 0; + // Bind a lightmap page current for rendering. You only have to + // do this for materials that require lightmaps. + virtual void BindLightmapPage( int lightmapPageID ) = 0; + + // inputs are between 0 and 1 + virtual void DepthRange( float zNear, float zFar ) = 0; + + virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil = false ) = 0; + + // read to a unsigned char rgb image. + virtual void ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ) = 0; + + // Sets lighting + virtual void SetLightingState( const MaterialLightingState_t& state ) = 0; + virtual void SetLights( int nCount, const LightDesc_t *pLights ) = 0; + + // The faces of the cube are specified in the same order as cubemap textures + virtual void SetAmbientLightCube( Vector4D cube[6] ) = 0; + + // Blit the backbuffer to the framebuffer texture + virtual void CopyRenderTargetToTexture( ITexture *pTexture ) = 0; + + // Set the current texture that is a copy of the framebuffer. + virtual void SetFrameBufferCopyTexture( ITexture *pTexture, int textureIndex = 0 ) = 0; + virtual ITexture *GetFrameBufferCopyTexture( int textureIndex ) = 0; + + // + // end vertex array api + // + + // matrix api + virtual void MatrixMode( MaterialMatrixMode_t matrixMode ) = 0; + virtual void PushMatrix( void ) = 0; + virtual void PopMatrix( void ) = 0; + virtual void LoadMatrix( VMatrix const& matrix ) = 0; + virtual void LoadMatrix( matrix3x4_t const& matrix ) = 0; + virtual void MultMatrix( VMatrix const& matrix ) = 0; + virtual void MultMatrix( matrix3x4_t const& matrix ) = 0; + virtual void MultMatrixLocal( VMatrix const& matrix ) = 0; + virtual void MultMatrixLocal( matrix3x4_t const& matrix ) = 0; + virtual void GetMatrix( MaterialMatrixMode_t matrixMode, VMatrix *matrix ) = 0; + virtual void GetMatrix( MaterialMatrixMode_t matrixMode, matrix3x4_t *matrix ) = 0; + virtual void LoadIdentity( void ) = 0; + virtual void Ortho( double left, double top, double right, double bottom, double zNear, double zFar ) = 0; + virtual void PerspectiveX( double fovx, double aspect, double zNear, double zFar ) = 0; + virtual void PickMatrix( int x, int y, int width, int height ) = 0; + virtual void Rotate( float angle, float x, float y, float z ) = 0; + virtual void Translate( float x, float y, float z ) = 0; + virtual void Scale( float x, float y, float z ) = 0; + // end matrix api + + // Sets/gets the viewport + virtual void Viewport( int x, int y, int width, int height ) = 0; + virtual void GetViewport( int& x, int& y, int& width, int& height ) const = 0; + + // The cull mode + virtual void CullMode( MaterialCullMode_t cullMode ) = 0; + virtual void FlipCullMode( void ) = 0; //CW->CCW or CCW->CW, intended for mirror support where the view matrix is flipped horizontally + + // end matrix api + + // This could easily be extended to a general user clip plane + virtual void SetHeightClipMode( MaterialHeightClipMode_t nHeightClipMode ) = 0; + // garymcthack : fog z is always used for heightclipz for now. + virtual void SetHeightClipZ( float z ) = 0; + + // Fog methods... + virtual void FogMode( MaterialFogMode_t fogMode ) = 0; + virtual void FogStart( float fStart ) = 0; + virtual void FogEnd( float fEnd ) = 0; + virtual void SetFogZ( float fogZ ) = 0; + virtual MaterialFogMode_t GetFogMode( void ) = 0; + + virtual void FogColor3f( float r, float g, float b ) = 0; + virtual void FogColor3fv( float const* rgb ) = 0; + virtual void FogColor3ub( unsigned char r, unsigned char g, unsigned char b ) = 0; + virtual void FogColor3ubv( unsigned char const* rgb ) = 0; + + virtual void GetFogColor( unsigned char *rgb ) = 0; + + // Sets the number of bones for skinning + virtual void SetNumBoneWeights( int numBones ) = 0; + + // Creates/destroys Mesh + virtual IMesh* CreateStaticMesh( VertexFormat_t fmt, const char *pTextureBudgetGroup, IMaterial * pMaterial = NULL, VertexStreamSpec_t *pStreamSpec = NULL ) = 0; + virtual void DestroyStaticMesh( IMesh* mesh ) = 0; + + // Gets the dynamic mesh associated with the currently bound material + // note that you've got to render the mesh before calling this function + // a second time. Clients should *not* call DestroyStaticMesh on the mesh + // returned by this call. + // Use buffered = false if you want to not have the mesh be buffered, + // but use it instead in the following pattern: + // meshBuilder.Begin + // meshBuilder.End + // Draw partial + // Draw partial + // Draw partial + // meshBuilder.Begin + // meshBuilder.End + // etc + // Use Vertex or Index Override to supply a static vertex or index buffer + // to use in place of the dynamic buffers. + // + // If you pass in a material in pAutoBind, it will automatically bind the + // material. This can be helpful since you must bind the material you're + // going to use BEFORE calling GetDynamicMesh. + virtual IMesh* GetDynamicMesh( + bool buffered = true, + IMesh* pVertexOverride = 0, + IMesh* pIndexOverride = 0, + IMaterial *pAutoBind = 0 ) = 0; + + // ------------ New Vertex/Index Buffer interface ---------------------------- + // Do we need support for bForceTempMesh and bSoftwareVertexShader? + // I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh. + virtual IVertexBuffer *CreateStaticVertexBuffer( VertexFormat_t fmt, int nVertexCount, const char *pTextureBudgetGroup ) = 0; + virtual IIndexBuffer *CreateStaticIndexBuffer( MaterialIndexFormat_t fmt, int nIndexCount, const char *pTextureBudgetGroup ) = 0; + virtual void DestroyVertexBuffer( IVertexBuffer * ) = 0; + virtual void DestroyIndexBuffer( IIndexBuffer * ) = 0; + // Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? + virtual IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true ) = 0; + virtual IIndexBuffer *GetDynamicIndexBuffer() = 0; + virtual void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ) = 0; + virtual void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) = 0; + virtual void Draw( MaterialPrimitiveType_t primitiveType, int firstIndex, int numIndices ) = 0; + // ------------ End ---------------------------- + + // Selection mode methods + virtual int SelectionMode( bool selectionMode ) = 0; + virtual void SelectionBuffer( unsigned int* pBuffer, int size ) = 0; + virtual void ClearSelectionNames( ) = 0; + virtual void LoadSelectionName( int name ) = 0; + virtual void PushSelectionName( int name ) = 0; + virtual void PopSelectionName() = 0; + + // Sets the Clear Color for ClearBuffer.... + virtual void ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ) = 0; + virtual void ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) = 0; + + // Allows us to override the depth buffer setting of a material + virtual void OverrideDepthEnable( bool bEnable, bool bDepthEnable ) = 0; + + // FIXME: This is a hack required for NVidia/XBox, can they fix in drivers? + virtual void DrawScreenSpaceQuad( IMaterial* pMaterial ) = 0; + + // For debugging and building recording files. This will stuff a token into the recording file, + // then someone doing a playback can watch for the token. + virtual void SyncToken( const char *pToken ) = 0; + + // FIXME: REMOVE THIS FUNCTION! + // The only reason why it's not gone is because we're a week from ship when I found the bug in it + // and everything's tuned to use it. + // It's returning values which are 2x too big (it's returning sphere diameter x2) + // Use ComputePixelDiameterOfSphere below in all new code instead. + virtual float ComputePixelWidthOfSphere( const Vector& origin, float flRadius ) = 0; + + // + // Occlusion query support + // + + // Allocate and delete query objects. + virtual OcclusionQueryObjectHandle_t CreateOcclusionQueryObject( void ) = 0; + virtual void DestroyOcclusionQueryObject( OcclusionQueryObjectHandle_t ) = 0; + + // Bracket drawing with begin and end so that we can get counts next frame. + virtual void BeginOcclusionQueryDrawing( OcclusionQueryObjectHandle_t ) = 0; + virtual void EndOcclusionQueryDrawing( OcclusionQueryObjectHandle_t ) = 0; + + // Get the number of pixels rendered between begin and end on an earlier frame. + // Calling this in the same frame is a huge perf hit! + virtual int OcclusionQuery_GetNumPixelsRendered( OcclusionQueryObjectHandle_t ) = 0; + + virtual void SetFlashlightMode( bool bEnable ) = 0; + + virtual void SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture ) = 0; + + // Gets the current height clip mode + virtual MaterialHeightClipMode_t GetHeightClipMode( ) = 0; + + // This returns the diameter of the sphere in pixels based on + // the current model, view, + projection matrices and viewport. + virtual float ComputePixelDiameterOfSphere( const Vector& vecAbsOrigin, float flRadius ) = 0; + + // By default, the material system applies the VIEW and PROJECTION matrices to the user clip + // planes (which are specified in world space) to generate projection-space user clip planes + // Occasionally (for the particle system in hl2, for example), we want to override that + // behavior and explictly specify a ViewProj transform for user clip planes + virtual void EnableUserClipTransformOverride( bool bEnable ) = 0; + virtual void UserClipTransform( const VMatrix &worldToView ) = 0; + + virtual bool GetFlashlightMode() const = 0; + + // Used to make the handle think it's never had a successful query before + virtual void ResetOcclusionQueryObject( OcclusionQueryObjectHandle_t ) = 0; + + // Creates/destroys morph data associated w/ a particular material + virtual IMorph *CreateMorph( MorphFormat_t format, const char *pDebugName ) = 0; + virtual void DestroyMorph( IMorph *pMorph ) = 0; + + // Binds the morph data for use in rendering + virtual void BindMorph( IMorph *pMorph ) = 0; + + // Sets flexweights for rendering + virtual void SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights ) = 0; + + // Allocates temp render data. Renderdata goes out of scope at frame end in multicore + // Renderdata goes out of scope after refcount goes to zero in singlecore. + // Locking/unlocking increases + decreases refcount + virtual void * LockRenderData( int nSizeInBytes ) = 0; + virtual void UnlockRenderData( void *pData ) = 0; + + // Typed version. If specified, pSrcData is copied into the locked memory. + template< class E > E* LockRenderDataTyped( int nCount, const E* pSrcData = NULL ); + + // Temp render data gets immediately freed after it's all unlocked in single core. + // This prevents it from being freed + virtual void AddRefRenderData() = 0; + virtual void ReleaseRenderData() = 0; + + // Returns whether a pointer is render data. NOTE: passing NULL returns true + virtual bool IsRenderData( const void *pData ) const = 0; + + // Read w/ stretch to a host-memory buffer + virtual void ReadPixelsAndStretch( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pBuffer, ImageFormat dstFormat, int nDstStride ) = 0; + + // Gets the window size + virtual void GetWindowSize( int &width, int &height ) const = 0; + + // This function performs a texture map from one texture map to the render destination, doing + // all the necessary pixel/texel coordinate fix ups. fractional values can be used for the + // src_texture coordinates to get linear sampling - integer values should produce 1:1 mappings + // for non-scaled operations. + virtual void DrawScreenSpaceRectangle( + IMaterial *pMaterial, + int destx, int desty, + int width, int height, + float src_texture_x0, float src_texture_y0, // which texel you want to appear at + // destx/y + float src_texture_x1, float src_texture_y1, // which texel you want to appear at + // destx+width-1, desty+height-1 + int src_texture_width, int src_texture_height, // needed for fixup + void *pClientRenderable = NULL, + int nXDice = 1, + int nYDice = 1 )=0; + + virtual void LoadBoneMatrix( int boneIndex, const matrix3x4_t& matrix ) = 0; + + // This version will push the current rendertarget + current viewport onto the stack + virtual void PushRenderTargetAndViewport( ) = 0; + + // This version will push a new rendertarget + a maximal viewport for that rendertarget onto the stack + virtual void PushRenderTargetAndViewport( ITexture *pTexture ) = 0; + + // This version will push a new rendertarget + a specified viewport onto the stack + virtual void PushRenderTargetAndViewport( ITexture *pTexture, int nViewX, int nViewY, int nViewW, int nViewH ) = 0; + + // This version will push a new rendertarget + a specified viewport onto the stack + virtual void PushRenderTargetAndViewport( ITexture *pTexture, ITexture *pDepthTexture, int nViewX, int nViewY, int nViewW, int nViewH ) = 0; + + // This will pop a rendertarget + viewport + virtual void PopRenderTargetAndViewport( void ) = 0; + + // Binds a particular texture as the current lightmap + virtual void BindLightmapTexture( ITexture *pLightmapTexture ) = 0; + + // Blit a subrect of the current render target to another texture + virtual void CopyRenderTargetToTextureEx( ITexture *pTexture, int nRenderTargetID, Rect_t *pSrcRect, Rect_t *pDstRect = NULL ) = 0; + + // Special off-center perspective matrix for DoF, MSAA jitter and poster rendering + virtual void PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ) = 0; + + // Sets the ambient light color + virtual void SetAmbientLightColor( float r, float g, float b ) = 0; + + // Rendering parameters control special drawing modes withing the material system, shader + // system, shaders, and engine. renderparm.h has their definitions. + virtual void SetFloatRenderingParameter(int parm_number, float value) = 0; + virtual void SetIntRenderingParameter(int parm_number, int value) = 0; + virtual void SetVectorRenderingParameter(int parm_number, Vector const &value) = 0; + + // stencil buffer operations. + virtual void SetStencilState( const ShaderStencilState_t &state ) = 0; + virtual void ClearStencilBufferRectangle(int xmin, int ymin, int xmax, int ymax,int value) =0; + + virtual void SetRenderTargetEx( int nRenderTargetID, ITexture *pTexture ) = 0; + + // rendering clip planes, beware that only the most recently pushed plane will actually be used in a sizeable chunk of hardware configurations + // and that changes to the clip planes mid-frame while UsingFastClipping() is true will result unresolvable depth inconsistencies + virtual void PushCustomClipPlane( const float *pPlane ) = 0; + virtual void PopCustomClipPlane( void ) = 0; + + // Returns the number of vertices + indices we can render using the dynamic mesh + // Passing true in the second parameter will return the max # of vertices + indices + // we can use before a flush is provoked and may return different values + // if called multiple times in succession. + // Passing false into the second parameter will return + // the maximum possible vertices + indices that can be rendered in a single batch + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) = 0; + + // Returns the max possible vertices + indices to render in a single draw call + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ) = 0; + virtual int GetMaxIndicesToRender( ) = 0; + virtual void DisableAllLocalLights() = 0; + virtual int CompareMaterialCombos( IMaterial *pMaterial1, IMaterial *pMaterial2, int lightMapID1, int lightMapID2 ) = 0; + + virtual IMesh *GetFlexMesh() = 0; + + virtual void SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ) = 0; + virtual void SetFlashlightStateExDeRef( const FlashlightState_t &state, ITexture *pFlashlightDepthTexture ) = 0; + + // Returns the currently bound local cubemap + virtual ITexture *GetLocalCubemap( ) = 0; + + // This is a version of clear buffers which will only clear the buffer at pixels which pass the stencil test + virtual void ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ) = 0; + + //enables/disables all entered clipping planes, returns the input from the last time it was called. + virtual bool EnableClipping( bool bEnable ) = 0; + + //get fog distances entered with FogStart(), FogEnd(), and SetFogZ() + virtual void GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) = 0; + + // Hooks for firing PIX events from outside the Material System... + virtual void BeginPIXEvent( unsigned long color, const char *szName ) = 0; + virtual void EndPIXEvent() = 0; + virtual void SetPIXMarker( unsigned long color, const char *szName ) = 0; + + // Batch API + // from changelist 166623: + // - replaced obtuse material system batch usage with an explicit and easier to thread API + virtual void BeginBatch( IMesh* pIndices ) = 0; + virtual void BindBatch( IMesh* pVertices, IMaterial *pAutoBind = NULL ) = 0; + virtual void DrawBatch(int firstIndex, int numIndices ) = 0; + virtual void EndBatch() = 0; + + // Raw access to the call queue, which can be NULL if not in a queued mode + virtual ICallQueue *GetCallQueue() = 0; + + // Returns the world-space camera position + virtual void GetWorldSpaceCameraPosition( Vector *pCameraPos ) = 0; + virtual void GetWorldSpaceCameraVectors( Vector *pVecForward, Vector *pVecRight, Vector *pVecUp ) = 0; + + // Set a linear vector color scale for all 3D rendering. + // A value of [1.0f, 1.0f, 1.0f] should match non-tone-mapped rendering. + virtual void SetToneMappingScaleLinear( const Vector &scale ) = 0; + virtual Vector GetToneMappingScaleLinear( void ) = 0; + + virtual void SetShadowDepthBiasFactors( float fSlopeScaleDepthBias, float fDepthBias ) = 0; + + // Apply stencil operations to every pixel on the screen without disturbing depth or color buffers + virtual void PerformFullScreenStencilOperation( void ) = 0; + + // Sets lighting origin for the current model (needed to convert directional lights to points) + virtual void SetLightingOrigin( Vector vLightingOrigin ) = 0; + + // Set scissor rect for rendering + virtual void SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ) = 0; + + // Methods used to build the morph accumulator that is read from when HW morphing is enabled. + virtual void BeginMorphAccumulation() = 0; + virtual void EndMorphAccumulation() = 0; + virtual void AccumulateMorph( IMorph* pMorph, int nMorphCount, const MorphWeight_t* pWeights ) = 0; + + virtual void PushDeformation( DeformationBase_t const *Deformation ) = 0; + virtual void PopDeformation( ) = 0; + virtual int GetNumActiveDeformations() const = 0; + + virtual bool GetMorphAccumulatorTexCoord( Vector2D *pTexCoord, IMorph *pMorph, int nVertex ) = 0; + + // Version of get dynamic mesh that specifies a specific vertex format + virtual IMesh* GetDynamicMeshEx( VertexFormat_t vertexFormat, bool bBuffered = true, + IMesh* pVertexOverride = 0, IMesh* pIndexOverride = 0, IMaterial *pAutoBind = 0 ) = 0; + + virtual void FogMaxDensity( float flMaxDensity ) = 0; + +#if defined( _X360 ) + //Seems best to expose GPR allocation to scene rendering code. 128 total to split between vertex/pixel shaders (pixel will be set to 128 - vertex). Minimum value of 16. More GPR's = more threads. + virtual void PushVertexShaderGPRAllocation( int iVertexShaderCount = 64 ) = 0; + virtual void PopVertexShaderGPRAllocation( void ) = 0; + + virtual void FlushHiStencil() = 0; + + virtual void Begin360ZPass( int nNumDynamicIndicesNeeded ) = 0; + virtual void End360ZPass() = 0; +#endif + + virtual IMaterial *GetCurrentMaterial() = 0; + virtual int GetCurrentNumBones() const = 0; + virtual void *GetCurrentProxy() = 0; + + // Color correction related methods.. + // Client cannot call IColorCorrectionSystem directly because it is not thread-safe + // FIXME: Make IColorCorrectionSystem threadsafe? + virtual void EnableColorCorrection( bool bEnable ) = 0; + virtual ColorCorrectionHandle_t AddLookup( const char *pName ) = 0; + virtual bool RemoveLookup( ColorCorrectionHandle_t handle ) = 0; + virtual void LockLookup( ColorCorrectionHandle_t handle ) = 0; + virtual void LoadLookup( ColorCorrectionHandle_t handle, const char *pLookupName ) = 0; + virtual void UnlockLookup( ColorCorrectionHandle_t handle ) = 0; + virtual void SetLookupWeight( ColorCorrectionHandle_t handle, float flWeight ) = 0; + virtual void ResetLookupWeights( ) = 0; + virtual void SetResetable( ColorCorrectionHandle_t handle, bool bResetable ) = 0; + + //There are some cases where it's simply not reasonable to update the full screen depth texture (mostly on PC). + //Use this to mark it as invalid and use a dummy texture for depth reads. + virtual void SetFullScreenDepthTextureValidityFlag( bool bIsValid ) = 0; + + // A special path used to tick the front buffer while loading on the 360 + virtual void SetNonInteractivePacifierTexture( ITexture *pTexture, float flNormalizedX, float flNormalizedY, float flNormalizedSize ) = 0; + virtual void SetNonInteractiveTempFullscreenBuffer( ITexture *pTexture, MaterialNonInteractiveMode_t mode ) = 0; + virtual void EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode ) = 0; + virtual void RefreshFrontBufferNonInteractive() = 0; + + // Flip culling state (swap CCW <-> CW) + virtual void FlipCulling( bool bFlipCulling ) = 0; + + virtual void SetTextureRenderingParameter(int parm_number, ITexture *pTexture) = 0; + + //only actually sets a bool that can be read in from shaders, doesn't do any of the legwork + virtual void EnableSinglePassFlashlightMode( bool bEnable ) = 0; + + // Draws instances with different meshes + virtual void DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstance ) = 0; + + // Allows us to override the color/alpha write settings of a material + virtual void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) = 0; + virtual void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) = 0; + + virtual void ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ) = 0; + + // Subdivision surface interface + virtual int GetSubDBufferWidth() = 0; + virtual float* LockSubDBuffer( int nNumRows ) = 0; + virtual void UnlockSubDBuffer() = 0; + + // Update current frame's game time for the shader api. + virtual void UpdateGameTime( float flTime ) = 0; + + virtual void PrintfVA( char *fmt, va_list vargs ) = 0; + virtual void Printf( char *fmt, ... ) = 0; + virtual float Knob( char *knobname, float *setvalue = NULL ) = 0; +}; + + +template< class E > inline E* IMatRenderContext::LockRenderDataTyped( int nCount, const E* pSrcData ) +{ + int nSizeInBytes = nCount * sizeof(E); + E *pDstData = (E*)LockRenderData( nSizeInBytes ); + if ( pSrcData && pDstData ) + { + memcpy( pDstData, pSrcData, nSizeInBytes ); + } + return pDstData; +} + + +//----------------------------------------------------------------------------- +// Utility class for addreffing/releasing render data (prevents freeing on single core) +//----------------------------------------------------------------------------- +class CMatRenderDataReference +{ +public: + CMatRenderDataReference(); + CMatRenderDataReference( IMatRenderContext* pRenderContext ); + ~CMatRenderDataReference(); + void Lock( IMatRenderContext *pRenderContext ); + void Release(); + +private: + IMatRenderContext *m_pRenderContext; +}; + + +inline CMatRenderDataReference::CMatRenderDataReference() +{ + m_pRenderContext = NULL; +} + +inline CMatRenderDataReference::CMatRenderDataReference( IMatRenderContext* pRenderContext ) +{ + m_pRenderContext = NULL; + Lock( pRenderContext ); +} + +inline CMatRenderDataReference::~CMatRenderDataReference() +{ + Release(); +} + +inline void CMatRenderDataReference::Lock( IMatRenderContext* pRenderContext ) +{ + if ( !m_pRenderContext ) + { + m_pRenderContext = pRenderContext; + m_pRenderContext->AddRefRenderData( ); + } +} + +inline void CMatRenderDataReference::Release() +{ + if ( m_pRenderContext ) + { + m_pRenderContext->ReleaseRenderData( ); + m_pRenderContext = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Utility class for locking/unlocking render data +//----------------------------------------------------------------------------- +template< typename E > +class CMatRenderData +{ +public: + CMatRenderData( IMatRenderContext* pRenderContext ); + CMatRenderData( IMatRenderContext* pRenderContext, int nCount, const E *pSrcData = NULL ); + ~CMatRenderData(); + E* Lock( int nCount, const E* pSrcData = NULL ); + void Release(); + bool IsValid() const; + const E* Base() const; + E* Base(); + const E& operator[]( int i ) const; + E& operator[]( int i ); + +private: + IMatRenderContext* m_pRenderContext; + E *m_pRenderData; + int m_nCount; + bool m_bNeedsUnlock; +}; + +template< typename E > +inline CMatRenderData::CMatRenderData( IMatRenderContext* pRenderContext ) +{ + m_pRenderContext = pRenderContext; + m_nCount = 0; + m_pRenderData = 0; + m_bNeedsUnlock = false; +} + +template< typename E > +inline CMatRenderData::CMatRenderData( IMatRenderContext* pRenderContext, int nCount, const E* pSrcData ) +{ + m_pRenderContext = pRenderContext; + m_nCount = 0; + m_pRenderData = 0; + m_bNeedsUnlock = false; + Lock( nCount, pSrcData ); +} + +template< typename E > +inline CMatRenderData::~CMatRenderData() +{ + Release(); +} + +template< typename E > +inline bool CMatRenderData::IsValid() const +{ + return m_pRenderData != NULL; +} + +template< typename E > +inline E* CMatRenderData::Lock( int nCount, const E* pSrcData ) +{ + m_nCount = nCount; + if ( pSrcData && m_pRenderContext->IsRenderData( pSrcData ) ) + { + // Yes, we're const-casting away, but that should be ok since + // the src data is render data + m_pRenderData = const_cast( pSrcData ); + m_pRenderContext->AddRefRenderData(); + m_bNeedsUnlock = false; + return m_pRenderData; + } + m_pRenderData = m_pRenderContext->LockRenderDataTyped( nCount, pSrcData ); + m_bNeedsUnlock = true; + return m_pRenderData; +} + +template< typename E > +inline void CMatRenderData::Release() +{ + if ( m_pRenderContext && m_pRenderData ) + { + if ( m_bNeedsUnlock ) + { + m_pRenderContext->UnlockRenderData( m_pRenderData ); + } + else + { + m_pRenderContext->ReleaseRenderData(); + } + } + m_pRenderData = NULL; + m_nCount = 0; + m_bNeedsUnlock = false; +} + +template< typename E > +inline E* CMatRenderData::Base() +{ + return m_pRenderData; +} + +template< typename E > +inline const E* CMatRenderData::Base() const +{ + return m_pRenderData; +} + +template< typename E > +inline E& CMatRenderData::operator[]( int i ) +{ + Assert( ( i >= 0 ) && ( i < m_nCount ) ); + return m_pRenderData[i]; +} + +template< typename E > +inline const E& CMatRenderData::operator[]( int i ) const +{ + Assert( ( i >= 0 ) && ( i < m_nCount ) ); + return m_pRenderData[i]; +} + + +//----------------------------------------------------------------------------- + +class CMatRenderContextPtr : public CRefPtr +{ + typedef CRefPtr BaseClass; +public: + CMatRenderContextPtr() {} + CMatRenderContextPtr( IMatRenderContext *pInit ) : BaseClass( pInit ) { if ( BaseClass::m_pObject ) BaseClass::m_pObject->BeginRender(); } + CMatRenderContextPtr( IMaterialSystem *pFrom ) : BaseClass( pFrom->GetRenderContext() ) { if ( BaseClass::m_pObject ) BaseClass::m_pObject->BeginRender(); } + ~CMatRenderContextPtr() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->EndRender(); } + + IMatRenderContext *operator=( IMatRenderContext *p ) { if ( p ) p->BeginRender(); return BaseClass::operator=( p ); } + + void SafeRelease() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->EndRender(); BaseClass::SafeRelease(); } + void AssignAddRef( IMatRenderContext *pFrom ) { if ( BaseClass::m_pObject ) BaseClass::m_pObject->EndRender(); BaseClass::AssignAddRef( pFrom ); BaseClass::m_pObject->BeginRender(); } + + void GetFrom( IMaterialSystem *pFrom ) { AssignAddRef( pFrom->GetRenderContext() ); } + + +private: + CMatRenderContextPtr( const CMatRenderContextPtr &from ); + void operator=( const CMatRenderContextPtr &from ); + +}; + +//----------------------------------------------------------------------------- +// Helper class for begin/end of pix event via constructor/destructor +//----------------------------------------------------------------------------- +#define PIX_VALVE_ORANGE 0xFFF5940F + +class PIXEvent +{ +public: + PIXEvent( IMatRenderContext *pRenderContext, const char *szName, unsigned long color = PIX_VALVE_ORANGE ) + { + m_pRenderContext = pRenderContext; + Assert( m_pRenderContext ); + Assert( szName ); + m_pRenderContext->BeginPIXEvent( color, szName ); + } + ~PIXEvent() + { + m_pRenderContext->EndPIXEvent(); + } +private: + IMatRenderContext *m_pRenderContext; +}; + + +#define PIX_ENABLE 0 // set this to 1 and build engine/studiorender to enable pix events in the engine + +#if PIX_ENABLE +# define PIXEVENT PIXEvent _pixEvent +#else +# define PIXEVENT +#endif + +//----------------------------------------------------------------------------- + +#ifdef MATERIAL_SYSTEM_DEBUG_CALL_QUEUE +#include "tier1/callqueue.h" +#include "tier1/fmtstr.h" +static void DoMatSysQueueMark( IMaterialSystem *pMaterialSystem, const char *psz ) +{ + CMatRenderContextPtr pRenderContext( pMaterialSystem ); + if ( pRenderContext->GetCallQueue() ) + pRenderContext->GetCallQueue()->QueueCall( Plat_DebugString, CUtlEnvelope( psz ) ); +} + +#define MatSysQueueMark( pMaterialSystem, ...) DoMatSysQueueMark( pMaterialSystem, CFmtStr( __VA_ARGS__ ) ) +#else +#define MatSysQueueMark( msg, ...) ((void)0) +#endif + +//----------------------------------------------------------------------------- + +DECLARE_TIER2_INTERFACE( IMaterialSystem, materials ); +DECLARE_TIER2_INTERFACE( IMaterialSystem, g_pMaterialSystem ); + +#endif // IMATERIALSYSTEM_H diff --git a/public/materialsystem/imaterialsystemhardwareconfig.h b/public/materialsystem/imaterialsystemhardwareconfig.h new file mode 100644 index 0000000..9ed92c8 --- /dev/null +++ b/public/materialsystem/imaterialsystemhardwareconfig.h @@ -0,0 +1,180 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMATERIALSYSTEMHARDWARECONFIG_H +#define IMATERIALSYSTEMHARDWARECONFIG_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/interface.h" +#include "tier2/tier2.h" + +#include "bitmap/imageformat.h" + + +//----------------------------------------------------------------------------- +// Material system interface version +//----------------------------------------------------------------------------- + +// HDRFIXME NOTE: must match common_ps_fxc.h +enum HDRType_t +{ + HDR_TYPE_NONE, + HDR_TYPE_INTEGER, + HDR_TYPE_FLOAT, +}; + +// For now, vertex compression is simply "on or off" (for the sake of simplicity +// and MeshBuilder perf.), but later we may support multiple flavours. +enum VertexCompressionType_t +{ + // This indicates an uninitialized VertexCompressionType_t value + VERTEX_COMPRESSION_INVALID = 0xFFFFFFFF, + + // 'VERTEX_COMPRESSION_NONE' means that no elements of a vertex are compressed + VERTEX_COMPRESSION_NONE = 0, + + // Currently (more stuff may be added as needed), 'VERTEX_COMPRESSION_ON' means: + // - if a vertex contains VERTEX_ELEMENT_NORMAL, this is compressed + // (see CVertexBuilder::CompressedNormal3f) + // - if a vertex contains VERTEX_ELEMENT_USERDATA4 (and a normal - together defining a tangent + // frame, with the binormal reconstructed in the vertex shader), this is compressed + // (see CVertexBuilder::CompressedUserData) + // - if a vertex contains VERTEX_ELEMENT_BONEWEIGHTSx, this is compressed + // (see CVertexBuilder::CompressedBoneWeight3fv) + VERTEX_COMPRESSION_ON = 1 +}; + + +// use DEFCONFIGMETHOD to define time-critical methods that we want to make just return constants +// on the 360, so that the checks will happen at compile time. Not all methods are defined this way +// - just the ones that I perceive as being called often in the frame interval. +#ifdef _X360 +#define DEFCONFIGMETHOD( ret_type, method, xbox_return_value ) \ +FORCEINLINE ret_type method const \ +{ \ + return xbox_return_value; \ +} + + +#else +#define DEFCONFIGMETHOD( ret_type, method, xbox_return_value ) \ +virtual ret_type method const = 0; +#endif + + + +//----------------------------------------------------------------------------- +// Material system configuration +//----------------------------------------------------------------------------- +class IMaterialSystemHardwareConfig +{ +public: + virtual int GetFrameBufferColorDepth() const = 0; + virtual int GetSamplerCount() const = 0; + virtual bool HasSetDeviceGammaRamp() const = 0; + virtual VertexCompressionType_t SupportsCompressedVertices() const = 0; + virtual int MaximumAnisotropicLevel() const = 0; // 0 means no anisotropic filtering + virtual int MaxTextureWidth() const = 0; + virtual int MaxTextureHeight() const = 0; + virtual int TextureMemorySize() const = 0; + virtual bool SupportsMipmappedCubemaps() const = 0; + + virtual int NumVertexShaderConstants() const = 0; + virtual int NumPixelShaderConstants() const = 0; + virtual int MaxNumLights() const = 0; + virtual int MaxTextureAspectRatio() const = 0; + virtual int MaxVertexShaderBlendMatrices() const = 0; + virtual int MaxUserClipPlanes() const = 0; + virtual bool UseFastClipping() const = 0; + + // This here should be the major item looked at when checking for compat + // from anywhere other than the material system shaders + DEFCONFIGMETHOD( int, GetDXSupportLevel(), 98 ); + virtual const char *GetShaderDLLName() const = 0; + + virtual bool ReadPixelsFromFrontBuffer() const = 0; + + // Are dx dynamic textures preferred? + virtual bool PreferDynamicTextures() const = 0; + + DEFCONFIGMETHOD( bool, SupportsHDR(), true ); + + virtual bool NeedsAAClamp() const = 0; + virtual bool NeedsATICentroidHack() const = 0; + + // This is the max dx support level supported by the card + virtual int GetMaxDXSupportLevel() const = 0; + + // Does the card specify fog color in linear space when sRGBWrites are enabled? + virtual bool SpecifiesFogColorInLinearSpace() const = 0; + + // Does the card support sRGB reads/writes? + DEFCONFIGMETHOD( bool, SupportsSRGB(), true ); + + virtual bool IsAAEnabled() const = 0; // Is antialiasing being used? + + // NOTE: Anything after this was added after shipping HL2. + virtual int GetVertexSamplerCount() const = 0; + virtual int GetMaxVertexTextureDimension() const = 0; + + virtual int MaxTextureDepth() const = 0; + + virtual HDRType_t GetHDRType() const = 0; + virtual HDRType_t GetHardwareHDRType() const = 0; + + virtual bool SupportsStreamOffset() const = 0; + + virtual int StencilBufferBits() const = 0; + virtual int MaxViewports() const = 0; + + virtual void OverrideStreamOffsetSupport( bool bOverrideEnabled, bool bEnableSupport ) = 0; + + virtual int GetShadowFilterMode() const = 0; + + virtual int NeedsShaderSRGBConversion() const = 0; + + DEFCONFIGMETHOD( bool, UsesSRGBCorrectBlending(), true ); + + virtual bool HasFastVertexTextures() const = 0; + virtual int MaxHWMorphBatchCount() const = 0; + + virtual bool SupportsHDRMode( HDRType_t nHDRMode ) const = 0; + + virtual bool GetHDREnabled( void ) const = 0; + virtual void SetHDREnabled( bool bEnable ) = 0; + + virtual bool SupportsBorderColor( void ) const = 0; + virtual bool SupportsFetch4( void ) const = 0; + + virtual float GetShadowDepthBias() const = 0; + virtual float GetShadowSlopeScaleDepthBias() const = 0; + + virtual bool PreferZPrepass() const = 0; + + virtual bool SuppressPixelShaderCentroidHackFixup() const = 0; + virtual bool PreferTexturesInHWMemory() const = 0; + virtual bool PreferHardwareSync() const = 0; + virtual bool ActualHasFastVertexTextures() const = 0; + + virtual bool SupportsShadowDepthTextures( void ) const = 0; + virtual ImageFormat GetShadowDepthTextureFormat( void ) const = 0; + virtual ImageFormat GetNullTextureFormat( void ) const = 0; + virtual int GetMinDXSupportLevel() const = 0; + virtual bool IsUnsupported() const = 0; + + // Backward compat for stdshaders +#if defined ( STDSHADER_DBG_DLL_EXPORT ) || defined( STDSHADER_DX9_DLL_EXPORT ) + inline bool SupportsPixelShaders_2_b() const { return GetDXSupportLevel() >= 92; } +#endif +}; + +#endif // IMATERIALSYSTEMHARDWARECONFIG_H diff --git a/public/materialsystem/imaterialsystemstub.h b/public/materialsystem/imaterialsystemstub.h new file mode 100644 index 0000000..76cbc6b --- /dev/null +++ b/public/materialsystem/imaterialsystemstub.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef IMATERIALSYSTEMSTUB_H +#define IMATERIALSYSTEMSTUB_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/imaterialsystem.h" + + +// If you get this interface out of the material system, it'll return an IMaterialSystem +// with everything stubbed. This is used for running the client in text mode. +#define MATERIAL_SYSTEM_STUB_INTERFACE_VERSION "VMaterialSystemStub001" + + +class IMaterialSystemStub : public IMaterialSystem +{ +public: + // If this is called, then the stub will call through to the real material + // system in some functions. + virtual void SetRealMaterialSystem( IMaterialSystem *pSys ) = 0; +}; + + +#endif // IMATERIALSYSTEMSTUB_H diff --git a/public/materialsystem/imaterialvar.h b/public/materialsystem/imaterialvar.h new file mode 100644 index 0000000..c8e3f60 --- /dev/null +++ b/public/materialsystem/imaterialvar.h @@ -0,0 +1,245 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef IMATERIALVAR_H +#define IMATERIALVAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier1/utlsymbol.h" +#include "mathlib/vector4d.h" +class IMaterial; +class VMatrix; +class ITexture; + +#define MAKE_MATERIALVAR_FOURCC(ch0, ch1, ch2, ch3) \ + ((unsigned long)(ch0) | ((unsigned long)(ch1) << 8) | \ + ((unsigned long)(ch2) << 16) | ((unsigned long)(ch3) << 24 )) + +// This fourcc is reserved. +#define FOURCC_UNKNOWN MAKE_MATERIALVAR_FOURCC('U','N','K','N') + + +//----------------------------------------------------------------------------- +// Various material var types +//----------------------------------------------------------------------------- +enum MaterialVarType_t +{ + MATERIAL_VAR_TYPE_FLOAT = 0, + MATERIAL_VAR_TYPE_STRING, + MATERIAL_VAR_TYPE_VECTOR, + MATERIAL_VAR_TYPE_TEXTURE, + MATERIAL_VAR_TYPE_INT, + MATERIAL_VAR_TYPE_FOURCC, + MATERIAL_VAR_TYPE_UNDEFINED, + MATERIAL_VAR_TYPE_MATRIX, + MATERIAL_VAR_TYPE_MATERIAL, +}; + +typedef unsigned short MaterialVarSym_t; + +class IMaterialVar +{ +public: + typedef unsigned long FourCC; + +protected: + // base data and accessors + char* m_pStringVal; + int m_intVal; + Vector4D m_VecVal; + + // member data. total = 4 bytes + uint8 m_Type : 4; + uint8 m_nNumVectorComps : 3; + uint8 m_bFakeMaterialVar : 1; + uint8 m_nTempIndex; + CUtlSymbol m_Name; + +public: + // class factory methods + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey, VMatrix const& matrix ); + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey, char const* pVal ); + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey, float* pVal, int numcomps ); + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey, float val ); + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey, int val ); + static IMaterialVar* Create( IMaterial* pMaterial, char const* pKey ); + static void Destroy( IMaterialVar* pVar ); + static MaterialVarSym_t GetSymbol( char const* pName ); + static MaterialVarSym_t FindSymbol( char const* pName ); + static bool SymbolMatches( char const* pName, MaterialVarSym_t symbol ); + static void DeleteUnreferencedTextures( bool enable ); + + virtual ITexture *GetTextureValue( void ) = 0; + + virtual char const * GetName( void ) const = 0; + virtual MaterialVarSym_t GetNameAsSymbol() const = 0; + + virtual void SetFloatValue( float val ) = 0; + + virtual void SetIntValue( int val ) = 0; + + virtual void SetStringValue( char const *val ) = 0; + virtual char const * GetStringValue( void ) const = 0; + + // Use FourCC values to pass app-defined data structures between + // the proxy and the shader. The shader should ignore the data if + // its FourCC type not correct. + virtual void SetFourCCValue( FourCC type, void *pData ) = 0; + virtual void GetFourCCValue( FourCC *type, void **ppData ) = 0; + + // Vec (dim 2-4) + virtual void SetVecValue( float const* val, int numcomps ) = 0; + virtual void SetVecValue( float x, float y ) = 0; + virtual void SetVecValue( float x, float y, float z ) = 0; + virtual void SetVecValue( float x, float y, float z, float w ) = 0; + virtual void GetLinearVecValue( float *val, int numcomps ) const = 0; + + // revisit: is this a good interface for textures? + virtual void SetTextureValue( ITexture * ) = 0; + + virtual IMaterial * GetMaterialValue( void ) = 0; + virtual void SetMaterialValue( IMaterial * ) = 0; + + virtual bool IsDefined() const = 0; + virtual void SetUndefined() = 0; + + // Matrix + virtual void SetMatrixValue( VMatrix const& matrix ) = 0; + virtual const VMatrix &GetMatrixValue( ) = 0; + virtual bool MatrixIsIdentity() const = 0; + + // Copy.... + virtual void CopyFrom( IMaterialVar *pMaterialVar ) = 0; + + virtual void SetValueAutodetectType( char const *val ) = 0; + + virtual IMaterial * GetOwningMaterial() = 0; + + //set just 1 component + virtual void SetVecComponentValue( float fVal, int nComponent ) = 0; + +protected: + virtual int GetIntValueInternal( void ) const = 0; + virtual float GetFloatValueInternal( void ) const = 0; + virtual float const* GetVecValueInternal( ) const = 0; + virtual void GetVecValueInternal( float *val, int numcomps ) const = 0; + virtual int VectorSizeInternal() const = 0; + +public: + FORCEINLINE MaterialVarType_t GetType( void ) const + { + return ( MaterialVarType_t )m_Type; + } + + FORCEINLINE bool IsTexture() const + { + return m_Type == MATERIAL_VAR_TYPE_TEXTURE; + } + + FORCEINLINE operator ITexture*() + { + return GetTextureValue(); + } + + // NOTE: Fast methods should only be called in thread-safe situations + FORCEINLINE int GetIntValueFast( void ) const + { + // Set methods for float and vector update this + return m_intVal; + } + + FORCEINLINE float GetFloatValueFast( void ) const + { + return m_VecVal[0]; + } + + FORCEINLINE float const* GetVecValueFast( ) const + { + return m_VecVal.Base(); + } + + FORCEINLINE void GetVecValueFast( float *val, int numcomps ) const + { + Assert( ( numcomps >0 ) && ( numcomps <= 4 ) ); + for( int i=0 ; i < numcomps; i++ ) + { + val[i] = m_VecVal[ i ]; + } + } + + FORCEINLINE int VectorSizeFast() const + { + return m_nNumVectorComps; + } + +#ifdef FAST_MATERIALVAR_ACCESS + FORCEINLINE int GetIntValue( void ) const + { + return GetIntValueFast(); + } + + FORCEINLINE float GetFloatValue( void ) const + { + return GetFloatValueFast(); + } + + FORCEINLINE float const* GetVecValue( ) const + { + return GetVecValueFast(); + } + + FORCEINLINE void GetVecValue( float *val, int numcomps ) const + { + GetVecValueFast( val, numcomps ); + } + + FORCEINLINE int VectorSize() const + { + return VectorSizeFast(); + } +#else // !FAST_MATERIALVAR_ACCESS + FORCEINLINE int GetIntValue( void ) const + { + return GetIntValueInternal(); + } + + FORCEINLINE float GetFloatValue( void ) const + { + return GetFloatValueInternal(); + } + + FORCEINLINE float const* GetVecValue( ) const + { + return GetVecValueInternal(); + } + + FORCEINLINE void GetVecValue( float *val, int numcomps ) const + { + return GetVecValueInternal( val, numcomps ); + } + + FORCEINLINE int VectorSize() const + { + return VectorSizeInternal(); + } +#endif + +private: + FORCEINLINE void SetTempIndex( int nIndex ) + { + m_nTempIndex = nIndex; + } + + friend void EnableThreadedMaterialVarAccess( bool bEnable, IMaterialVar **ppParams, int nVarCount ); +}; + +#endif // IMATERIALVAR_H diff --git a/public/materialsystem/imesh.h b/public/materialsystem/imesh.h new file mode 100644 index 0000000..0d860fa --- /dev/null +++ b/public/materialsystem/imesh.h @@ -0,0 +1,3945 @@ +//==== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IMESH_H +#define IMESH_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "materialsystem/imaterial.h" +#include +#include +#include "tier0/dbg.h" +#include "tier2/meshutils.h" + +#ifdef OSX + // leaving this disabled while we test out EXT_vertex_array_bgra on 10.6.4 drivers + //#define OPENGL_SWAP_COLORS defined +#endif + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class CMeshBuilder; +class IMaterialVar; +typedef uint64 VertexFormat_t; +struct ShaderStencilState_t; + + +//----------------------------------------------------------------------------- +// Define this to find write-combine problems +//----------------------------------------------------------------------------- +#ifdef _DEBUG +//#ifndef DEBUG_WRITE_COMBINE +//#define DEBUG_WRITE_COMBINE 1 +//#endif +#endif + + +//----------------------------------------------------------------------------- +// The Vertex Buffer interface +//----------------------------------------------------------------------------- +enum +{ + VERTEX_MAX_TEXTURE_COORDINATES = 8, + BONE_MATRIX_INDEX_INVALID = 255 +}; + +// Internal maximums for sizes. Don't use directly, use IMaterialSystem::GetMaxToRender() +enum +{ + INDEX_BUFFER_SIZE = 32768, + DYNAMIC_VERTEX_BUFFER_MEMORY = ( 1024 + 512 ) * 1024, + DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL = 384 * 1024, // Only allocate this much during map transitions +}; + +// Vertex fields must be written in well-defined order to achieve write combining, +// which is a perf booster +enum WriteCombineOrdering_t +{ + MB_FIELD_NONE = -1, + MB_FIELD_POSITION = 0, + MB_FIELD_BONE_WEIGHTS, + MB_FIELD_BONE_INDEX, + MB_FIELD_NORMAL, + MB_FIELD_COLOR, + MB_FIELD_SPECULAR, + MB_FIELD_TEXCOORD_FIRST, + MB_FIELD_TEXCOORD_LAST = MB_FIELD_TEXCOORD_FIRST + VERTEX_MAX_TEXTURE_COORDINATES - 1, + MB_FIELD_TANGENT_S, + MB_FIELD_TANGENT_T, + MB_FIELD_USERDATA, +}; + +#define MB_FIELD_TEXCOORD( nStage ) ( MB_FIELD_TEXCOORD_FIRST + ( nStage ) ) + +struct VertexDesc_t +{ + // These can be set to zero if there are pointers to dummy buffers, when the + // actual buffer format doesn't contain the data but it needs to be safe to + // use all the CMeshBuilder functions. + int m_VertexSize_Position; + int m_VertexSize_BoneWeight; + int m_VertexSize_BoneMatrixIndex; + int m_VertexSize_Normal; + int m_VertexSize_Color; + int m_VertexSize_Specular; + int m_VertexSize_TexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; + int m_VertexSize_TangentS; + int m_VertexSize_TangentT; + int m_VertexSize_Wrinkle; + + int m_VertexSize_UserData; + + int m_ActualVertexSize; // Size of the vertices.. Some of the m_VertexSize_ elements above + // are set to this value and some are set to zero depending on which + // fields exist in a buffer's vertex format. + + // The type of compression applied to this vertex data + VertexCompressionType_t m_CompressionType; + + // Number of bone weights per vertex... + int m_NumBoneWeights; + + // Pointers to our current vertex data + float *m_pPosition; + + float *m_pBoneWeight; + +#ifndef NEW_SKINNING + unsigned char *m_pBoneMatrixIndex; +#else + float *m_pBoneMatrixIndex; +#endif + + float *m_pNormal; + + unsigned char *m_pColor; + unsigned char *m_pSpecular; + float *m_pTexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; + + // Tangent space *associated with one particular set of texcoords* + float *m_pTangentS; + float *m_pTangentT; + + float *m_pWrinkle; + + // user data + float *m_pUserData; + + // The first vertex index (used for buffered vertex buffers, or cards that don't support stream offset) + int m_nFirstVertex; + + // The offset in bytes of the memory we're writing into + // from the start of the D3D buffer (will be 0 for static meshes) + unsigned int m_nOffset; + +#ifdef DEBUG_WRITE_COMBINE + int m_nLastWrittenField; + unsigned char* m_pLastWrittenAddress; +#endif +}; + +struct IndexDesc_t +{ + // Pointers to the index data + unsigned short *m_pIndices; + + // The offset in bytes of the memory we're writing into + // from the start of the D3D buffer (will be 0 for static meshes) + unsigned int m_nOffset; + + // The first index (used for buffered index buffers, or cards that don't support stream offset) + unsigned int m_nFirstIndex; + + // 1 if the device is active, 0 if the device isn't active. + // Faster than doing if checks for null m_pIndices if someone is + // trying to write the m_pIndices while the device is inactive. + unsigned int m_nIndexSize; +}; + + +//----------------------------------------------------------------------------- +// The Mesh memory descriptor +//----------------------------------------------------------------------------- +struct MeshDesc_t : public VertexDesc_t, public IndexDesc_t +{ +}; + + +//----------------------------------------------------------------------------- +// Standard vertex formats for models +//----------------------------------------------------------------------------- +struct ModelVertexDX8_t +{ + Vector m_vecPosition; + Vector m_vecNormal; + Vector2D m_vecTexCoord; + Vector4D m_vecUserData; +}; + +//--------------------------------------------------------------------------------------- +// Thin Vertex format for use with ATI tessellator in quad mode +//--------------------------------------------------------------------------------------- +struct QuadTessVertex_t +{ + Vector4D m_vTangent; // last component is Binormal flip and Wrinkle weight + Vector4D m_vUV01; // UV coordinates for points Interior (0), and Parametric V Edge (1) + Vector4D m_vUV23; // UV coordinates for points Parametric U Edge (2), and Corner (3) +}; + +struct MeshBoneRemap_t // see BoneStateChangeHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int m_nActualBoneIndex; + int m_nSrcBoneIndex; +}; + +struct MeshInstanceData_t +{ + int m_nIndexOffset; + int m_nIndexCount; + int m_nBoneCount; + MeshBoneRemap_t * m_pBoneRemap; // there are bone count of these, they index into pose to world + matrix3x4_t * m_pPoseToWorld; // transforms for the *entire* model, indexed into by m_pBoneIndex. Potentially more than bone count of these + const ITexture * m_pEnvCubemap; + MaterialLightingState_t *m_pLightingState; + MaterialPrimitiveType_t m_nPrimType; + const IVertexBuffer * m_pVertexBuffer; + int m_nVertexOffsetInBytes; + const IIndexBuffer * m_pIndexBuffer; + const IVertexBuffer * m_pColorBuffer; + int m_nColorVertexOffsetInBytes; + ShaderStencilState_t * m_pStencilState; + Vector4D m_DiffuseModulation; +}; + + +//----------------------------------------------------------------------------- +// Utility methods for buffer builders +//----------------------------------------------------------------------------- +inline float *OffsetFloatPointer( float *pBufferPointer, int nVertexCount, int vertexSize ) +{ + return reinterpret_cast( + reinterpret_cast(pBufferPointer) + + nVertexCount * vertexSize); +} + +inline const float *OffsetFloatPointer( const float *pBufferPointer, int nVertexCount, int vertexSize ) +{ + return reinterpret_cast( + reinterpret_cast(pBufferPointer) + + nVertexCount * vertexSize); +} + +inline void IncrementFloatPointer( float* &pBufferPointer, int vertexSize ) +{ + pBufferPointer = reinterpret_cast( reinterpret_cast( pBufferPointer ) + vertexSize ); +} + + +//----------------------------------------------------------------------------- +// Used in lists of indexed primitives. +//----------------------------------------------------------------------------- +class CPrimList +{ +public: + CPrimList(); + CPrimList( int nFirstIndex, int nIndexCount ); + + int m_FirstIndex; + int m_NumIndices; +}; + +inline CPrimList::CPrimList() +{ +} + +inline CPrimList::CPrimList( int nFirstIndex, int nIndexCount ) +{ + m_FirstIndex = nFirstIndex; + m_NumIndices = nIndexCount; +} + +abstract_class IVertexBuffer +{ +public: + // NOTE: The following two methods are only valid for static vertex buffers + // Returns the number of vertices and the format of the vertex buffer + virtual int VertexCount() const = 0; + virtual VertexFormat_t GetVertexFormat() const = 0; + + // Is this vertex buffer dynamic? + virtual bool IsDynamic() const = 0; + + // NOTE: For dynamic vertex buffers only! + // Casts the memory of the dynamic vertex buffer to the appropriate type + virtual void BeginCastBuffer( VertexFormat_t format ) = 0; + virtual void EndCastBuffer() = 0; + + // Returns the number of vertices that can still be written into the buffer + virtual int GetRoomRemaining() const = 0; + + virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) = 0; + virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) = 0; + + // Spews the mesh data + virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) = 0; + + // Call this in debug mode to make sure our data is good. + virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) = 0; +}; + +abstract_class IIndexBuffer +{ +public: + // NOTE: The following two methods are only valid for static index buffers + // Returns the number of indices and the format of the index buffer + virtual int IndexCount() const = 0; + virtual MaterialIndexFormat_t IndexFormat() const = 0; + + // Is this index buffer dynamic? + virtual bool IsDynamic() const = 0; + + // NOTE: For dynamic index buffers only! + // Casts the memory of the dynamic index buffer to the appropriate type + virtual void BeginCastBuffer( MaterialIndexFormat_t format ) = 0; + virtual void EndCastBuffer() = 0; + + // Returns the number of indices that can still be written into the buffer + virtual int GetRoomRemaining() const = 0; + + // Locks, unlocks the index buffer + virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ) = 0; + virtual void Unlock( int nWrittenIndexCount, IndexDesc_t &desc ) = 0; + + // FIXME: Remove this!! Here only for backward compat on IMesh + // Locks, unlocks the index buffer for modify + virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) = 0; + virtual void ModifyEnd( IndexDesc_t& desc ) = 0; + + // Spews the mesh data + virtual void Spew( int nIndexCount, const IndexDesc_t &desc ) = 0; + + // Ensures the data in the index buffer is valid + virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) = 0; + + // For backward compat to IMesh + virtual IMesh* GetMesh() = 0; +}; + + +//----------------------------------------------------------------------------- +// Interface to the mesh - needs to contain an IVertexBuffer and an IIndexBuffer to emulate old mesh behavior +//----------------------------------------------------------------------------- +abstract_class IMesh : public IVertexBuffer, public IIndexBuffer +{ +public: + // ----------------------------------- + + // Sets/gets the primitive type + virtual void SetPrimitiveType( MaterialPrimitiveType_t type ) = 0; + + // Draws the mesh + virtual void Draw( int nFirstIndex = -1, int nIndexCount = 0 ) = 0; + + virtual void SetColorMesh( IMesh *pColorMesh, int nVertexOffset ) = 0; + + // Draw a list of (lists of) primitives. Batching your lists together that use + // the same lightmap, material, vertex and index buffers with multipass shaders + // can drastically reduce state-switching overhead. + // NOTE: this only works with STATIC meshes. + virtual void Draw( CPrimList *pLists, int nLists ) = 0; + + // Copy verts and/or indices to a mesh builder. This only works for temp meshes! + virtual void CopyToMeshBuilder( + int iStartVert, // Which vertices to copy. + int nVerts, + int iStartIndex, // Which indices to copy. + int nIndices, + int indexOffset, // This is added to each index. + CMeshBuilder &builder ) = 0; + + // Spews the mesh data + virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &desc ) = 0; + + // Call this in debug mode to make sure our data is good. + virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &desc ) = 0; + + // New version + // Locks/unlocks the mesh, providing space for nVertexCount and nIndexCount. + // nIndexCount of -1 means don't lock the index buffer... + virtual void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ) = 0; + virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) = 0; + virtual void ModifyEnd( MeshDesc_t& desc ) = 0; + virtual void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ) = 0; + + virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t &desc ) = 0; + + virtual void SetFlexMesh( IMesh *pMesh, int nVertexOffset ) = 0; + + virtual void DisableFlexMesh() = 0; + + virtual void MarkAsDrawn() = 0; + + // NOTE: I chose to create this method strictly because it's 2 days to code lock + // and I could use the DrawInstances technique without a larger code change + // Draws the mesh w/ modulation. + virtual void DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex = -1, int nIndexCount = 0 ) = 0; + +#if defined( _X360 ) + virtual unsigned ComputeMemoryUsed() = 0; +#endif +}; + + +#include "meshreader.h" + +#define INVALID_BUFFER_OFFSET 0xFFFFFFFFUL + +// flags for advancevertex optimization +#define VTX_HAVEPOS 1 +#define VTX_HAVENORMAL 2 +#define VTX_HAVECOLOR 4 +#define VTX_HAVEALL ( VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR ) + + +//----------------------------------------------------------------------------- +// +// Helper class used to define vertex buffers +// +//----------------------------------------------------------------------------- +class CVertexBuilder : private VertexDesc_t +{ +public: + CVertexBuilder(); + CVertexBuilder( IVertexBuffer *pVertexBuffer, VertexFormat_t fmt = 0 ); + ~CVertexBuilder(); + + // Begins, ends modification of the index buffer (returns true if the lock succeeded) + // A lock may not succeed if append is set to true and there isn't enough room + // NOTE: Append is only used with dynamic index buffers; it's ignored for static buffers + bool Lock( int nMaxIndexCount, bool bAppend = false ); + void Unlock(); + + // Spews the current data + // NOTE: Can only be called during a lock/unlock block + void SpewData(); + + // Returns the number of indices we can fit into the buffer without needing to discard + int GetRoomRemaining() const; + + // Binds this vertex buffer + void Bind( IMatRenderContext *pContext, int nStreamID, VertexFormat_t usage = 0 ); + + // Returns the byte offset + int Offset() const; + + // This must be called before Begin, if a vertex buffer with a compressed format is to be used + void SetCompressionType( VertexCompressionType_t compressionType ); + void ValidateCompressionType(); + + void Begin( IVertexBuffer *pVertexBuffer, int nVertexCount, int *nFirstVertex ); + void Begin( IVertexBuffer *pVertexBuffer, int nVertexCount ); + + // Use this when you're done writing + // Set bDraw to true to call m_pMesh->Draw automatically. + void End( bool bSpewData = false ); + + // Locks the vertex buffer to modify existing data + // Passing nVertexCount == -1 says to lock all the vertices for modification. + void BeginModify( IVertexBuffer *pVertexBuffer, int nFirstVertex = 0, int nVertexCount = -1 ); + void EndModify( bool bSpewData = false ); + + // returns the number of vertices + int VertexCount() const; + + // Returns the total number of vertices across all Locks() + int TotalVertexCount() const; + + // Resets the mesh builder so it points to the start of everything again + void Reset(); + + // Returns the size of the vertex + int VertexSize() { return m_ActualVertexSize; } + + // returns the data size of a given texture coordinate + int TextureCoordinateSize( int nTexCoordNumber ) { return m_VertexSize_TexCoord[ nTexCoordNumber ]; } + + // Returns the base vertex memory pointer + void* BaseVertexData(); + + // Selects the nth Vertex and Index + void SelectVertex( int idx ); + + // Advances the current vertex and index by one + void AdvanceVertex( void ); + template void AdvanceVertexF( void ); + void AdvanceVertices( int nVerts ); + + int GetCurrentVertex() const; + int GetFirstVertex() const; + + // Data retrieval... + const float *Position() const; + + const float *Normal() const; + + unsigned int Color() const; + + unsigned char *Specular() const; + + const float *TexCoord( int stage ) const; + + const float *TangentS() const; + const float *TangentT() const; + + const float *BoneWeight() const; + float Wrinkle() const; + + int NumBoneWeights() const; +#ifndef NEW_SKINNING + unsigned char *BoneMatrix() const; +#else + float *BoneMatrix() const; +#endif + + // position setting + void Position3f( float x, float y, float z ); + void Position3fv( const float *v ); + + // normal setting + void Normal3f( float nx, float ny, float nz ); + void Normal3fv( const float *n ); + void NormalDelta3fv( const float *n ); + void NormalDelta3f( float nx, float ny, float nz ); + // normal setting (templatized for code which needs to support compressed vertices) + template void CompressedNormal3f( float nx, float ny, float nz ); + template void CompressedNormal3fv( const float *n ); + + // color setting + void Color3f( float r, float g, float b ); + void Color3fv( const float *rgb ); + void Color4f( float r, float g, float b, float a ); + void Color4fv( const float *rgba ); + + // Faster versions of color + void Color3ub( unsigned char r, unsigned char g, unsigned char b ); + void Color3ubv( unsigned char const* rgb ); + void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Color4ubv( unsigned char const* rgba ); + void Color4Packed( int packedColor ); + int PackColor4( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + + // specular color setting + void Specular3f( float r, float g, float b ); + void Specular3fv( const float *rgb ); + void Specular4f( float r, float g, float b, float a ); + void Specular4fv( const float *rgba ); + + // Faster version of specular + void Specular3ub( unsigned char r, unsigned char g, unsigned char b ); + void Specular3ubv( unsigned char const *c ); + void Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Specular4ubv( unsigned char const *c ); + + // texture coordinate setting + void TexCoord1f( int stage, float s ); + void TexCoord2f( int stage, float s, float t ); + void TexCoord2fv( int stage, const float *st ); + void TexCoord3f( int stage, float s, float t, float u ); + void TexCoord3fv( int stage, const float *stu ); + void TexCoord4f( int stage, float s, float t, float u, float w ); + void TexCoord4fv( int stage, const float *stuv ); + + void TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ); + void TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ); + + // tangent space + void TangentS3f( float sx, float sy, float sz ); + void TangentS3fv( const float* s ); + + void TangentT3f( float tx, float ty, float tz ); + void TangentT3fv( const float* t ); + + // Wrinkle + void Wrinkle1f( float flWrinkle ); + + // bone weights + void BoneWeight( int idx, float weight ); + void BoneWeights2( float weight1, float weight2 ); + + // bone weights (templatized for code which needs to support compressed vertices) + template void CompressedBoneWeight3fv( const float * pWeights ); + + // bone matrix index + void BoneMatrix( int idx, int matrixIndex ); + void BoneMatrices4( int matrixIdx0, int matrixIdx1, int matrixIdx2, int matrixIdx3 ); + + // Generic per-vertex data + void UserData( const float* pData ); + // Generic per-vertex data (templatized for code which needs to support compressed vertices) + template void CompressedUserData( const float* pData ); + + // Fast Vertex! No need to call advance vertex, and no random access allowed. + // WARNING - these are low level functions that are intended only for use + // in the software vertex skinner. + void FastVertex( const ModelVertexDX8_t &vertex ); + void FastVertexSSE( const ModelVertexDX8_t &vertex ); + void FastQuadVertexSSE( const QuadTessVertex_t &vertex ); + + // Add number of verts and current vert since FastVertex routines do not update. + void FastAdvanceNVertices( int n ); + +#if defined( _X360 ) + void VertexDX8ToX360( const ModelVertexDX8_t &vertex ); +#endif + + // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. + void AttachBegin( IMesh* pMesh, int nMaxVertexCount, const MeshDesc_t &desc ); + void AttachEnd(); + void AttachBeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, const MeshDesc_t &desc ); + void AttachEndModify(); + +private: + // The vertex buffer we're modifying + IVertexBuffer *m_pVertexBuffer; + + // Used to make sure Begin/End calls and BeginModify/EndModify calls match. + bool m_bModify; + + // Max number of indices and vertices + int m_nMaxVertexCount; + + // Number of indices and vertices + int m_nVertexCount; + + // The current vertex and index + mutable int m_nCurrentVertex; + + // Optimization: Pointer to the current pos, norm, texcoord, and color + mutable float *m_pCurrPosition; + mutable float *m_pCurrNormal; + mutable unsigned char *m_pCurrColor; + mutable float *m_pCurrTexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; + + // Total number of vertices appended + int m_nTotalVertexCount; + + // First vertex buffer offset + index + unsigned int m_nBufferOffset; + unsigned int m_nBufferFirstVertex; + +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) + // Debug checks to make sure we write userdata4/tangents AFTER normals + bool m_bWrittenNormal : 1; + bool m_bWrittenUserData : 1; +#endif + + friend class CMeshBuilder; +}; + + +//----------------------------------------------------------------------------- +// +// Inline methods of CVertexBuilder +// +//----------------------------------------------------------------------------- +inline CVertexBuilder::CVertexBuilder() +{ + m_pVertexBuffer = NULL; + m_nBufferOffset = INVALID_BUFFER_OFFSET; + m_nBufferFirstVertex = 0; + m_nVertexCount = 0; + m_nCurrentVertex = 0; + m_nMaxVertexCount = 0; + m_nTotalVertexCount = 0; + m_CompressionType = VERTEX_COMPRESSION_INVALID; + +#ifdef _DEBUG + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + m_bModify = false; +#endif +} + +inline CVertexBuilder::CVertexBuilder( IVertexBuffer *pVertexBuffer, VertexFormat_t fmt ) +{ + m_pVertexBuffer = pVertexBuffer; + m_nBufferOffset = INVALID_BUFFER_OFFSET; + m_nBufferFirstVertex = 0; + m_nVertexCount = 0; + m_nCurrentVertex = 0; + m_nMaxVertexCount = 0; + m_nTotalVertexCount = 0; + m_CompressionType = VERTEX_COMPRESSION_INVALID; + + if ( m_pVertexBuffer->IsDynamic() ) + { + m_pVertexBuffer->BeginCastBuffer( fmt ); + } + else + { + Assert( m_pVertexBuffer->GetVertexFormat() == fmt ); + } + +#ifdef _DEBUG + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + m_bModify = false; +#endif +} + +inline CVertexBuilder::~CVertexBuilder() +{ + if ( m_pVertexBuffer && m_pVertexBuffer->IsDynamic() ) + { + m_pVertexBuffer->EndCastBuffer(); + } +} + +//----------------------------------------------------------------------------- +// Begins, ends modification of the index buffer +//----------------------------------------------------------------------------- +inline bool CVertexBuilder::Lock( int nMaxVertexCount, bool bAppend ) +{ + Assert( m_pVertexBuffer ); + m_bModify = false; + m_nMaxVertexCount = nMaxVertexCount; + bool bFirstLock = ( m_nBufferOffset == INVALID_BUFFER_OFFSET ); + if ( bFirstLock ) + { + bAppend = false; + } + if ( !bAppend ) + { + m_nTotalVertexCount = 0; + } + + // Lock the vertex buffer + if ( !m_pVertexBuffer->Lock( m_nMaxVertexCount, bAppend, *this ) ) + { + m_nMaxVertexCount = 0; + return false; + } + + Reset(); + + if ( bFirstLock ) + { + m_nBufferOffset = m_nOffset; + m_nBufferFirstVertex = m_nFirstVertex; + } + + return true; +} + +inline void CVertexBuilder::Unlock() +{ + Assert( !m_bModify && m_pVertexBuffer ); + +#ifdef _DEBUG + m_pVertexBuffer->ValidateData( m_nVertexCount, *this ); +#endif + + m_pVertexBuffer->Unlock( m_nVertexCount, *this ); + m_nTotalVertexCount += m_nVertexCount; + + m_nMaxVertexCount = 0; + +#ifdef _DEBUG + // Null out our data... + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); +#endif +} + +inline void CVertexBuilder::SpewData() +{ + m_pVertexBuffer->Spew( m_nVertexCount, *this ); +} + + +//----------------------------------------------------------------------------- +// Binds this vertex buffer +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Bind( IMatRenderContext *pContext, int nStreamID, VertexFormat_t usage ) +{ + if ( m_pVertexBuffer && ( m_nBufferOffset != INVALID_BUFFER_OFFSET ) ) + { + pContext->BindVertexBuffer( nStreamID, m_pVertexBuffer, m_nBufferOffset, + m_nFirstVertex, m_nTotalVertexCount, usage ? usage : m_pVertexBuffer->GetVertexFormat() ); + } + else + { + pContext->BindVertexBuffer( nStreamID, NULL, 0, 0, 0, 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Returns the byte offset +//----------------------------------------------------------------------------- +inline int CVertexBuilder::Offset() const +{ + return m_nBufferOffset; +} + +inline int CVertexBuilder::GetFirstVertex() const +{ + return m_nBufferFirstVertex; +} + +//----------------------------------------------------------------------------- +// Specify the type of vertex compression that this CMeshBuilder will perform +//----------------------------------------------------------------------------- +inline void CVertexBuilder::SetCompressionType( VertexCompressionType_t compressionType ) +{ + // The real purpose of this method is to allow us to emit a Warning in Begin() + m_CompressionType = compressionType; +} + +inline void CVertexBuilder::ValidateCompressionType() +{ +#ifdef _DEBUG + VertexCompressionType_t vbCompressionType = CompressionType( m_pVertexBuffer->GetVertexFormat() ); + if ( vbCompressionType != VERTEX_COMPRESSION_NONE ) + { + Assert( m_CompressionType == vbCompressionType ); + if ( m_CompressionType != vbCompressionType ) + { + Warning( "ERROR: CVertexBuilder::SetCompressionType() must be called to specify the same vertex compression type (%s) as the vertex buffer being modified." + "Junk vertices will be rendered, or there will be a crash in CVertexBuilder!\n", + vbCompressionType == VERTEX_COMPRESSION_ON ? "VERTEX_COMPRESSION_ON" : "VERTEX_COMPRESSION_NONE" ); + } + // Never use vertex compression for dynamic VBs (the conversions can really hurt perf) + Assert( !m_pVertexBuffer->IsDynamic() ); + } +#endif +} + +inline void CVertexBuilder::Begin( IVertexBuffer *pVertexBuffer, int nVertexCount ) +{ + Assert( pVertexBuffer && (!m_pVertexBuffer) ); + + m_pVertexBuffer = pVertexBuffer; + m_bModify = false; + + m_nMaxVertexCount = nVertexCount; + m_nVertexCount = 0; + + // Make sure SetCompressionType was called correctly, if this VB is compressed + ValidateCompressionType(); + + // Lock the vertex and index buffer + m_pVertexBuffer->Lock( m_nMaxVertexCount, false, *this ); + + // Point to the start of the buffers.. + Reset(); +} + + +//----------------------------------------------------------------------------- +// Use this when you're done modifying the mesh +//----------------------------------------------------------------------------- +inline void CVertexBuilder::End( bool bSpewData ) +{ + // Make sure they called Begin() + Assert( !m_bModify ); + + if ( bSpewData ) + { + m_pVertexBuffer->Spew( m_nVertexCount, *this ); + } + +#ifdef _DEBUG + m_pVertexBuffer->ValidateData( m_nVertexCount, *this ); +#endif + + // Unlock our buffers + m_pVertexBuffer->Unlock( m_nVertexCount, *this ); + + m_pVertexBuffer = 0; + m_nMaxVertexCount = 0; + + m_CompressionType = VERTEX_COMPRESSION_INVALID; + +#ifdef _DEBUG + // Null out our pointers... + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + memset( static_cast< VertexDesc_t* >( this ), 0, sizeof(VertexDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. +//----------------------------------------------------------------------------- +inline void CVertexBuilder::AttachBegin( IMesh* pMesh, int nMaxVertexCount, const MeshDesc_t &desc ) +{ + m_pVertexBuffer = pMesh; + memcpy( static_cast( this ), static_cast( &desc ), sizeof(VertexDesc_t) ); + m_nMaxVertexCount = nMaxVertexCount; + m_NumBoneWeights = m_NumBoneWeights == 0 ? 0 : 2; // Two weights if any + m_nVertexCount = 0; + m_bModify = false; + + // Make sure SetCompressionType was called correctly, if this VB is compressed + ValidateCompressionType(); + + if ( m_nBufferOffset == INVALID_BUFFER_OFFSET ) + { + m_nTotalVertexCount = 0; + m_nBufferOffset = static_cast< const VertexDesc_t* >( &desc )->m_nOffset; + m_nBufferFirstVertex = desc.m_nFirstVertex; + } +} + +inline void CVertexBuilder::AttachEnd() +{ + // Make sure they called Begin() + Assert( !m_bModify ); + + m_nMaxVertexCount = 0; + m_pVertexBuffer = NULL; + + m_CompressionType = VERTEX_COMPRESSION_INVALID; + +#ifdef _DEBUG + // Null out our pointers... + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); +#endif +} + +inline void CVertexBuilder::AttachBeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, const MeshDesc_t &desc ) +{ + Assert( pMesh && (!m_pVertexBuffer) ); + + m_pVertexBuffer = pMesh; + memcpy( static_cast( this ), static_cast( &desc ), sizeof(VertexDesc_t) ); + m_nMaxVertexCount = m_nVertexCount = nVertexCount; + m_NumBoneWeights = m_NumBoneWeights == 0 ? 0 : 2; // Two weights if any + m_bModify = true; + + // Make sure SetCompressionType was called correctly, if this VB is compressed + ValidateCompressionType(); +} + +inline void CVertexBuilder::AttachEndModify() +{ + Assert( m_pVertexBuffer ); + Assert( m_bModify ); // Make sure they called BeginModify. + + m_pVertexBuffer = 0; + m_nMaxVertexCount = 0; + + m_CompressionType = VERTEX_COMPRESSION_INVALID; + +#ifdef _DEBUG + // Null out our pointers... + m_pCurrPosition = NULL; + m_pCurrNormal = NULL; + m_pCurrColor = NULL; + memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); + memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Computes the first min non-null address +//----------------------------------------------------------------------------- +inline unsigned char* FindMinAddress( void *pAddress1, void *pAddress2, int nAddress2Size ) +{ + if ( nAddress2Size == 0 ) + return (unsigned char*)pAddress1; + if ( !pAddress1 ) + return (unsigned char*)pAddress2; + return ( pAddress1 < pAddress2 ) ? (unsigned char*)pAddress1 : (unsigned char*)pAddress2; +} + +//----------------------------------------------------------------------------- +// Resets the vertex buffer builder so it points to the start of everything again +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Reset() +{ + m_nCurrentVertex = 0; + + m_pCurrPosition = m_pPosition; + m_pCurrNormal = m_pNormal; + for ( int i = 0; i < NELEMS( m_pCurrTexCoord ); i++ ) + { + m_pCurrTexCoord[i] = m_pTexCoord[i]; + } + m_pCurrColor = m_pColor; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif + +#ifdef DEBUG_WRITE_COMBINE + // Logic for m_pLastWrittenAddress is tricky. It really wants the min of the + // non-null address pointers. + m_nLastWrittenField = MB_FIELD_NONE; + m_pLastWrittenAddress = NULL; + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pPosition, m_VertexSize_Position ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pBoneWeight, m_VertexSize_BoneWeight ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pBoneMatrixIndex, m_VertexSize_BoneMatrixIndex ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pNormal, m_VertexSize_Normal ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pColor, m_VertexSize_Color ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pSpecular, m_VertexSize_Specular ); + for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) + { + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTexCoord[i], m_VertexSize_TexCoord[i] ); + } + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTangentS, m_VertexSize_TangentS ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTangentT, m_VertexSize_TangentT ); + m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pUserData, m_VertexSize_UserData ); +#endif +} + + +//----------------------------------------------------------------------------- +// returns the number of vertices +//----------------------------------------------------------------------------- +inline int CVertexBuilder::VertexCount() const +{ + return m_nVertexCount; +} + + +//----------------------------------------------------------------------------- +// Returns the total number of vertices across all Locks() +//----------------------------------------------------------------------------- +inline int CVertexBuilder::TotalVertexCount() const +{ + return m_nTotalVertexCount; +} + + +//----------------------------------------------------------------------------- +// Returns the base vertex memory pointer +//----------------------------------------------------------------------------- +inline void* CVertexBuilder::BaseVertexData() +{ + // FIXME: If there's no position specified, we need to find + // the base address + Assert( m_pPosition ); + return m_pPosition; +} + + +//----------------------------------------------------------------------------- +// Selects the current vertex +//----------------------------------------------------------------------------- +inline void CVertexBuilder::SelectVertex( int nIndex ) +{ + // NOTE: This index is expected to be relative + Assert( (nIndex >= 0) && (nIndex < m_nMaxVertexCount) ); + m_nCurrentVertex = nIndex; + + m_pCurrPosition = OffsetFloatPointer( m_pPosition, m_nCurrentVertex, m_VertexSize_Position ); + m_pCurrNormal = OffsetFloatPointer( m_pNormal, m_nCurrentVertex, m_VertexSize_Normal ); + + COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); + m_pCurrTexCoord[0] = OffsetFloatPointer( m_pTexCoord[0], m_nCurrentVertex, m_VertexSize_TexCoord[0] ); + m_pCurrTexCoord[1] = OffsetFloatPointer( m_pTexCoord[1], m_nCurrentVertex, m_VertexSize_TexCoord[1] ); + m_pCurrTexCoord[2] = OffsetFloatPointer( m_pTexCoord[2], m_nCurrentVertex, m_VertexSize_TexCoord[2] ); + m_pCurrTexCoord[3] = OffsetFloatPointer( m_pTexCoord[3], m_nCurrentVertex, m_VertexSize_TexCoord[3] ); + m_pCurrTexCoord[4] = OffsetFloatPointer( m_pTexCoord[4], m_nCurrentVertex, m_VertexSize_TexCoord[4] ); + m_pCurrTexCoord[5] = OffsetFloatPointer( m_pTexCoord[5], m_nCurrentVertex, m_VertexSize_TexCoord[5] ); + m_pCurrTexCoord[6] = OffsetFloatPointer( m_pTexCoord[6], m_nCurrentVertex, m_VertexSize_TexCoord[6] ); + m_pCurrTexCoord[7] = OffsetFloatPointer( m_pTexCoord[7], m_nCurrentVertex, m_VertexSize_TexCoord[7] ); + m_pCurrColor = m_pColor + m_nCurrentVertex * m_VertexSize_Color; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + + +//----------------------------------------------------------------------------- +// Advances vertex after you're done writing to it. +//----------------------------------------------------------------------------- + +template FORCEINLINE void CVertexBuilder::AdvanceVertexF() +{ + if ( ++m_nCurrentVertex > m_nVertexCount ) + { + m_nVertexCount = m_nCurrentVertex; + } + + if ( nFlags & VTX_HAVEPOS ) + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); + if ( nFlags & VTX_HAVENORMAL ) + IncrementFloatPointer( m_pCurrNormal, m_VertexSize_Normal ); + if ( nFlags & VTX_HAVECOLOR ) + m_pCurrColor += m_VertexSize_Color; + + COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); + if ( nNumTexCoords > 0 ) + IncrementFloatPointer( m_pCurrTexCoord[0], m_VertexSize_TexCoord[0] ); + if ( nNumTexCoords > 1 ) + IncrementFloatPointer( m_pCurrTexCoord[1], m_VertexSize_TexCoord[1] ); + if ( nNumTexCoords > 2 ) + IncrementFloatPointer( m_pCurrTexCoord[2], m_VertexSize_TexCoord[2] ); + if ( nNumTexCoords > 3 ) + IncrementFloatPointer( m_pCurrTexCoord[3], m_VertexSize_TexCoord[3] ); + if ( nNumTexCoords > 4 ) + IncrementFloatPointer( m_pCurrTexCoord[4], m_VertexSize_TexCoord[4] ); + if ( nNumTexCoords > 5 ) + IncrementFloatPointer( m_pCurrTexCoord[5], m_VertexSize_TexCoord[5] ); + if ( nNumTexCoords > 6 ) + IncrementFloatPointer( m_pCurrTexCoord[6], m_VertexSize_TexCoord[6] ); + if ( nNumTexCoords > 7 ) + IncrementFloatPointer( m_pCurrTexCoord[7], m_VertexSize_TexCoord[7] ); + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + +inline void CVertexBuilder::AdvanceVertex() +{ + AdvanceVertexF(); +} + + +inline void CVertexBuilder::AdvanceVertices( int nVerts ) +{ + m_nCurrentVertex += nVerts; + if ( m_nCurrentVertex > m_nVertexCount ) + { + m_nVertexCount = m_nCurrentVertex; + } + + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position*nVerts ); + IncrementFloatPointer( m_pCurrNormal, m_VertexSize_Normal*nVerts ); + + COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); + IncrementFloatPointer( m_pCurrTexCoord[0], m_VertexSize_TexCoord[0]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[1], m_VertexSize_TexCoord[1]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[2], m_VertexSize_TexCoord[2]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[3], m_VertexSize_TexCoord[3]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[4], m_VertexSize_TexCoord[4]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[5], m_VertexSize_TexCoord[5]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[6], m_VertexSize_TexCoord[6]*nVerts ); + IncrementFloatPointer( m_pCurrTexCoord[7], m_VertexSize_TexCoord[7]*nVerts ); + m_pCurrColor += m_VertexSize_Color*nVerts; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + + +//----------------------------------------------------------------------------- +// For use with the FastVertex methods, advances the current vertex by N +//----------------------------------------------------------------------------- +inline void CVertexBuilder::FastAdvanceNVertices( int n ) +{ + m_nCurrentVertex += n; + m_nVertexCount = m_nCurrentVertex; +} + +inline void CVertexBuilder::FastVertex( const ModelVertexDX8_t &vertex ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + +#if defined( _WIN32 ) && !defined( _X360 ) + const void *pRead = &vertex; + void *pCurrPos = m_pCurrPosition; + __asm + { + mov esi, pRead + mov edi, pCurrPos + + movq mm0, [esi + 0] + movq mm1, [esi + 8] + movq mm2, [esi + 16] + movq mm3, [esi + 24] + movq mm4, [esi + 32] + movq mm5, [esi + 40] + + movntq [edi + 0], mm0 + movntq [edi + 8], mm1 + movntq [edi + 16], mm2 + movntq [edi + 24], mm3 + movntq [edi + 32], mm4 + movntq [edi + 40], mm5 + + emms + } +#elif defined(GNUC) + const void *pRead = &vertex; + void *pCurrPos = m_pCurrPosition; + __asm__ __volatile__ ( + "movq (%0), %%mm0\n" + "movq 8(%0), %%mm1\n" + "movq 16(%0), %%mm2\n" + "movq 24(%0), %%mm3\n" + "movq 32(%0), %%mm4\n" + "movq 40(%0), %%mm5\n" + "movq 48(%0), %%mm6\n" + "movq 56(%0), %%mm7\n" + "movntq %%mm0, (%1)\n" + "movntq %%mm1, 8(%1)\n" + "movntq %%mm2, 16(%1)\n" + "movntq %%mm3, 24(%1)\n" + "movntq %%mm4, 32(%1)\n" + "movntq %%mm5, 40(%1)\n" + "movntq %%mm6, 48(%1)\n" + "movntq %%mm7, 56(%1)\n" + "emms\n" + :: "r" (pRead), "r" (pCurrPos) : "memory"); +#else + Error( "Implement CMeshBuilder::FastVertex(dx8)" ); +#endif + + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); + // m_nVertexCount = ++m_nCurrentVertex; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + +inline void CVertexBuilder::FastVertexSSE( const ModelVertexDX8_t &vertex ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + +#if defined( _WIN32 ) && !defined( _X360 ) + const void *pRead = &vertex; + void *pCurrPos = m_pCurrPosition; + __asm + { + mov esi, pRead + mov edi, pCurrPos + + movaps xmm0, [esi + 0] + movaps xmm1, [esi + 16] + movaps xmm2, [esi + 32] + + movntps [edi + 0], xmm0 + movntps [edi + 16], xmm1 + movntps [edi + 32], xmm2 + } +#elif defined(GNUC) + const void *pRead = &vertex; + void *pCurrPos = m_pCurrPosition; + __asm__ __volatile__ ( + "movaps (%0), %%xmm0\n" + "movaps 16(%0), %%xmm1\n" + "movaps 32(%0), %%xmm2\n" + "movaps 48(%0), %%xmm3\n" + "movntps %%xmm0, (%1)\n" + "movntps %%xmm1, 16(%1)\n" + "movntps %%xmm2, 32(%1)\n" + "movntps %%xmm3, 48(%1)\n" + :: "r" (pRead), "r" (pCurrPos) : "memory"); +#else + Error( "Implement CMeshBuilder::FastVertexSSE((dx8)" ); +#endif + + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); + // m_nVertexCount = ++m_nCurrentVertex; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + + +inline void CVertexBuilder::FastQuadVertexSSE( const QuadTessVertex_t &vertex ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + +#if defined( _WIN32 ) && !defined( _X360 ) + const void *pRead = &vertex; + void *pCurrPos = m_pCurrPosition; + __asm + { + mov esi, pRead + mov edi, pCurrPos + + movaps xmm0, [esi + 0] + movaps xmm1, [esi + 16] + movaps xmm2, [esi + 32] + + movntps [edi + 0], xmm0 + movntps [edi + 16], xmm1 + movntps [edi + 32], xmm2 + } +#endif + + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); + // m_nVertexCount = ++m_nCurrentVertex; + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} + + + +//----------------------------------------------------------------------------- +// Returns the current vertex +//----------------------------------------------------------------------------- +inline int CVertexBuilder::GetCurrentVertex() const +{ + return m_nCurrentVertex; +} + + +//----------------------------------------------------------------------------- +// Copies a vertex into the x360 format +//----------------------------------------------------------------------------- +#if defined( _X360 ) +inline void CVertexBuilder::VertexDX8ToX360( const ModelVertexDX8_t &vertex ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + + // get the start of the data + unsigned char *pDst = (unsigned char*)m_pCurrPosition; + + Assert( m_VertexSize_Position > 0 ); // Assume position is always present + Assert( GetVertexElementSize( VERTEX_ELEMENT_POSITION, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecPosition ) ); + memcpy( pDst, vertex.m_vecPosition.Base(), sizeof( vertex.m_vecPosition ) ); + pDst += sizeof( vertex.m_vecPosition ); + + if ( m_VertexSize_Normal ) + { + Assert( GetVertexElementSize( VERTEX_ELEMENT_NORMAL, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecNormal ) ); + memcpy( pDst, vertex.m_vecNormal.Base(), sizeof( vertex.m_vecNormal ) ); + pDst += sizeof( vertex.m_vecNormal ); + } + + if ( m_VertexSize_TexCoord[0] ) + { + Assert( GetVertexElementSize( VERTEX_ELEMENT_TEXCOORD2D_0, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecTexCoord ) ); + memcpy( pDst, vertex.m_vecTexCoord.Base(), sizeof( vertex.m_vecTexCoord ) ); + pDst += sizeof( vertex.m_vecTexCoord ); + } + + if ( m_VertexSize_UserData ) + { + Assert( GetVertexElementSize( VERTEX_ELEMENT_USERDATA4, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecUserData ) ); + memcpy( pDst, vertex.m_vecUserData.Base(), sizeof( vertex.m_vecUserData ) ); + pDst += sizeof( vertex.m_vecUserData ); + } + + // ensure code is synced with the mesh builder that established the offsets + Assert( pDst - (unsigned char*)m_pCurrPosition == m_VertexSize_Position ); + + IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); + +#if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) + m_bWrittenNormal = false; + m_bWrittenUserData = false; +#endif +} +#endif + + +//----------------------------------------------------------------------------- +// Data retrieval... +//----------------------------------------------------------------------------- +inline const float* CVertexBuilder::Position() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pCurrPosition; +} + +inline const float* CVertexBuilder::Normal() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pCurrNormal; +} + +inline unsigned int CVertexBuilder::Color() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + // Swizzle it so it returns the same format as accepted by Color4ubv - rgba + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + unsigned int color; + if ( IsPC() || !IsX360() ) + { + color = (m_pCurrColor[3] << 24) | (m_pCurrColor[0] << 16) | (m_pCurrColor[1] << 8) | (m_pCurrColor[2]); + } + else + { + // in memory as argb, back to rgba + color = (m_pCurrColor[1] << 24) | (m_pCurrColor[2] << 16) | (m_pCurrColor[3] << 8) | (m_pCurrColor[0]); + } + return color; +} + +inline unsigned char *CVertexBuilder::Specular() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pSpecular + m_nCurrentVertex * m_VertexSize_Specular; +} + +inline const float* CVertexBuilder::TexCoord( int stage ) const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pCurrTexCoord[stage]; +} + +inline const float* CVertexBuilder::TangentS() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); +} + +inline const float* CVertexBuilder::TangentT() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); +} + +inline float CVertexBuilder::Wrinkle() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return *OffsetFloatPointer( m_pWrinkle, m_nCurrentVertex, m_VertexSize_Wrinkle ); +} + +inline const float* CVertexBuilder::BoneWeight() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); +} + +inline int CVertexBuilder::NumBoneWeights() const +{ + return m_NumBoneWeights; +} + +#ifndef NEW_SKINNING +inline unsigned char* CVertexBuilder::BoneMatrix() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pBoneMatrixIndex + m_nCurrentVertex * m_VertexSize_BoneMatrixIndex; +} +#else +inline float* CVertexBuilder::BoneMatrix() const +{ + // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) + // for code that needs to access compressed data (and/or a return-by-value templatized accessor) + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); + Assert( m_nCurrentVertex < m_nMaxVertexCount ); + return m_pBoneMatrixIndex + m_nCurrentVertex * m_VertexSize_BoneMatrixIndex; +} +#endif + + +//----------------------------------------------------------------------------- +// Position setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Position3f( float x, float y, float z ) +{ + Assert( m_pPosition && m_pCurrPosition ); + Assert( IsFinite(x) && IsFinite(y) && IsFinite(z) ); + float *pDst = m_pCurrPosition; + *pDst++ = x; + *pDst++ = y; + *pDst = z; +} + +inline void CVertexBuilder::Position3fv( const float *v ) +{ + Assert(v); + Assert( m_pPosition && m_pCurrPosition ); + + float *pDst = m_pCurrPosition; + *pDst++ = *v++; + *pDst++ = *v++; + *pDst = *v; +} + + +//----------------------------------------------------------------------------- +// Normal setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Normal3f( float nx, float ny, float nz ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression + Assert( m_pNormal ); + Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); + Assert( nx >= -1.05f && nx <= 1.05f ); + Assert( ny >= -1.05f && ny <= 1.05f ); + Assert( nz >= -1.05f && nz <= 1.05f ); + + float *pDst = m_pCurrNormal; + *pDst++ = nx; + *pDst++ = ny; + *pDst = nz; +} + +inline void CVertexBuilder::Normal3fv( const float *n ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression + Assert( n ); + Assert( m_pNormal && m_pCurrNormal ); + Assert( IsFinite(n[0]) && IsFinite(n[1]) && IsFinite(n[2]) ); + Assert( n[0] >= -1.05f && n[0] <= 1.05f ); + Assert( n[1] >= -1.05f && n[1] <= 1.05f ); + Assert( n[2] >= -1.05f && n[2] <= 1.05f ); + + float *pDst = m_pCurrNormal; + *pDst++ = *n++; + *pDst++ = *n++; + *pDst = *n; +} + +inline void CVertexBuilder::NormalDelta3f( float nx, float ny, float nz ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression + Assert( m_pNormal ); + Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); + + float *pDst = m_pCurrNormal; + *pDst++ = nx; + *pDst++ = ny; + *pDst = nz; +} + +inline void CVertexBuilder::NormalDelta3fv( const float *n ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression + Assert( n ); + Assert( m_pNormal && m_pCurrNormal ); + Assert( IsFinite(n[0]) && IsFinite(n[1]) && IsFinite(n[2]) ); + + float *pDst = m_pCurrNormal; + *pDst++ = *n++; + *pDst++ = *n++; + *pDst = *n; +} + +//----------------------------------------------------------------------------- +// Templatized normal setting methods which support compressed vertices +//----------------------------------------------------------------------------- +template inline void CVertexBuilder::CompressedNormal3f( float nx, float ny, float nz ) +{ + Assert( T == m_CompressionType ); + Assert( m_pNormal && m_pCurrNormal ); + Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); + Assert( nx >= -1.05f && nx <= 1.05f ); + Assert( ny >= -1.05f && ny <= 1.05f ); + Assert( nz >= -1.05f && nz <= 1.05f ); + // FIXME: studiorender is passing in non-unit normals + //float lengthSqd = nx*nx + ny*ny + nz*nz; + //Assert( lengthSqd >= 0.95f && lengthSqd <= 1.05f ); + + if ( T == VERTEX_COMPRESSION_ON ) + { +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) + PackNormal_SHORT2( nx, ny, nz, (unsigned int *)m_pCurrNormal ); + +#else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) + // NOTE: write the normal into the lower 16 bits of a word, clearing the top 16 bits - a userdata4 + // tangent must be written into the upper 16 bits by CompressedUserData() *AFTER* this. +#ifdef _DEBUG + Assert( m_bWrittenUserData == false ); + m_bWrittenNormal = true; +#endif + PackNormal_UBYTE4( nx, ny, nz, (unsigned int *)m_pCurrNormal ); +#endif + } + else + { + float *pDst = m_pCurrNormal; + *pDst++ = nx; + *pDst++ = ny; + *pDst = nz; + } +} + +template inline void CVertexBuilder::CompressedNormal3fv( const float *n ) +{ + Assert( n ); + CompressedNormal3f( n[0], n[1], n[2] ); +} + + +//----------------------------------------------------------------------------- +// Color setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Color3f( float r, float g, float b ) +{ + Assert( m_pColor && m_pCurrColor ); + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); + + int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | 0xFF000000; + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color3fv( const float *rgb ) +{ + Assert(rgb); + Assert( m_pColor && m_pCurrColor ); + Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) ); + Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) ); + Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) ); + + int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | 0xFF000000; + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color4f( float r, float g, float b, float a ) +{ + Assert( m_pColor && m_pCurrColor ); + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) && IsFinite(a) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) && (a >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) && (a <= 1.0) ); + + int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | (FastFToC(a) << 24); + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color4fv( const float *rgba ) +{ + Assert(rgba); + Assert( m_pColor && m_pCurrColor ); + Assert( IsFinite(rgba[0]) && IsFinite(rgba[1]) && IsFinite(rgba[2]) && IsFinite(rgba[3]) ); + Assert( (rgba[0] >= 0.0) && (rgba[1] >= 0.0) && (rgba[2] >= 0.0) && (rgba[3] >= 0.0) ); + Assert( (rgba[0] <= 1.0) && (rgba[1] <= 1.0) && (rgba[2] <= 1.0) && (rgba[3] <= 1.0) ); + + int col = (FastFToC(rgba[2])) | (FastFToC(rgba[1]) << 8) | (FastFToC(rgba[0]) << 16) | (FastFToC(rgba[3]) << 24); + *(int*)m_pCurrColor = col; +} + + +//----------------------------------------------------------------------------- +// Faster versions of color +//----------------------------------------------------------------------------- + +// note that on the OSX target (OpenGL) whenever there is vertex data being written as bytes - they need to be written in R,G,B,A memory order + +inline void CVertexBuilder::Color3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + Assert( m_pColor && m_pCurrColor ); + #ifdef OPENGL_SWAP_COLORS + int col = r | (g << 8) | (b << 16) | 0xFF000000; // r, g, b, a in memory + #else + int col = b | (g << 8) | (r << 16) | 0xFF000000; + #endif + + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color3ubv( unsigned char const* rgb ) +{ + Assert(rgb); + Assert( m_pColor && m_pCurrColor ); + #ifdef OPENGL_SWAP_COLORS + int col = rgb[0] | (rgb[1] << 8) | (rgb[2] << 16) | 0xFF000000; // r, g, b, a in memory + #else + int col = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16) | 0xFF000000; + #endif + + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + Assert( m_pColor && m_pCurrColor ); + #ifdef OPENGL_SWAP_COLORS + int col = r | (g << 8) | (b << 16) | (a << 24); // r, g, b, a in memory + #else + int col = b | (g << 8) | (r << 16) | (a << 24); + #endif + + *(int*)m_pCurrColor = col; +} + +inline void CVertexBuilder::Color4ubv( unsigned char const* rgba ) +{ + Assert( rgba ); + Assert( m_pColor && m_pCurrColor ); + #ifdef OPENGL_SWAP_COLORS + int col = rgba[0] | (rgba[1] << 8) | (rgba[2] << 16) | (rgba[3] << 24); // r, g, b, a in memory + #else + int col = rgba[2] | (rgba[1] << 8) | (rgba[0] << 16) | (rgba[3] << 24); + #endif + *(int*)m_pCurrColor = col; +} + +FORCEINLINE void CVertexBuilder::Color4Packed( int packedColor ) +{ + *(int*)m_pCurrColor = packedColor; +} + +FORCEINLINE int CVertexBuilder::PackColor4( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + return b | (g << 8) | (r << 16) | (a << 24); +} + + +inline void CVertexBuilder::Specular3f( float r, float g, float b ) +{ + Assert( m_pSpecular ); + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); + + unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | 0xFF000000; + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular3fv( const float *rgb ) +{ + Assert(rgb); + Assert( m_pSpecular ); + Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) ); + Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) ); + Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) ); + + unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | 0xFF000000; + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular4f( float r, float g, float b, float a ) +{ + Assert( m_pSpecular ); + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) && IsFinite(a) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) && (a >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) && (a <= 1.0f) ); + + unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | (FastFToC(a) << 24); + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular4fv( const float *rgb ) +{ + Assert(rgb); + Assert( m_pSpecular ); + Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) && IsFinite(rgb[3]) ); + Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) && (rgb[3] >= 0.0) ); + Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) && (rgb[3] <= 1.0) ); + + unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | (FastFToC(rgb[3]) << 24); + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + Assert( m_pSpecular ); + unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + + #ifdef OPENGL_SWAP_COLORS + int col = r | (g << 8) | (b << 16) | 0xFF000000; // r, g, b, a in memory + #else + int col = b | (g << 8) | (r << 16) | 0xFF000000; + #endif + + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular3ubv( unsigned char const *c ) +{ + Assert( m_pSpecular ); + unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + + #ifdef OPENGL_SWAP_COLORS + int col = c[0] | (c[1] << 8) | (c[2] << 16) | 0xFF000000; // r, g, b, a in memory + #else + int col = c[2] | (c[1] << 8) | (c[0] << 16) | 0xFF000000; + #endif + + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + Assert( m_pSpecular ); + unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + + #ifdef OPENGL_SWAP_COLORS + int col = r | (g << 8) | (b << 16) | (a << 24); // r, g, b, a in memory + #else + int col = b | (g << 8) | (r << 16) | (a << 24); + #endif + + *(int*)pSpecular = col; +} + +inline void CVertexBuilder::Specular4ubv( unsigned char const *c ) +{ + Assert( m_pSpecular ); + unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; + + #ifdef OPENGL_SWAP_COLORS + int col = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + #else + int col = c[2] | (c[1] << 8) | (c[0] << 16) | (c[3] << 24); + #endif + + *(int*)pSpecular = col; +} + + +//----------------------------------------------------------------------------- +// Texture coordinate setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::TexCoord1f( int nStage, float s ) +{ + Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); + Assert( IsFinite(s) ); + + float *pDst = m_pCurrTexCoord[nStage]; + *pDst = s; +} + +inline void CVertexBuilder::TexCoord2f( int nStage, float s, float t ) +{ + Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); + Assert( IsFinite(s) && IsFinite(t) ); + + float *pDst = m_pCurrTexCoord[nStage]; + *pDst++ = s; + *pDst = t; +} + +inline void CVertexBuilder::TexCoord2fv( int nStage, const float *st ) +{ + Assert(st); + Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); + Assert( IsFinite(st[0]) && IsFinite(st[1]) ); + + float *pDst = m_pCurrTexCoord[nStage]; + *pDst++ = *st++; + *pDst = *st; +} + +inline void CVertexBuilder::TexCoord3f( int stage, float s, float t, float u ) +{ + // Tried to add too much! + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(s) && IsFinite(t) && IsFinite(u) ); + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = s; + *pDst++ = t; + *pDst = u; +} + +inline void CVertexBuilder::TexCoord3fv( int stage, const float *stu ) +{ + Assert(stu); + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(stu[0]) && IsFinite(stu[1]) && IsFinite(stu[2]) ); + + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = *stu++; + *pDst++ = *stu++; + *pDst = *stu; +} + +inline void CVertexBuilder::TexCoord4f( int stage, float s, float t, float u, float v ) +{ + // Tried to add too much! + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(s) && IsFinite(t) && IsFinite(u) ); + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = s; + *pDst++ = t; + *pDst++ = u; + *pDst = v; +} + +inline void CVertexBuilder::TexCoord4fv( int stage, const float *stuv ) +{ + Assert(stuv); + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(stuv[0]) && IsFinite(stuv[1]) && IsFinite(stuv[2]) ); + + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = *stuv++; + *pDst++ = *stuv++; + *pDst++ = *stuv++; + *pDst = *stuv; +} + + +inline void CVertexBuilder::TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ) +{ + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(s) && IsFinite(t) ); + + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = ( s * scaleS ) + offsetS; + *pDst = ( t * scaleT ) + offsetT; +} + +inline void CVertexBuilder::TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ) +{ + Assert(st); + Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); + Assert( IsFinite(st[0]) && IsFinite(st[1]) ); + + float *pDst = m_pCurrTexCoord[stage]; + *pDst++ = ( *st++ * *scale++ ) + *offset++; + *pDst = ( *st * *scale ) + *offset; +} + + +//----------------------------------------------------------------------------- +// Tangent space setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::TangentS3f( float sx, float sy, float sz ) +{ + Assert( m_pTangentS ); + Assert( IsFinite(sx) && IsFinite(sy) && IsFinite(sz) ); + + float* pTangentS = OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); + *pTangentS++ = sx; + *pTangentS++ = sy; + *pTangentS = sz; +} + +inline void CVertexBuilder::TangentS3fv( const float* s ) +{ + Assert( s ); + Assert( m_pTangentS ); + Assert( IsFinite(s[0]) && IsFinite(s[1]) && IsFinite(s[2]) ); + + float* pTangentS = OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); + *pTangentS++ = *s++; + *pTangentS++ = *s++; + *pTangentS = *s; +} + +inline void CVertexBuilder::TangentT3f( float tx, float ty, float tz ) +{ + Assert( m_pTangentT ); + Assert( IsFinite(tx) && IsFinite(ty) && IsFinite(tz) ); + + float* pTangentT = OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); + *pTangentT++ = tx; + *pTangentT++ = ty; + *pTangentT = tz; +} + +inline void CVertexBuilder::TangentT3fv( const float* t ) +{ + Assert( t ); + Assert( m_pTangentT ); + Assert( IsFinite(t[0]) && IsFinite(t[1]) && IsFinite(t[2]) ); + + float* pTangentT = OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); + *pTangentT++ = *t++; + *pTangentT++ = *t++; + *pTangentT = *t; +} + + +//----------------------------------------------------------------------------- +// Wrinkle setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::Wrinkle1f( float flWrinkle ) +{ + Assert( m_pWrinkle ); + Assert( IsFinite(flWrinkle) ); + + float *pWrinkle = OffsetFloatPointer( m_pWrinkle, m_nCurrentVertex, m_VertexSize_Wrinkle ); + *pWrinkle = flWrinkle; +} + + +//----------------------------------------------------------------------------- +// Bone weight setting methods +//----------------------------------------------------------------------------- +inline void CVertexBuilder::BoneWeight( int idx, float weight ) +{ + Assert( m_pBoneWeight ); + Assert( IsFinite( weight ) ); + Assert( idx >= 0 ); + Assert( m_NumBoneWeights == 2 ); + + // This test is here because we store N-1 bone weights (the Nth is computed in + // the vertex shader as "1 - C", where C is the sum of the (N-1) other weights) + if ( idx < m_NumBoneWeights ) + { + float* pBoneWeight = OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); + pBoneWeight[idx] = weight; + } +} + +inline void CVertexBuilder::BoneWeights2( float weight1, float weight2 ) +{ + Assert( m_pBoneWeight ); + Assert( IsFinite( weight1 ) && IsFinite( weight2 ) ); + AssertOnce( m_NumBoneWeights == 2 ); + + // This test is here because we store N-1 bone weights (the Nth is computed in + // the vertex shader as "1 - C", where C is the sum of the (N-1) other weights) + float* pBoneWeight = OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); + pBoneWeight[0] = weight1; + pBoneWeight[1] = weight2; +} + +inline void CVertexBuilder::BoneMatrix( int idx, int matrixIdx ) +{ + Assert( m_pBoneMatrixIndex ); + Assert( idx >= 0 ); + Assert( idx < 4 ); + + // garymcthack + if ( matrixIdx == BONE_MATRIX_INDEX_INVALID ) + { + matrixIdx = 0; + } + Assert( (matrixIdx >= 0) && (matrixIdx < 53) ); + +#ifndef NEW_SKINNING + unsigned char* pBoneMatrix = &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex]; + if ( IsX360() ) + { + // store sequentially as wzyx order, gpu delivers as xyzw + idx = 3-idx; + } + pBoneMatrix[idx] = (unsigned char)matrixIdx; +#else + float* pBoneMatrix = &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex]; + pBoneMatrix[idx] = matrixIdx; +#endif +} + +inline void CVertexBuilder::BoneMatrices4( int matrixIdx0, int matrixIdx1, int matrixIdx2, int matrixIdx3 ) +{ + Assert( m_pBoneMatrixIndex ); + + // garymcthack + Assert( matrixIdx0 != BONE_MATRIX_INDEX_INVALID ); + Assert( matrixIdx1 != BONE_MATRIX_INDEX_INVALID ); + Assert( matrixIdx2 != BONE_MATRIX_INDEX_INVALID ); + Assert( matrixIdx3 != BONE_MATRIX_INDEX_INVALID ); + +#ifndef NEW_SKINNING + int nVal; + if ( IsX360() ) + { + // store sequentially as wzyx order, gpu delivers as xyzw + nVal = matrixIdx3 | ( matrixIdx2 << 8 ) | ( matrixIdx1 << 16 ) | ( matrixIdx0 << 24 ); + } + else + { + nVal = matrixIdx0 | ( matrixIdx1 << 8 ) | ( matrixIdx2 << 16 ) | ( matrixIdx3 << 24 ); + } + int* pBoneMatrix = (int*)( &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex] ); + *pBoneMatrix = nVal; +#else + float* pBoneMatrix = &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex]; + pBoneMatrix[0] = matrixIdx0; + pBoneMatrix[1] = matrixIdx1; + pBoneMatrix[2] = matrixIdx2; + pBoneMatrix[3] = matrixIdx3; +#endif +} + +//----------------------------------------------------------------------------- +// Templatized bone weight setting methods which support compressed vertices +//----------------------------------------------------------------------------- +template inline void CVertexBuilder::CompressedBoneWeight3fv( const float * pWeights ) +{ + Assert( T == m_CompressionType ); + Assert( m_pBoneWeight ); + Assert( pWeights ); + + float *pDestWeights = OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); + + if ( T == VERTEX_COMPRESSION_ON ) + { + // Quantize to 15 bits per weight (we use D3DDECLTYPE_SHORT2) + // NOTE: we perform careful normalization (weights sum to 1.0f in the vertex shader), so + // as to avoid cracking at boundaries between meshes with different numbers of weights + // per vertex. For example, (1) needs to yield the same normalized weights as (1,0), + // and (0.5,0.49) needs to normalize the same normalized weights as (0.5,0.49,0). + // The key is that values which are *computed* in the shader (e.g. the second weight + // in a 2-weight mesh) must exactly equal values which are *read* from the vertex + // stream (e.g. the second weight in a 3-weight mesh). + + // Only 1 or 2 weights (SHORT2N) supported for compressed verts so far + Assert( m_NumBoneWeights <= 2 ); + + const int WEIGHT0_SHIFT = IsX360() ? 16 : 0; + const int WEIGHT1_SHIFT = IsX360() ? 0 : 16; + unsigned int *weights = (unsigned int *)pDestWeights; + + // We scale our weights so that they sum to 32768, then subtract 1 (which gets added + // back in the shader), because dividing by 32767 introduces nasty rounding issues. + Assert( IsFinite( pWeights[0] ) && ( pWeights[0] >= 0.0f ) && ( pWeights[0] <= 1.0f ) ); + unsigned int weight0 = Float2Int( pWeights[0] * 32768.0f ); + *weights = ( 0x0000FFFF & (weight0 - 1) ) << WEIGHT0_SHIFT; + +#ifdef DEBUG + if ( m_NumBoneWeights == 1 ) + { + // Double-check the validity of the values that were passed in + Assert( IsFinite( pWeights[1] ) && ( pWeights[1] >= 0.0f ) && ( pWeights[1] <= 1.0f ) ); + unsigned int weight1 = Float2Int( pWeights[1] * 32768.0f ); + Assert( ( weight0 + weight1 ) <= 32768 ); + } +#endif + + if ( m_NumBoneWeights > 1 ) + { + // This path for 3 weights per vert (2 are stored and the 3rd is computed + // in the shader - we do post-quantization normalization here in such a + // way as to avoid mesh-boundary cracking) + Assert( m_NumBoneWeights == 2 ); + Assert( IsFinite( pWeights[1] ) && ( pWeights[1] >= 0.0f ) && ( pWeights[1] <= 1.0f ) ); + Assert( IsFinite( pWeights[2] ) && ( pWeights[2] >= 0.0f ) && ( pWeights[2] <= 1.0f ) ); + unsigned int weight1 = Float2Int( pWeights[1] * 32768.0f ); + unsigned int weight2 = Float2Int( pWeights[2] * 32768.0f ); + Assert( ( weight0 + weight1 + weight2 ) <= 32768 ); + unsigned int residual = 32768 - ( weight0 + weight1 + weight2 ); + weight1 += residual; // Normalize + *weights |= ( 0x0000FFFF & ( weight1 - 1 ) ) << WEIGHT1_SHIFT; + } + } + else // Uncompressed path + { + pDestWeights[0] = pWeights[0]; + pDestWeights[1] = pWeights[1]; + } +} + +//----------------------------------------------------------------------------- +// Generic per-vertex data setting method +//----------------------------------------------------------------------------- +inline void CVertexBuilder::UserData( const float* pData ) +{ + Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression + Assert( pData ); + + int userDataSize = 4; // garymcthack + float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); + memcpy( pUserData, pData, sizeof( float ) * userDataSize ); +} + +//----------------------------------------------------------------------------- +// Templatized generic per-vertex data setting method which supports compressed vertices +//----------------------------------------------------------------------------- +template inline void CVertexBuilder::CompressedUserData( const float* pData ) +{ + Assert( T == m_CompressionType ); + Assert( pData ); + // This is always in fact a tangent vector, not generic 'userdata' + Assert( IsFinite(pData[0]) && IsFinite(pData[1]) && IsFinite(pData[2]) ); + Assert( pData[0] >= -1.05f && pData[0] <= 1.05f ); + Assert( pData[1] >= -1.05f && pData[1] <= 1.05f ); + Assert( pData[2] >= -1.05f && pData[2] <= 1.05f ); + Assert( pData[3] == +1.0f || pData[3] == -1.0f ); + // FIXME: studiorender is passing in non-unit normals + //float lengthSqd = pData[0]*pData[0] + pData[1]*pData[1] + pData[2]*pData[2]; + //Assert( lengthSqd >= 0.95f && lengthSqd <= 1.05f ); + + if ( T == VERTEX_COMPRESSION_ON ) + { + float binormalSign = pData[3]; + +#if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) + float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); + PackNormal_SHORT2( pData, (unsigned int *)pUserData, binormalSign ); +#else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) + // FIXME: add a combined CompressedNormalAndTangent() accessor, to avoid reading back from write-combined memory here + // The normal should have already been written into the lower 16 + // bits - here, we OR in the tangent into the upper 16 bits + unsigned int existingNormalData = *(unsigned int *)m_pCurrNormal; + Assert( ( existingNormalData & 0xFFFF0000 ) == 0 ); +#ifdef _DEBUG + Assert( m_bWrittenNormal == true ); + m_bWrittenUserData = true; +#endif + bool bIsTangent = true; + unsigned int tangentData = 0; + PackNormal_UBYTE4( pData, &tangentData, bIsTangent, binormalSign ); + *(unsigned int *)m_pCurrNormal = existingNormalData | tangentData; +#endif + } + else + { + int userDataSize = 4; // garymcthack + float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); + memcpy( pUserData, pData, sizeof( float ) * userDataSize ); + } +} + + +//----------------------------------------------------------------------------- +// +// Helper class used to define index buffers +// +//----------------------------------------------------------------------------- +class CIndexBuilder : private IndexDesc_t +{ +public: + CIndexBuilder(); + CIndexBuilder( IIndexBuffer *pIndexBuffer, MaterialIndexFormat_t fmt = MATERIAL_INDEX_FORMAT_UNKNOWN ); + ~CIndexBuilder(); + + // Begins, ends modification of the index buffer (returns true if the lock succeeded) + // A lock may not succeed if append is set to true and there isn't enough room + // NOTE: Append is only used with dynamic index buffers; it's ignored for static buffers + bool Lock( int nMaxIndexCount, int nIndexOffset, bool bAppend = false ); + void Unlock(); + + // Spews the current data + // NOTE: Can only be called during a lock/unlock block + void SpewData(); + + // Returns the number of indices we can fit into the buffer without needing to discard + int GetRoomRemaining() const; + + // Binds this index buffer + void Bind( IMatRenderContext *pContext ); + + // Returns the byte offset + int Offset() const; + + // Begins, ends modification of the index buffer + // NOTE: IndexOffset is the number to add to all indices written into the buffer; + // useful when using dynamic vertex buffers. + void Begin( IIndexBuffer *pIndexBuffer, int nMaxIndexCount, int nIndexOffset = 0 ); + void End( bool bSpewData = false ); + + // Locks the index buffer to modify existing data + // Passing nVertexCount == -1 says to lock all the vertices for modification. + // Pass 0 for nIndexCount to not lock the index buffer. + void BeginModify( IIndexBuffer *pIndexBuffer, int nFirstIndex = 0, int nIndexCount = 0, int nIndexOffset = 0 ); + void EndModify( bool bSpewData = false ); + + // returns the number of indices + int IndexCount() const; + + // Returns the total number of indices across all Locks() + int TotalIndexCount() const; + + // Resets the mesh builder so it points to the start of everything again + void Reset(); + + // Selects the nth Index + void SelectIndex( int nBufferIndex ); + + // Advances the current index by one + void AdvanceIndex(); + void AdvanceIndices( int nIndexCount ); + + int GetCurrentIndex(); + int GetFirstIndex() const; + + unsigned short const* Index() const; + + // Used to define the indices (only used if you aren't using primitives) + void Index( unsigned short nIndex ); + + // Fast Index! No need to call advance index, and no random access allowed + void FastIndex( unsigned short nIndex ); + + // NOTE: This version is the one you really want to achieve write-combining; + // Write combining only works if you write in 4 bytes chunks. + void FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ); + + // Generates indices for a particular primitive type + void GenerateIndices( MaterialPrimitiveType_t primitiveType, int nIndexCount ); + + // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. + void AttachBegin( IMesh* pMesh, int nMaxIndexCount, const MeshDesc_t &desc ); + void AttachEnd(); + void AttachBeginModify( IMesh* pMesh, int nFirstIndex, int nIndexCount, const MeshDesc_t &desc ); + void AttachEndModify(); + + void FastTriangle( int startVert ); + void FastQuad( int startVert ); + void FastPolygon( int startVert, int numTriangles ); + void FastPolygonList( int startVert, int *pVertexCount, int polygonCount ); + void FastIndexList( const unsigned short *pIndexList, int startVert, int indexCount ); + +private: + // The mesh we're modifying + IIndexBuffer *m_pIndexBuffer; + + // Max number of indices + int m_nMaxIndexCount; + + // Number of indices + int m_nIndexCount; + + // Offset to add to each index as it's written into the buffer + int m_nIndexOffset; + + // The current index + mutable int m_nCurrentIndex; + + // Total number of indices appended + int m_nTotalIndexCount; + + // First index buffer offset + first index + unsigned int m_nBufferOffset; + unsigned int m_nBufferFirstIndex; + + // Used to make sure Begin/End calls and BeginModify/EndModify calls match. + bool m_bModify; +}; + + +//----------------------------------------------------------------------------- +// +// Inline methods related to CIndexBuilder +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +inline CIndexBuilder::CIndexBuilder() : m_pIndexBuffer(0), m_nIndexCount(0), + m_nCurrentIndex(0), m_nMaxIndexCount(0) +{ + m_nTotalIndexCount = 0; + m_nBufferOffset = INVALID_BUFFER_OFFSET; + m_nBufferFirstIndex = 0; +#ifdef _DEBUG + m_bModify = false; +#endif +} + +inline CIndexBuilder::CIndexBuilder( IIndexBuffer *pIndexBuffer, MaterialIndexFormat_t fmt ) +{ + m_pIndexBuffer = pIndexBuffer; + m_nBufferOffset = INVALID_BUFFER_OFFSET; + m_nBufferFirstIndex = 0; + m_nIndexCount = 0; + m_nCurrentIndex = 0; + m_nMaxIndexCount = 0; + m_nTotalIndexCount = 0; + if ( m_pIndexBuffer->IsDynamic() ) + { + m_pIndexBuffer->BeginCastBuffer( fmt ); + } + else + { + Assert( m_pIndexBuffer->IndexFormat() == fmt ); + } +#ifdef _DEBUG + m_bModify = false; +#endif +} + +inline CIndexBuilder::~CIndexBuilder() +{ + if ( m_pIndexBuffer && m_pIndexBuffer->IsDynamic() ) + { + m_pIndexBuffer->EndCastBuffer(); + } +} + + +//----------------------------------------------------------------------------- +// Begins, ends modification of the index buffer +//----------------------------------------------------------------------------- +inline bool CIndexBuilder::Lock( int nMaxIndexCount, int nIndexOffset, bool bAppend ) +{ + Assert( m_pIndexBuffer ); + m_bModify = false; + m_nIndexOffset = nIndexOffset; + m_nMaxIndexCount = nMaxIndexCount; + m_nIndexCount = 0; + bool bFirstLock = ( m_nBufferOffset == INVALID_BUFFER_OFFSET ); + if ( bFirstLock ) + { + bAppend = false; + } + if ( !bAppend ) + { + m_nTotalIndexCount = 0; + } + Reset(); + + // Lock the index buffer + if ( !m_pIndexBuffer->Lock( m_nMaxIndexCount, bAppend, *this ) ) + { + m_nMaxIndexCount = 0; + return false; + } + + if ( bFirstLock ) + { + m_nBufferOffset = m_nOffset; + m_nBufferFirstIndex = m_nFirstIndex; + } + + return true; +} + +inline void CIndexBuilder::Unlock() +{ + Assert( !m_bModify && m_pIndexBuffer ); + + m_pIndexBuffer->Unlock( m_nIndexCount, *this ); + m_nTotalIndexCount += m_nIndexCount; + + m_nMaxIndexCount = 0; + +#ifdef _DEBUG + // Null out our data... + memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); +#endif +} + +inline void CIndexBuilder::SpewData() +{ + m_pIndexBuffer->Spew( m_nIndexCount, *this ); +} + + +//----------------------------------------------------------------------------- +// Binds this index buffer +//----------------------------------------------------------------------------- +inline void CIndexBuilder::Bind( IMatRenderContext *pContext ) +{ + if ( m_pIndexBuffer && ( m_nBufferOffset != INVALID_BUFFER_OFFSET ) ) + { + pContext->BindIndexBuffer( m_pIndexBuffer, m_nBufferOffset ); + } + else + { + pContext->BindIndexBuffer( NULL, 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Returns the byte offset +//----------------------------------------------------------------------------- +inline int CIndexBuilder::Offset() const +{ + return m_nBufferOffset; +} + +inline int CIndexBuilder::GetFirstIndex() const +{ + return m_nBufferFirstIndex; +} + + +//----------------------------------------------------------------------------- +// Begins, ends modification of the index buffer +//----------------------------------------------------------------------------- +inline void CIndexBuilder::Begin( IIndexBuffer *pIndexBuffer, int nMaxIndexCount, int nIndexOffset ) +{ + Assert( pIndexBuffer && (!m_pIndexBuffer) ); + + m_pIndexBuffer = pIndexBuffer; + m_nIndexCount = 0; + m_nMaxIndexCount = nMaxIndexCount; + m_nIndexOffset = nIndexOffset; + + m_bModify = false; + + // Lock the index buffer + m_pIndexBuffer->Lock( m_nMaxIndexCount, false, *this ); + + // Point to the start of the buffers.. + Reset(); +} + +inline void CIndexBuilder::End( bool bSpewData ) +{ + // Make sure they called Begin() + Assert( !m_bModify ); + + if ( bSpewData ) + { + m_pIndexBuffer->Spew( m_nIndexCount, *this ); + } + + // Unlock our buffers + m_pIndexBuffer->Unlock( m_nIndexCount, *this ); + + m_pIndexBuffer = 0; + m_nMaxIndexCount = 0; + +#ifdef _DEBUG + // Null out our data... + memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Begins, ends modification of an existing index buffer which has already been filled out +//----------------------------------------------------------------------------- +inline void CIndexBuilder::BeginModify( IIndexBuffer* pIndexBuffer, int nFirstIndex, int nIndexCount, int nIndexOffset ) +{ + m_pIndexBuffer = pIndexBuffer; + m_nIndexCount = nIndexCount; + m_nMaxIndexCount = nIndexCount; + m_nIndexOffset = nIndexOffset; + m_bModify = true; + + // Lock the vertex and index buffer + m_pIndexBuffer->ModifyBegin( false, nFirstIndex, nIndexCount, *this ); + + // Point to the start of the buffers.. + Reset(); +} + +inline void CIndexBuilder::EndModify( bool bSpewData ) +{ + Assert( m_pIndexBuffer ); + Assert( m_bModify ); // Make sure they called BeginModify. + + if ( bSpewData ) + { + m_pIndexBuffer->Spew( m_nIndexCount, *this ); + } + + // Unlock our buffers + m_pIndexBuffer->ModifyEnd( *this ); + + m_pIndexBuffer = 0; + m_nMaxIndexCount = 0; + +#ifdef _DEBUG + // Null out our data... + memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. +//----------------------------------------------------------------------------- +inline void CIndexBuilder::AttachBegin( IMesh* pMesh, int nMaxIndexCount, const MeshDesc_t &desc ) +{ + m_pIndexBuffer = pMesh; + m_nIndexCount = 0; + m_nMaxIndexCount = nMaxIndexCount; + + m_bModify = false; + + // Copy relevant data from the mesh desc + m_nIndexOffset = desc.m_nFirstVertex; + m_pIndices = desc.m_pIndices; + m_nIndexSize = desc.m_nIndexSize; + + // Point to the start of the buffers.. + Reset(); +} + +inline void CIndexBuilder::AttachEnd() +{ + Assert( m_pIndexBuffer ); + Assert( !m_bModify ); // Make sure they called AttachBegin. + + m_pIndexBuffer = 0; + m_nMaxIndexCount = 0; + +#ifdef _DEBUG + // Null out our data... + memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); +#endif +} + +inline void CIndexBuilder::AttachBeginModify( IMesh* pMesh, int nFirstIndex, int nIndexCount, const MeshDesc_t &desc ) +{ + m_pIndexBuffer = pMesh; + m_nIndexCount = nIndexCount; + m_nMaxIndexCount = nIndexCount; + m_bModify = true; + + // Copy relevant data from the mesh desc + m_nIndexOffset = desc.m_nFirstVertex; + m_pIndices = desc.m_pIndices; + m_nIndexSize = desc.m_nIndexSize; + + // Point to the start of the buffers.. + Reset(); +} + +inline void CIndexBuilder::AttachEndModify() +{ + Assert( m_pIndexBuffer ); + Assert( m_bModify ); // Make sure they called AttachBeginModify. + + m_pIndexBuffer = 0; + m_nMaxIndexCount = 0; + +#ifdef _DEBUG + // Null out our data... + memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Resets the index buffer builder so it points to the start of everything again +//----------------------------------------------------------------------------- +inline void CIndexBuilder::Reset() +{ + m_nCurrentIndex = 0; +} + + +//----------------------------------------------------------------------------- +// returns the number of indices +//----------------------------------------------------------------------------- +inline int CIndexBuilder::IndexCount() const +{ + return m_nIndexCount; +} + + +//----------------------------------------------------------------------------- +// Returns the total number of indices across all Locks() +//----------------------------------------------------------------------------- +inline int CIndexBuilder::TotalIndexCount() const +{ + return m_nTotalIndexCount; +} + + +//----------------------------------------------------------------------------- +// Advances the current index +//----------------------------------------------------------------------------- +inline void CIndexBuilder::AdvanceIndex() +{ + m_nCurrentIndex += m_nIndexSize; + if ( m_nCurrentIndex > m_nIndexCount ) + { + m_nIndexCount = m_nCurrentIndex; + } +} + +inline void CIndexBuilder::AdvanceIndices( int nIndices ) +{ + m_nCurrentIndex += nIndices * m_nIndexSize; + if ( m_nCurrentIndex > m_nIndexCount ) + { + m_nIndexCount = m_nCurrentIndex; + } +} + + +//----------------------------------------------------------------------------- +// Returns the current index +//----------------------------------------------------------------------------- +inline int CIndexBuilder::GetCurrentIndex() +{ + return m_nCurrentIndex; +} + +inline unsigned short const* CIndexBuilder::Index() const +{ + Assert( m_nCurrentIndex < m_nMaxIndexCount ); + return &m_pIndices[m_nCurrentIndex]; +} + +inline void CIndexBuilder::SelectIndex( int nIndex ) +{ + Assert( ( nIndex >= 0 ) && ( nIndex < m_nIndexCount ) ); + m_nCurrentIndex = nIndex * m_nIndexSize; +} + + +//----------------------------------------------------------------------------- +// Used to write data into the index buffer +//----------------------------------------------------------------------------- +inline void CIndexBuilder::Index( unsigned short nIndex ) +{ + Assert( m_pIndices ); + Assert( m_nCurrentIndex < m_nMaxIndexCount ); + m_pIndices[ m_nCurrentIndex ] = (unsigned short)( m_nIndexOffset + nIndex ); +} + +// Fast Index! No need to call advance index +inline void CIndexBuilder::FastIndex( unsigned short nIndex ) +{ + Assert( m_pIndices ); + Assert( m_nCurrentIndex < m_nMaxIndexCount ); + m_pIndices[m_nCurrentIndex] = (unsigned short)( m_nIndexOffset + nIndex ); + m_nCurrentIndex += m_nIndexSize; + m_nIndexCount = m_nCurrentIndex; +} + +FORCEINLINE void CIndexBuilder::FastTriangle( int startVert ) +{ + startVert += m_nIndexOffset; + unsigned short *pIndices = &m_pIndices[m_nCurrentIndex]; + *pIndices++ = startVert++; + *pIndices++ = startVert++; + *pIndices++ = startVert; + AdvanceIndices(3); +} + +FORCEINLINE void CIndexBuilder::FastQuad( int startVert ) +{ + startVert += m_nIndexOffset; + unsigned short *pIndices = &m_pIndices[m_nCurrentIndex]; + *pIndices++ = startVert++; + *pIndices++ = startVert++; + *pIndices++ = startVert; + + *pIndices++ = startVert - 2; + *pIndices++ = startVert++; + *pIndices++ = startVert; + AdvanceIndices(6); +} + +inline void CIndexBuilder::FastPolygon( int startVert, int triangleCount ) +{ + unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; + startVert += m_nIndexOffset; + if ( !IsX360() ) + { + // NOTE: IndexSize is 1 or 0 (0 for alt-tab) + // This prevents us from writing into bogus memory + Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); + triangleCount *= m_nIndexSize; + } + for ( int v = 0; v < triangleCount; ++v ) + { + *pIndex++ = startVert; + *pIndex++ = startVert + v + 1; + *pIndex++ = startVert + v + 2; + } + AdvanceIndices(triangleCount*3); +} + +inline void CIndexBuilder::FastPolygonList( int startVert, int *pVertexCount, int polygonCount ) +{ + unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; + startVert += m_nIndexOffset; + int indexOut = 0; + + if ( !IsX360() ) + { + // NOTE: IndexSize is 1 or 0 (0 for alt-tab) + // This prevents us from writing into bogus memory + Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); + polygonCount *= m_nIndexSize; + } + + for ( int i = 0; i < polygonCount; i++ ) + { + int vertexCount = pVertexCount[i]; + int triangleCount = vertexCount-2; + for ( int v = 0; v < triangleCount; ++v ) + { + *pIndex++ = startVert; + *pIndex++ = startVert + v + 1; + *pIndex++ = startVert + v + 2; + } + startVert += vertexCount; + indexOut += triangleCount * 3; + } + AdvanceIndices(indexOut); +} + +inline void CIndexBuilder::FastIndexList( const unsigned short *pIndexList, int startVert, int indexCount ) +{ + unsigned short *pIndexOut = &m_pIndices[m_nCurrentIndex]; + startVert += m_nIndexOffset; + if ( !IsX360() ) + { + // NOTE: IndexSize is 1 or 0 (0 for alt-tab) + // This prevents us from writing into bogus memory + Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); + indexCount *= m_nIndexSize; + } + for ( int i = 0; i < indexCount; ++i ) + { + pIndexOut[i] = startVert + pIndexList[i]; + } + AdvanceIndices(indexCount); +} + + +//----------------------------------------------------------------------------- +// NOTE: This version is the one you really want to achieve write-combining; +// Write combining only works if you write in 4 bytes chunks. +//----------------------------------------------------------------------------- +inline void CIndexBuilder::FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ) +{ + Assert( m_pIndices ); + Assert( m_nCurrentIndex < m_nMaxIndexCount - 1 ); +// Assert( ( (int)( &m_pIndices[m_nCurrentIndex] ) & 0x3 ) == 0 ); + +#ifndef _X360 + unsigned int nIndices = ( (unsigned int)nIndex1 + m_nIndexOffset ) | ( ( (unsigned int)nIndex2 + m_nIndexOffset ) << 16 ); +#else + unsigned int nIndices = ( (unsigned int)nIndex2 + m_nIndexOffset ) | ( ( (unsigned int)nIndex1 + m_nIndexOffset ) << 16 ); +#endif + + *(int*)( &m_pIndices[m_nCurrentIndex] ) = nIndices; + m_nCurrentIndex += m_nIndexSize + m_nIndexSize; + m_nIndexCount = m_nCurrentIndex; +} + + +//----------------------------------------------------------------------------- +// Generates indices for a particular primitive type +//----------------------------------------------------------------------------- +inline void CIndexBuilder::GenerateIndices( MaterialPrimitiveType_t primitiveType, int nIndexCount ) +{ + // FIXME: How to make this work with short vs int sized indices? + // Don't generate indices if we've got an empty buffer + if ( m_nIndexSize == 0 ) + return; + + int nMaxIndices = m_nMaxIndexCount - m_nCurrentIndex; + nIndexCount = MIN( nMaxIndices, nIndexCount ); + if ( nIndexCount == 0 ) + return; + + unsigned short *pIndices = &m_pIndices[m_nCurrentIndex]; + + switch( primitiveType ) + { + case MATERIAL_INSTANCED_QUADS: + Assert(0); // Shouldn't get here (this primtype is unindexed) + break; + case MATERIAL_QUADS: + GenerateQuadIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); + break; + case MATERIAL_POLYGON: + GeneratePolygonIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); + break; + case MATERIAL_LINE_STRIP: + GenerateLineStripIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); + break; + case MATERIAL_LINE_LOOP: + GenerateLineLoopIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); + break; + case MATERIAL_POINTS: + Assert(0); // Shouldn't get here (this primtype is unindexed) + break; + case MATERIAL_SUBD_QUADS_EXTRA: + case MATERIAL_SUBD_QUADS_REG: + default: + GenerateSequentialIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); + break; + } + + AdvanceIndices( nIndexCount ); +} + + +//----------------------------------------------------------------------------- +// +// Helper class used to define meshes +// +//----------------------------------------------------------------------------- +//class CMeshBuilder : private MeshDesc_t +// hack fixme +class CMeshBuilder : public MeshDesc_t +{ +public: + CMeshBuilder(); + ~CMeshBuilder() { Assert(!m_pMesh); } // if this fires you did a Begin() without an End() + + operator CIndexBuilder&() { return m_IndexBuilder; } + + // This must be called before Begin, if a vertex buffer with a compressed format is to be used + void SetCompressionType( VertexCompressionType_t compressionType ); + + // Locks the vertex buffer + // (*cannot* use the Index() call below) + void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int numPrimitives ); + + // Locks the vertex buffer, can specify arbitrary index lists + // (must use the Index() call below) + void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ); + void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ); + + // forward compat + void Begin( IVertexBuffer *pVertexBuffer, MaterialPrimitiveType_t type, int numPrimitives ); + void Begin( IVertexBuffer *pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ); + void Begin( IVertexBuffer *pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ); + + // Use this when you're done writing + // Set bDraw to true to call m_pMesh->Draw automatically. + void End( bool bSpewData = false, bool bDraw = false ); + + // Locks the vertex buffer to modify existing data + // Passing nVertexCount == -1 says to lock all the vertices for modification. + // Pass 0 for nIndexCount to not lock the index buffer. + void BeginModify( IMesh *pMesh, int nFirstVertex = 0, int nVertexCount = -1, int nFirstIndex = 0, int nIndexCount = 0 ); + void EndModify( bool bSpewData = false ); + + // A helper method since this seems to be done a whole bunch. + void DrawQuad( IMesh* pMesh, const float *v1, const float *v2, + const float *v3, const float *v4, unsigned char const *pColor, bool wireframe = false ); + + // returns the number of indices and vertices + int VertexCount() const; + int IndexCount() const; + + // Resets the mesh builder so it points to the start of everything again + void Reset(); + + // Returns the size of the vertex + int VertexSize() { return m_ActualVertexSize; } + + // returns the data size of a given texture coordinate + int TextureCoordinateSize( int nTexCoordNumber ) { return m_VertexSize_TexCoord[ nTexCoordNumber ]; } + + // Returns the base vertex memory pointer + void* BaseVertexData(); + + // Selects the nth Vertex and Index + void SelectVertex( int idx ); + void SelectIndex( int idx ); + + // Given an index, point to the associated vertex + void SelectVertexFromIndex( int idx ); + + // Advances the current vertex and index by one + void AdvanceVertex(); + template void AdvanceVertexF(); + void AdvanceVertices( int nVerts ); + void AdvanceIndex(); + void AdvanceIndices( int nIndices ); + + int GetCurrentVertex(); + int GetCurrentIndex(); + + // Data retrieval... + const float *Position() const; + + const float *Normal() const; + + unsigned int Color() const; + + unsigned char *Specular() const; + + const float *TexCoord( int stage ) const; + + const float *TangentS() const; + const float *TangentT() const; + + const float *BoneWeight() const; + float Wrinkle() const; + + int NumBoneWeights() const; +#ifndef NEW_SKINNING + unsigned char *BoneMatrix() const; +#else + float *BoneMatrix() const; +#endif + unsigned short const *Index() const; + + // position setting + void Position3f( float x, float y, float z ); + void Position3fv( const float *v ); + + // normal setting + void Normal3f( float nx, float ny, float nz ); + void Normal3fv( const float *n ); + void NormalDelta3fv( const float *n ); + void NormalDelta3f( float nx, float ny, float nz ); + + // normal setting (templatized for code which needs to support compressed vertices) + template void CompressedNormal3f( float nx, float ny, float nz ); + template void CompressedNormal3fv( const float *n ); + + // color setting + void Color3f( float r, float g, float b ); + void Color3fv( const float *rgb ); + void Color4f( float r, float g, float b, float a ); + void Color4fv( const float *rgba ); + + // Faster versions of color + void Color3ub( unsigned char r, unsigned char g, unsigned char b ); + void Color3ubv( unsigned char const* rgb ); + void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Color4ubv( unsigned char const* rgba ); + void Color4Packed( int packedColor ); + int PackColor4( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + + // specular color setting + void Specular3f( float r, float g, float b ); + void Specular3fv( const float *rgb ); + void Specular4f( float r, float g, float b, float a ); + void Specular4fv( const float *rgba ); + + // Faster version of specular + void Specular3ub( unsigned char r, unsigned char g, unsigned char b ); + void Specular3ubv( unsigned char const *c ); + void Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); + void Specular4ubv( unsigned char const *c ); + + // texture coordinate setting + void TexCoord1f( int stage, float s ); + void TexCoord2f( int stage, float s, float t ); + void TexCoord2fv( int stage, const float *st ); + void TexCoord3f( int stage, float s, float t, float u ); + void TexCoord3fv( int stage, const float *stu ); + void TexCoord4f( int stage, float s, float t, float u, float w ); + void TexCoord4fv( int stage, const float *stuv ); + + void TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ); + void TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ); + + // tangent space + void TangentS3f( float sx, float sy, float sz ); + void TangentS3fv( const float *s ); + + void TangentT3f( float tx, float ty, float tz ); + void TangentT3fv( const float *t ); + + // Wrinkle + void Wrinkle1f( float flWrinkle ); + + // bone weights + void BoneWeight( int idx, float weight ); + void BoneWeights2( float weight1, float weight2 ); + + // bone weights (templatized for code which needs to support compressed vertices) + template void CompressedBoneWeight3fv( const float * pWeights ); + + // bone matrix index + void BoneMatrix( int idx, int matrixIndex ); + void BoneMatrices4( int matrixIdx0, int matrixIdx1, int matrixIdx2, int matrixIdx3 ); + + // Generic per-vertex data + void UserData( const float *pData ); + // Generic per-vertex data (templatized for code which needs to support compressed vertices) + template void CompressedUserData( const float* pData ); + + // Used to define the indices (only used if you aren't using primitives) + void Index( unsigned short index ); + + // NOTE: Use this one to get write combining! Much faster than the other version of FastIndex + // Fast Index! No need to call advance index, and no random access allowed + void FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ); + + // Fast Index! No need to call advance index, and no random access allowed + void FastIndex( unsigned short index ); + void FastQuad( int index ); + + // Fast Vertex! No need to call advance vertex, and no random access allowed. + // WARNING - these are low level functions that are intended only for use + // in the software vertex skinner. + void FastVertex( const ModelVertexDX8_t &vertex ); + void FastVertexSSE( const ModelVertexDX8_t &vertex ); + void FastQuadVertexSSE( const QuadTessVertex_t &vertex ); + + // Add number of verts and current vert since FastVertexxx routines do not update. + void FastAdvanceNVertices(int n); + +#if defined( _X360 ) + void VertexDX8ToX360( const ModelVertexDX8_t &vertex ); +#endif + + // this low level function gets you a pointer to the vertex output data. It is dangerous - any + // caller using it must understand the vertex layout that it is building. It is for optimized + // meshbuilding loops like particle drawing that use special shaders. After writing to the output + // data, you shuodl call FastAdvanceNVertices + FORCEINLINE void *GetVertexDataPtr( int nWhatSizeIThinkItIs ) + { + if ( m_VertexBuilder.m_VertexSize_Position != nWhatSizeIThinkItIs ) + return NULL; + return m_VertexBuilder.m_pCurrPosition; + } + + +private: + // Computes number of verts and indices + void ComputeNumVertsAndIndices( int *pMaxVertices, int *pMaxIndices, + MaterialPrimitiveType_t type, int nPrimitiveCount ); + int IndicesFromVertices( MaterialPrimitiveType_t type, int nVertexCount ); + + // The mesh we're modifying + IMesh *m_pMesh; + + MaterialPrimitiveType_t m_Type; + + // Generate indices? + bool m_bGenerateIndices; + + CIndexBuilder m_IndexBuilder; + CVertexBuilder m_VertexBuilder; +}; + + +//----------------------------------------------------------------------------- +// Forward compat +//----------------------------------------------------------------------------- +inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, MaterialPrimitiveType_t type, int numPrimitives ) +{ + Assert( 0 ); + // Begin( pVertexBuffer->GetMesh(), type, numPrimitives ); +} + +inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ) +{ + Assert( 0 ); + // Begin( pVertexBuffer->GetMesh(), type, nVertexCount, nIndexCount, nFirstVertex ); +} + +inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ) +{ + Assert( 0 ); + // Begin( pVertexBuffer->GetMesh(), type, nVertexCount, nIndexCount ); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +inline CMeshBuilder::CMeshBuilder() : m_pMesh(0), m_bGenerateIndices(false) +{ +} + + +//----------------------------------------------------------------------------- +// Computes the number of verts and indices based on primitive type and count +//----------------------------------------------------------------------------- +inline void CMeshBuilder::ComputeNumVertsAndIndices( int *pMaxVertices, int *pMaxIndices, + MaterialPrimitiveType_t type, int nPrimitiveCount ) +{ + switch(type) + { + case MATERIAL_POINTS: + *pMaxVertices = *pMaxIndices = nPrimitiveCount; + break; + + case MATERIAL_LINES: + *pMaxVertices = *pMaxIndices = nPrimitiveCount * 2; + break; + + case MATERIAL_LINE_STRIP: + *pMaxVertices = nPrimitiveCount + 1; + *pMaxIndices = nPrimitiveCount * 2; + break; + + case MATERIAL_LINE_LOOP: + *pMaxVertices = nPrimitiveCount; + *pMaxIndices = nPrimitiveCount * 2; + break; + + case MATERIAL_TRIANGLES: + *pMaxVertices = *pMaxIndices = nPrimitiveCount * 3; + break; + + case MATERIAL_TRIANGLE_STRIP: + *pMaxVertices = *pMaxIndices = nPrimitiveCount + 2; + break; + + case MATERIAL_QUADS: + *pMaxVertices = nPrimitiveCount * 4; + *pMaxIndices = nPrimitiveCount * 6; + break; + + case MATERIAL_INSTANCED_QUADS: + *pMaxVertices = nPrimitiveCount; + *pMaxIndices = 0; // This primtype is unindexed + break; + + case MATERIAL_POLYGON: + *pMaxVertices = nPrimitiveCount; + *pMaxIndices = (nPrimitiveCount - 2) * 3; + break; + + default: + Assert(0); + } + + // FIXME: need to get this from meshdx8.cpp, or move it to somewhere common + Assert( *pMaxVertices <= 32768 ); + Assert( *pMaxIndices <= 32768 ); +} + + +inline int CMeshBuilder::IndicesFromVertices( MaterialPrimitiveType_t type, int nVertexCount ) +{ + switch( type ) + { + case MATERIAL_QUADS: + Assert( (nVertexCount & 0x3) == 0 ); + return (nVertexCount * 6) / 4; + + case MATERIAL_INSTANCED_QUADS: + // This primtype is unindexed + return 0; + + case MATERIAL_POLYGON: + Assert( nVertexCount >= 3 ); + return (nVertexCount - 2) * 3; + + case MATERIAL_LINE_STRIP: + Assert( nVertexCount >= 2 ); + return (nVertexCount - 1) * 2; + + case MATERIAL_LINE_LOOP: + Assert( nVertexCount >= 3 ); + return nVertexCount * 2; + + default: + return nVertexCount; + } +} + +//----------------------------------------------------------------------------- +// Specify the type of vertex compression that this CMeshBuilder will perform +//----------------------------------------------------------------------------- +inline void CMeshBuilder::SetCompressionType( VertexCompressionType_t vertexCompressionType ) +{ + m_CompressionType = vertexCompressionType; + m_VertexBuilder.SetCompressionType( vertexCompressionType ); +} + +//----------------------------------------------------------------------------- +// Begins modifying the mesh +//----------------------------------------------------------------------------- +inline void CMeshBuilder::Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int numPrimitives ) +{ + Assert( pMesh && (!m_pMesh) ); + Assert( type != MATERIAL_HETEROGENOUS ); + + m_pMesh = pMesh; + m_bGenerateIndices = true; + m_Type = type; + + int nMaxVertexCount, nMaxIndexCount; + ComputeNumVertsAndIndices( &nMaxVertexCount, &nMaxIndexCount, type, numPrimitives ); + + switch( type ) + { + case MATERIAL_INSTANCED_QUADS: + m_pMesh->SetPrimitiveType( MATERIAL_INSTANCED_QUADS ); + break; + + case MATERIAL_QUADS: + case MATERIAL_POLYGON: + m_pMesh->SetPrimitiveType( MATERIAL_TRIANGLES ); + break; + + case MATERIAL_LINE_STRIP: + case MATERIAL_LINE_LOOP: + m_pMesh->SetPrimitiveType( MATERIAL_LINES ); + break; + + default: + m_pMesh->SetPrimitiveType( type ); + } + + // Lock the mesh + m_pMesh->LockMesh( nMaxVertexCount, nMaxIndexCount, *this ); + + m_IndexBuilder.AttachBegin( pMesh, nMaxIndexCount, *this ); + m_VertexBuilder.AttachBegin( pMesh, nMaxVertexCount, *this ); + + // Point to the start of the index and vertex buffers + Reset(); +} + +inline void CMeshBuilder::Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ) +{ + Begin( pMesh, type, nVertexCount, nIndexCount ); + + *nFirstVertex = m_VertexBuilder.m_nFirstVertex * m_VertexBuilder.VertexSize(); +} + +inline void CMeshBuilder::Begin( IMesh* pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ) +{ + Assert( pMesh && (!m_pMesh) ); + + // NOTE: We can't specify the indices when we use quads, polygons, or + // linestrips; they aren't actually directly supported by + // the material system + Assert( (type != MATERIAL_QUADS) && (type != MATERIAL_INSTANCED_QUADS) && (type != MATERIAL_POLYGON) && + (type != MATERIAL_LINE_STRIP) && (type != MATERIAL_LINE_LOOP)); + + // Dx8 doesn't support indexed points... + Assert( type != MATERIAL_POINTS ); + + m_pMesh = pMesh; + m_bGenerateIndices = false; + m_Type = type; + + // Set the primitive type + m_pMesh->SetPrimitiveType( type ); + + // Lock the vertex and index buffer + m_pMesh->LockMesh( nVertexCount, nIndexCount, *this ); + + m_IndexBuilder.AttachBegin( pMesh, nIndexCount, *this ); + m_VertexBuilder.AttachBegin( pMesh, nVertexCount, *this ); + + // Point to the start of the buffers.. + Reset(); +} + + +//----------------------------------------------------------------------------- +// Use this when you're done modifying the mesh +//----------------------------------------------------------------------------- +inline void CMeshBuilder::End( bool bSpewData, bool bDraw ) +{ + if ( m_bGenerateIndices ) + { + int nIndexCount = IndicesFromVertices( m_Type, m_VertexBuilder.VertexCount() ); + m_IndexBuilder.GenerateIndices( m_Type, nIndexCount ); + } + + if ( bSpewData ) + { + m_pMesh->Spew( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); + } + +#ifdef _DEBUG + m_pMesh->ValidateData( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); +#endif + + // Unlock our buffers + m_pMesh->UnlockMesh( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); + + m_IndexBuilder.AttachEnd(); + m_VertexBuilder.AttachEnd(); + + if ( bDraw ) + { + m_pMesh->Draw(); + } + + m_pMesh = 0; + +#ifdef _DEBUG + memset( (MeshDesc_t*)this, 0, sizeof(MeshDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Locks the vertex buffer to modify existing data +//----------------------------------------------------------------------------- +inline void CMeshBuilder::BeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount ) +{ + Assert( pMesh && (!m_pMesh) ); + + if (nVertexCount < 0) + { + nVertexCount = pMesh->VertexCount(); + } + + m_pMesh = pMesh; + m_bGenerateIndices = false; + + // Locks mesh for modifying + pMesh->ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, *this ); + + m_IndexBuilder.AttachBeginModify( pMesh, nFirstIndex, nIndexCount, *this ); + m_VertexBuilder.AttachBeginModify( pMesh, nFirstVertex, nVertexCount, *this ); + + // Point to the start of the buffers.. + Reset(); +} + +inline void CMeshBuilder::EndModify( bool bSpewData ) +{ + Assert( m_pMesh ); + + if (bSpewData) + { + m_pMesh->Spew( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); + } +#ifdef _DEBUG + m_pMesh->ValidateData( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); +#endif + + // Unlocks mesh + m_pMesh->ModifyEnd( *this ); + m_pMesh = 0; + + m_IndexBuilder.AttachEndModify(); + m_VertexBuilder.AttachEndModify(); + +#ifdef _DEBUG + // Null out our pointers... + memset( (MeshDesc_t*)this, 0, sizeof(MeshDesc_t) ); +#endif +} + + +//----------------------------------------------------------------------------- +// Resets the mesh builder so it points to the start of everything again +//----------------------------------------------------------------------------- +inline void CMeshBuilder::Reset() +{ + m_IndexBuilder.Reset(); + m_VertexBuilder.Reset(); +} + + +//----------------------------------------------------------------------------- +// Selects the current Vertex and Index +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::SelectVertex( int nIndex ) +{ + m_VertexBuilder.SelectVertex( nIndex ); +} + +inline void CMeshBuilder::SelectVertexFromIndex( int idx ) +{ + // NOTE: This index is expected to be relative + int vertIdx = idx - m_nFirstVertex; + SelectVertex( vertIdx ); +} + +FORCEINLINE void CMeshBuilder::SelectIndex( int idx ) +{ + m_IndexBuilder.SelectIndex( idx ); +} + + +//----------------------------------------------------------------------------- +// Advances the current vertex and index by one +//----------------------------------------------------------------------------- +template FORCEINLINE void CMeshBuilder::AdvanceVertexF() +{ + m_VertexBuilder.AdvanceVertexF(); +} +FORCEINLINE void CMeshBuilder::AdvanceVertex() +{ + m_VertexBuilder.AdvanceVertex(); +} + +FORCEINLINE void CMeshBuilder::AdvanceVertices( int nVertexCount ) +{ + m_VertexBuilder.AdvanceVertices( nVertexCount ); +} + +FORCEINLINE void CMeshBuilder::AdvanceIndex() +{ + m_IndexBuilder.AdvanceIndex(); +} + +FORCEINLINE void CMeshBuilder::AdvanceIndices( int nIndices ) +{ + m_IndexBuilder.AdvanceIndices( nIndices ); +} + +FORCEINLINE int CMeshBuilder::GetCurrentVertex() +{ + return m_VertexBuilder.GetCurrentVertex(); +} + +FORCEINLINE int CMeshBuilder::GetCurrentIndex() +{ + return m_IndexBuilder.GetCurrentIndex(); +} + + +//----------------------------------------------------------------------------- +// A helper method since this seems to be done a whole bunch. +//----------------------------------------------------------------------------- +inline void CMeshBuilder::DrawQuad( IMesh* pMesh, const float* v1, const float* v2, + const float* v3, const float* v4, unsigned char const* pColor, bool wireframe ) +{ + if (!wireframe) + { + Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 ); + + Position3fv (v1); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v2); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v4); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v3); + Color4ubv( pColor ); + AdvanceVertexF(); + } + else + { + Begin( pMesh, MATERIAL_LINE_LOOP, 4 ); + Position3fv (v1); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v2); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v3); + Color4ubv( pColor ); + AdvanceVertexF(); + + Position3fv (v4); + Color4ubv( pColor ); + AdvanceVertexF(); + } + + End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// returns the number of indices and vertices +//----------------------------------------------------------------------------- +FORCEINLINE int CMeshBuilder::VertexCount() const +{ + return m_VertexBuilder.VertexCount(); +} + +FORCEINLINE int CMeshBuilder::IndexCount() const +{ + return m_IndexBuilder.IndexCount(); +} + + +//----------------------------------------------------------------------------- +// Returns the base vertex memory pointer +//----------------------------------------------------------------------------- +FORCEINLINE void* CMeshBuilder::BaseVertexData() +{ + return m_VertexBuilder.BaseVertexData(); +} + +//----------------------------------------------------------------------------- +// Data retrieval... +//----------------------------------------------------------------------------- +FORCEINLINE const float* CMeshBuilder::Position() const +{ + return m_VertexBuilder.Position(); +} + +FORCEINLINE const float* CMeshBuilder::Normal() const +{ + return m_VertexBuilder.Normal(); +} + +FORCEINLINE unsigned int CMeshBuilder::Color() const +{ + return m_VertexBuilder.Color(); +} + +FORCEINLINE unsigned char *CMeshBuilder::Specular() const +{ + return m_VertexBuilder.Specular(); +} + +FORCEINLINE const float* CMeshBuilder::TexCoord( int nStage ) const +{ + return m_VertexBuilder.TexCoord( nStage ); +} + +FORCEINLINE const float* CMeshBuilder::TangentS() const +{ + return m_VertexBuilder.TangentS(); +} + +FORCEINLINE const float* CMeshBuilder::TangentT() const +{ + return m_VertexBuilder.TangentT(); +} + +FORCEINLINE float CMeshBuilder::Wrinkle() const +{ + return m_VertexBuilder.Wrinkle(); +} + +FORCEINLINE const float* CMeshBuilder::BoneWeight() const +{ + return m_VertexBuilder.BoneWeight(); +} + +FORCEINLINE int CMeshBuilder::NumBoneWeights() const +{ + return m_VertexBuilder.NumBoneWeights(); +} + +FORCEINLINE unsigned short const* CMeshBuilder::Index() const +{ + return m_IndexBuilder.Index(); +} + + +//----------------------------------------------------------------------------- +// Index +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::Index( unsigned short idx ) +{ + m_IndexBuilder.Index( idx ); +} + + +//----------------------------------------------------------------------------- +// Fast Index! No need to call advance index +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::FastIndex( unsigned short idx ) +{ + m_IndexBuilder.FastIndex( idx ); +} + +// NOTE: Use this one to get write combining! Much faster than the other version of FastIndex +// Fast Index! No need to call advance index, and no random access allowed +FORCEINLINE void CMeshBuilder::FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ) +{ + m_IndexBuilder.FastIndex2( nIndex1, nIndex2 ); +} + +FORCEINLINE void CMeshBuilder::FastQuad( int nIndex ) +{ + m_IndexBuilder.FastQuad( nIndex ); +} + +//----------------------------------------------------------------------------- +// For use with the FastVertex methods, advances the current vertex by N +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::FastAdvanceNVertices( int nVertexCount ) +{ + m_VertexBuilder.FastAdvanceNVertices( nVertexCount ); +} + + +//----------------------------------------------------------------------------- +// Fast Vertex! No need to call advance vertex, and no random access allowed +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::FastVertex( const ModelVertexDX8_t &vertex ) +{ + m_VertexBuilder.FastVertex( vertex ); +} + +FORCEINLINE void CMeshBuilder::FastVertexSSE( const ModelVertexDX8_t &vertex ) +{ + m_VertexBuilder.FastVertexSSE( vertex ); +} + +FORCEINLINE void CMeshBuilder::FastQuadVertexSSE( const QuadTessVertex_t &vertex ) +{ + m_VertexBuilder.FastQuadVertexSSE( vertex ); +} + + +//----------------------------------------------------------------------------- +// Copies a vertex into the x360 format +//----------------------------------------------------------------------------- +#if defined( _X360 ) +inline void CMeshBuilder::VertexDX8ToX360( const ModelVertexDX8_t &vertex ) +{ + m_VertexBuilder.VertexDX8ToX360( vertex ); +} +#endif + +//----------------------------------------------------------------------------- +// Vertex field setting methods +//----------------------------------------------------------------------------- +FORCEINLINE void CMeshBuilder::Position3f( float x, float y, float z ) +{ + m_VertexBuilder.Position3f( x, y, z ); +} + +FORCEINLINE void CMeshBuilder::Position3fv( const float *v ) +{ + m_VertexBuilder.Position3fv( v ); +} + +FORCEINLINE void CMeshBuilder::Normal3f( float nx, float ny, float nz ) +{ + m_VertexBuilder.Normal3f( nx, ny, nz ); +} + +FORCEINLINE void CMeshBuilder::Normal3fv( const float *n ) +{ + m_VertexBuilder.Normal3fv( n ); +} + +FORCEINLINE void CMeshBuilder::NormalDelta3f( float nx, float ny, float nz ) +{ + m_VertexBuilder.NormalDelta3f( nx, ny, nz ); +} + +FORCEINLINE void CMeshBuilder::NormalDelta3fv( const float *n ) +{ + m_VertexBuilder.NormalDelta3fv( n ); +} + +FORCEINLINE void CMeshBuilder::Color3f( float r, float g, float b ) +{ + m_VertexBuilder.Color3f( r, g, b ); +} + +FORCEINLINE void CMeshBuilder::Color3fv( const float *rgb ) +{ + m_VertexBuilder.Color3fv( rgb ); +} + +FORCEINLINE void CMeshBuilder::Color4f( float r, float g, float b, float a ) +{ + m_VertexBuilder.Color4f( r, g ,b, a ); +} + +FORCEINLINE void CMeshBuilder::Color4fv( const float *rgba ) +{ + m_VertexBuilder.Color4fv( rgba ); +} + +FORCEINLINE void CMeshBuilder::Color3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + m_VertexBuilder.Color3ub( r, g, b ); +} + +FORCEINLINE void CMeshBuilder::Color3ubv( unsigned char const* rgb ) +{ + m_VertexBuilder.Color3ubv( rgb ); +} + +FORCEINLINE void CMeshBuilder::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + m_VertexBuilder.Color4ub( r, g, b, a ); +} + +FORCEINLINE void CMeshBuilder::Color4ubv( unsigned char const* rgba ) +{ + m_VertexBuilder.Color4ubv( rgba ); +} + +FORCEINLINE void CMeshBuilder::Color4Packed( int packedColor ) +{ + m_VertexBuilder.Color4Packed(packedColor); +} + +FORCEINLINE int CMeshBuilder::PackColor4( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + return m_VertexBuilder.PackColor4(r,g,b,a); +} + +FORCEINLINE void CMeshBuilder::Specular3f( float r, float g, float b ) +{ + m_VertexBuilder.Specular3f( r, g, b ); +} + +FORCEINLINE void CMeshBuilder::Specular3fv( const float *rgb ) +{ + m_VertexBuilder.Specular3fv( rgb ); +} + +FORCEINLINE void CMeshBuilder::Specular4f( float r, float g, float b, float a ) +{ + m_VertexBuilder.Specular4f( r, g, b, a ); +} + +FORCEINLINE void CMeshBuilder::Specular4fv( const float *rgba ) +{ + m_VertexBuilder.Specular4fv( rgba ); +} + +FORCEINLINE void CMeshBuilder::Specular3ub( unsigned char r, unsigned char g, unsigned char b ) +{ + m_VertexBuilder.Specular3ub( r, g, b ); +} + +FORCEINLINE void CMeshBuilder::Specular3ubv( unsigned char const *c ) +{ + m_VertexBuilder.Specular3ubv( c ); +} + +FORCEINLINE void CMeshBuilder::Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) +{ + m_VertexBuilder.Specular4ub( r, g, b, a ); +} + +FORCEINLINE void CMeshBuilder::Specular4ubv( unsigned char const *c ) +{ + m_VertexBuilder.Specular4ubv( c ); +} + +FORCEINLINE void CMeshBuilder::TexCoord1f( int nStage, float s ) +{ + m_VertexBuilder.TexCoord1f( nStage, s ); +} + +FORCEINLINE void CMeshBuilder::TexCoord2f( int nStage, float s, float t ) +{ + m_VertexBuilder.TexCoord2f( nStage, s, t ); +} + +FORCEINLINE void CMeshBuilder::TexCoord2fv( int nStage, const float *st ) +{ + m_VertexBuilder.TexCoord2fv( nStage, st ); +} + +FORCEINLINE void CMeshBuilder::TexCoord3f( int nStage, float s, float t, float u ) +{ + m_VertexBuilder.TexCoord3f( nStage, s, t, u ); +} + +FORCEINLINE void CMeshBuilder::TexCoord3fv( int nStage, const float *stu ) +{ + m_VertexBuilder.TexCoord3fv( nStage, stu ); +} + +FORCEINLINE void CMeshBuilder::TexCoord4f( int nStage, float s, float t, float u, float v ) +{ + m_VertexBuilder.TexCoord4f( nStage, s, t, u, v ); +} + +FORCEINLINE void CMeshBuilder::TexCoord4fv( int nStage, const float *stuv ) +{ + m_VertexBuilder.TexCoord4fv( nStage, stuv ); +} + +FORCEINLINE void CMeshBuilder::TexCoordSubRect2f( int nStage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ) +{ + m_VertexBuilder.TexCoordSubRect2f( nStage, s, t, offsetS, offsetT, scaleS, scaleT ); +} + +FORCEINLINE void CMeshBuilder::TexCoordSubRect2fv( int nStage, const float *st, const float *offset, const float *scale ) +{ + m_VertexBuilder.TexCoordSubRect2fv( nStage, st, offset, scale ); +} + +FORCEINLINE void CMeshBuilder::TangentS3f( float sx, float sy, float sz ) +{ + m_VertexBuilder.TangentS3f( sx, sy, sz ); +} + +FORCEINLINE void CMeshBuilder::TangentS3fv( const float* s ) +{ + m_VertexBuilder.TangentS3fv( s ); +} + +FORCEINLINE void CMeshBuilder::TangentT3f( float tx, float ty, float tz ) +{ + m_VertexBuilder.TangentT3f( tx, ty, tz ); +} + +FORCEINLINE void CMeshBuilder::TangentT3fv( const float* t ) +{ + m_VertexBuilder.TangentT3fv( t ); +} + +FORCEINLINE void CMeshBuilder::Wrinkle1f( float flWrinkle ) +{ + m_VertexBuilder.Wrinkle1f( flWrinkle ); +} + +FORCEINLINE void CMeshBuilder::BoneWeight( int nIndex, float flWeight ) +{ + m_VertexBuilder.BoneWeight( nIndex, flWeight ); +} + +FORCEINLINE void CMeshBuilder::BoneWeights2( float weight1, float weight2 ) +{ + m_VertexBuilder.BoneWeights2( weight1, weight2 ); +} + +template FORCEINLINE void CMeshBuilder::CompressedBoneWeight3fv( const float * pWeights ) +{ + m_VertexBuilder.CompressedBoneWeight3fv( pWeights ); +} + +FORCEINLINE void CMeshBuilder::BoneMatrix( int nIndex, int nMatrixIdx ) +{ + m_VertexBuilder.BoneMatrix( nIndex, nMatrixIdx ); +} + +FORCEINLINE void CMeshBuilder::BoneMatrices4( int matrixIdx0, int matrixIdx1, int matrixIdx2, int matrixIdx3 ) +{ + m_VertexBuilder.BoneMatrices4( matrixIdx0, matrixIdx1, matrixIdx2, matrixIdx3 ); +} + +FORCEINLINE void CMeshBuilder::UserData( const float* pData ) +{ + m_VertexBuilder.UserData( pData ); +} + +template FORCEINLINE void CMeshBuilder::CompressedUserData( const float* pData ) +{ + m_VertexBuilder.CompressedUserData( pData ); +} + +//----------------------------------------------------------------------------- +// Templatized vertex field setting methods which support compression +//----------------------------------------------------------------------------- + +template FORCEINLINE void CMeshBuilder::CompressedNormal3f( float nx, float ny, float nz ) +{ + m_VertexBuilder.CompressedNormal3f( nx, ny, nz ); +} + +template FORCEINLINE void CMeshBuilder::CompressedNormal3fv( const float *n ) +{ + m_VertexBuilder.CompressedNormal3fv( n ); +} + +#endif // IMESH_H diff --git a/public/materialsystem/imorph.h b/public/materialsystem/imorph.h new file mode 100644 index 0000000..73d9814 --- /dev/null +++ b/public/materialsystem/imorph.h @@ -0,0 +1,251 @@ +//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// $Header: $ +// $NoKeywords: $ +// +// Interface used to construct morph buffers +//============================================================================= + +#ifndef IMORPH_H +#define IMORPH_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" +#include +#include "tier0/dbg.h" +#include "materialsystem/imaterial.h" + + +//----------------------------------------------------------------------------- +// Single morph data +//----------------------------------------------------------------------------- +struct MorphVertexInfo_t +{ + int m_nVertexId; // What vertex is this going to affect? + int m_nMorphTargetId; // What morph did it come from? + Vector m_PositionDelta; // Positional morph delta + Vector m_NormalDelta; // Normal morph delta + float m_flWrinkleDelta; // Wrinkle morph delta + float m_flSpeed; + float m_flSide; +}; + + +//----------------------------------------------------------------------------- +// Morph weight data +//----------------------------------------------------------------------------- +enum MorphWeightType_t +{ + MORPH_WEIGHT = 0, + MORPH_WEIGHT_LAGGED, + MORPH_WEIGHT_STEREO, + MORPH_WEIGHT_STEREO_LAGGED, + + MORPH_WEIGHT_COUNT, +}; + +struct MorphWeight_t +{ + float m_pWeight[MORPH_WEIGHT_COUNT]; +}; + + +//----------------------------------------------------------------------------- +// Interface to the morph +//----------------------------------------------------------------------------- +abstract_class IMorph +{ +public: + // Locks the morph, destroys any existing contents + virtual void Lock( float flFloatToFixedScale = 1.0f ) = 0; + + // Adds a morph + virtual void AddMorph( const MorphVertexInfo_t &info ) = 0; + + // Unlocks the morph + virtual void Unlock( ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Morph builders +//----------------------------------------------------------------------------- +class CMorphBuilder +{ +public: + CMorphBuilder(); + ~CMorphBuilder(); + + // Start building the morph + void Begin( IMorph *pMorph, float flFloatToFixedScale = 1.0f ); + + // End building the morph + void End(); + + void PositionDelta3fv( const float *pDelta ); + void PositionDelta3f( float dx, float dy, float dz ); + void PositionDelta3( const Vector &vec ); + + void NormalDelta3fv( const float *pDelta ); + void NormalDelta3f( float dx, float dy, float dz ); + void NormalDelta3( const Vector &vec ); + + void WrinkleDelta1f( float flWrinkle ); + + // Both are 0-1 values indicating which morph target to use (for stereo morph targets) + // and how much to blend between using lagged weights vs actual weights + // Speed: 0 - use lagged, 1 - use actual + void Speed1f( float flSpeed ); + void Side1f( float flSide ); + + void AdvanceMorph( int nSourceVertex, int nMorphTargetId ); + +private: + MorphVertexInfo_t m_Info; + IMorph *m_pMorph; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +inline CMorphBuilder::CMorphBuilder() +{ + m_pMorph = NULL; +} + +inline CMorphBuilder::~CMorphBuilder() +{ + // You forgot to call End()! + Assert( !m_pMorph ); +} + + +//----------------------------------------------------------------------------- +// Start building the morph +//----------------------------------------------------------------------------- +inline void CMorphBuilder::Begin( IMorph *pMorph, float flFloatToFixedScale ) +{ + Assert( pMorph && !m_pMorph ); + m_pMorph = pMorph; + m_pMorph->Lock( flFloatToFixedScale ); + +#ifdef _DEBUG + m_Info.m_PositionDelta.Init( VEC_T_NAN, VEC_T_NAN, VEC_T_NAN ); + m_Info.m_NormalDelta.Init( VEC_T_NAN, VEC_T_NAN, VEC_T_NAN ); + m_Info.m_flWrinkleDelta = VEC_T_NAN; + m_Info.m_flSpeed = VEC_T_NAN; + m_Info.m_flSide = VEC_T_NAN; +#endif +} + +// End building the morph +inline void CMorphBuilder::End() +{ + Assert( m_pMorph ); + m_pMorph->Unlock(); + m_pMorph = NULL; +} + + +//----------------------------------------------------------------------------- +// Set position delta +//----------------------------------------------------------------------------- +inline void CMorphBuilder::PositionDelta3fv( const float *pDelta ) +{ + Assert( m_pMorph ); + m_Info.m_PositionDelta.Init( pDelta[0], pDelta[1], pDelta[2] ); +} + +inline void CMorphBuilder::PositionDelta3f( float dx, float dy, float dz ) +{ + Assert( m_pMorph ); + m_Info.m_PositionDelta.Init( dx, dy, dz ); +} + +inline void CMorphBuilder::PositionDelta3( const Vector &vec ) +{ + Assert( m_pMorph ); + m_Info.m_PositionDelta = vec; +} + + +//----------------------------------------------------------------------------- +// Set normal delta +//----------------------------------------------------------------------------- +inline void CMorphBuilder::NormalDelta3fv( const float *pDelta ) +{ + Assert( m_pMorph ); + m_Info.m_NormalDelta.Init( pDelta[0], pDelta[1], pDelta[2] ); +} + +inline void CMorphBuilder::NormalDelta3f( float dx, float dy, float dz ) +{ + Assert( m_pMorph ); + m_Info.m_NormalDelta.Init( dx, dy, dz ); +} + +inline void CMorphBuilder::NormalDelta3( const Vector &vec ) +{ + Assert( m_pMorph ); + m_Info.m_NormalDelta = vec; +} + + +//----------------------------------------------------------------------------- +// Set wrinkle delta +//----------------------------------------------------------------------------- +inline void CMorphBuilder::WrinkleDelta1f( float flWrinkle ) +{ + Assert( m_pMorph ); + m_Info.m_flWrinkleDelta = flWrinkle; +} + + +//----------------------------------------------------------------------------- +// Set speed,side data +//----------------------------------------------------------------------------- +inline void CMorphBuilder::Speed1f( float flSpeed ) +{ + Assert( m_pMorph ); + m_Info.m_flSpeed = flSpeed; +} + +inline void CMorphBuilder::Side1f( float flSide ) +{ + Assert( m_pMorph ); + m_Info.m_flSide = flSide; +} + + +//----------------------------------------------------------------------------- +// Advance morph +//----------------------------------------------------------------------------- +inline void CMorphBuilder::AdvanceMorph( int nSourceVertex, int nMorphTargetId ) +{ + Assert( m_pMorph ); + + m_Info.m_nVertexId = nSourceVertex; + m_Info.m_nMorphTargetId = nMorphTargetId; + + m_pMorph->AddMorph( m_Info ); + +#ifdef _DEBUG + m_Info.m_PositionDelta.Init( VEC_T_NAN, VEC_T_NAN, VEC_T_NAN ); + m_Info.m_NormalDelta.Init( VEC_T_NAN, VEC_T_NAN, VEC_T_NAN ); + m_Info.m_flWrinkleDelta = VEC_T_NAN; + m_Info.m_flSpeed = VEC_T_NAN; + m_Info.m_flSide = VEC_T_NAN; +#endif +} + + +#endif // IMORPH_H diff --git a/public/materialsystem/ishaderapi.h b/public/materialsystem/ishaderapi.h new file mode 100644 index 0000000..30777cc --- /dev/null +++ b/public/materialsystem/ishaderapi.h @@ -0,0 +1,43 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: NOTE: This file is for backward compat! +// We'll get rid of it soon. Most of the contents of this file were moved +// into shaderpi/ishadershadow.h, shaderapi/ishaderdynamic.h, or +// shaderapi/shareddefs.h +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERAPI_MS_H +#define ISHADERAPI_MS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IMaterialVar; + + +//----------------------------------------------------------------------------- +// Methods that can be called from the SHADER_INIT blocks of shaders +//----------------------------------------------------------------------------- +abstract_class IShaderInit +{ +public: + // Loads up a texture + virtual void LoadTexture( IMaterialVar *pTextureVar, const char *pTextureGroupName ) = 0; + virtual void LoadBumpMap( IMaterialVar *pTextureVar, const char *pTextureGroupName ) = 0; + virtual void LoadCubeMap( IMaterialVar **ppParams, IMaterialVar *pTextureVar ) = 0; +}; + + +#endif // ISHADERAPI_MS_H diff --git a/public/materialsystem/itexture.h b/public/materialsystem/itexture.h new file mode 100644 index 0000000..dbd3767 --- /dev/null +++ b/public/materialsystem/itexture.h @@ -0,0 +1,141 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ITEXTURE_H +#define ITEXTURE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "bitmap/imageformat.h" // ImageFormat defn. + +class IVTFTexture; +class ITexture; +struct Rect_t; + +#ifdef _X360 +enum RTMultiSampleCount360_t +{ + RT_MULTISAMPLE_NONE = 0, + RT_MULTISAMPLE_2_SAMPLES = 2, + RT_MULTISAMPLE_4_SAMPLES = 4, + RT_MULTISAMPLE_MATCH_BACKBUFFER +}; +#endif + +//----------------------------------------------------------------------------- +// This will get called on procedural textures to re-fill the textures +// with the appropriate bit pattern. Calling Download() will also +// cause this interface to be called. It will also be called upon +// mode switch, or on other occasions where the bits are discarded. +//----------------------------------------------------------------------------- +abstract_class ITextureRegenerator +{ +public: + // This will be called when the texture bits need to be regenerated. + // Use the VTFTexture interface, which has been set up with the + // appropriate texture size + format + // The rect specifies which part of the texture needs to be updated + // You can choose to update all of the bits if you prefer + virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) = 0; + + // This will be called when the regenerator needs to be deleted + // which will happen when the texture is destroyed + virtual void Release() = 0; +}; + +abstract_class ITexture +{ +public: + // Various texture polling methods + virtual const char *GetName( void ) const = 0; + virtual int GetMappingWidth() const = 0; + virtual int GetMappingHeight() const = 0; + virtual int GetActualWidth() const = 0; + virtual int GetActualHeight() const = 0; + virtual int GetNumAnimationFrames() const = 0; + virtual bool IsTranslucent() const = 0; + virtual bool IsMipmapped() const = 0; + + virtual void GetLowResColorSample( float s, float t, float *color ) const = 0; + + // Gets texture resource data of the specified type. + // Params: + // eDataType type of resource to retrieve. + // pnumBytes on return is the number of bytes available in the read-only data buffer or is undefined + // Returns: + // pointer to the resource data, or NULL + virtual void *GetResourceData( uint32 eDataType, size_t *pNumBytes ) const = 0; + + // Methods associated with reference count + virtual void IncrementReferenceCount( void ) = 0; + virtual void DecrementReferenceCount( void ) = 0; + + inline void AddRef() { IncrementReferenceCount(); } + inline void Release() { DecrementReferenceCount(); } + + // Used to modify the texture bits (procedural textures only) + virtual void SetTextureRegenerator( ITextureRegenerator *pTextureRegen, bool releaseExisting = true ) = 0; + + // Reconstruct the texture bits in HW memory + + // If rect is not specified, reconstruct all bits, otherwise just + // reconstruct a subrect. + virtual void Download( Rect_t *pRect = 0 ) = 0; + + // Uses for stats. . .get the approximate size of the texture in it's current format. + virtual int GetApproximateVidMemBytes( void ) const = 0; + + // Returns true if the texture data couldn't be loaded. + virtual bool IsError() const = 0; + + // NOTE: Stuff after this is added after shipping HL2. + + // For volume textures + virtual bool IsVolumeTexture() const = 0; + virtual int GetMappingDepth() const = 0; + virtual int GetActualDepth() const = 0; + + virtual ImageFormat GetImageFormat() const = 0; + + // Various information about the texture + virtual bool IsRenderTarget() const = 0; + virtual bool IsCubeMap() const = 0; + virtual bool IsNormalMap() const = 0; + virtual bool IsProcedural() const = 0; + + virtual void DeleteIfUnreferenced() = 0; + +#if defined( _X360 ) + virtual bool ClearTexture( int r, int g, int b, int a ) = 0; + virtual bool CreateRenderTargetSurface( int width, int height, ImageFormat format, bool bSameAsTexture, RTMultiSampleCount360_t multiSampleCount = RT_MULTISAMPLE_NONE ) = 0; +#endif + + // swap everything except the name with another texture + virtual void SwapContents( ITexture *pOther ) = 0; + + // Retrieve the vtf flags mask + virtual unsigned int GetFlags( void ) const = 0; + + // Force LOD override (automatically downloads the texture) + virtual void ForceLODOverride( int iNumLodsOverrideUpOrDown ) = 0; + + // Force exclude override (automatically downloads the texture) + virtual void ForceExcludeOverride( int iExcludeOverride ) = 0; +}; + + +inline bool IsErrorTexture( ITexture *pTex ) +{ + return !pTex || pTex->IsError(); +} + + +#endif // ITEXTURE_H diff --git a/public/materialsystem/ivballoctracker.h b/public/materialsystem/ivballoctracker.h new file mode 100644 index 0000000..8788a63 --- /dev/null +++ b/public/materialsystem/ivballoctracker.h @@ -0,0 +1,34 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: tracks VB allocations (and compressed/uncompressed vertex memory usage) +// +//===========================================================================// + +#ifndef IVBALLOCTRACKER_H +#define IVBALLOCTRACKER_H + +#include "materialsystem/imaterialsystem.h" + +// By default, only enable this alloc tracking for a debug shaderapidx*.dll +// (it uses about 0.25MB to track ~7000 allocations) +#if defined(_DEBUG) +#define ENABLE_VB_ALLOC_TRACKER 1 +#else +#define ENABLE_VB_ALLOC_TRACKER 0 +#endif + +// This interface is actually exported by the shader API DLL. + +// Interface to the VB mem alloc tracker +abstract_class IVBAllocTracker +{ +public: + // This should be called wherever VertexBuffers are allocated + virtual void CountVB( void * buffer, bool isDynamic, int bufferSize, int vertexSize, VertexFormat_t fmt ) = 0; + // This should be called wherever VertexBuffers are freed + virtual void UnCountVB( void * buffer ) = 0; + // Track mesh allocations (set this before an allocation, clear it after) + virtual void TrackMeshAllocations( const char * allocatorName ) = 0; +}; + +#endif // IVBALLOCTRACKER_H diff --git a/public/materialsystem/materialsystem_config.h b/public/materialsystem/materialsystem_config.h new file mode 100644 index 0000000..f8c99ba --- /dev/null +++ b/public/materialsystem/materialsystem_config.h @@ -0,0 +1,200 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef MATERIALSYSTEM_CONFIG_H +#define MATERIALSYSTEM_CONFIG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "materialsystem/imaterialsystem.h" + +#define MATERIALSYSTEM_CONFIG_VERSION "VMaterialSystemConfig004" + +enum MaterialSystem_Config_Flags_t +{ + MATSYS_VIDCFG_FLAGS_WINDOWED = ( 1 << 0 ), + MATSYS_VIDCFG_FLAGS_RESIZING = ( 1 << 1 ), + MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC = ( 1 << 3 ), + MATSYS_VIDCFG_FLAGS_STENCIL = ( 1 << 4 ), + MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR = ( 1 << 7 ), + MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP = ( 1 << 8 ), + MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING = ( 1 << 9 ), + MATSYS_VIDCFG_FLAGS_USE_Z_PREFILL = ( 1 << 10 ), + MATSYS_VIDCFG_FLAGS_ENABLE_HDR = ( 1 << 12 ), + MATSYS_VIDCFG_FLAGS_LIMIT_WINDOWED_SIZE = ( 1 << 13 ), + MATSYS_VIDCFG_FLAGS_SCALE_TO_OUTPUT_RESOLUTION = ( 1 << 14 ), + MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS = ( 1 << 15 ), + MATSYS_VIDCFG_FLAGS_DISABLE_PHONG = ( 1 << 16 ), + MATSYS_VIDCFG_FLAGS_NO_WINDOW_BORDER = ( 1 << 17 ), +}; + +struct MaterialSystemHardwareIdentifier_t +{ + char *m_pCardName; + unsigned int m_nVendorID; + unsigned int m_nDeviceID; +}; + +struct MaterialSystem_Config_t +{ + bool Windowed() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_WINDOWED ) != 0; } + bool NoWindowBorder() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_NO_WINDOW_BORDER ) != 0; } + bool Resizing() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_RESIZING ) != 0; } + bool WaitForVSync() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC ) == 0; } + bool Stencil() const { return (m_Flags & MATSYS_VIDCFG_FLAGS_STENCIL ) != 0; } + bool UseSpecular() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR ) == 0; } + bool UseBumpmapping() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP ) == 0; } + bool UseParallaxMapping() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING ) != 0; } + bool UseZPrefill() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_USE_Z_PREFILL ) != 0; } + bool HDREnabled() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_ENABLE_HDR ) != 0; } + bool LimitWindowedSize() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_LIMIT_WINDOWED_SIZE ) != 0; } + bool ScaleToOutputResolution() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_SCALE_TO_OUTPUT_RESOLUTION ) != 0; } + bool UsingMultipleWindows() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS ) != 0; } + bool ShadowDepthTexture() const { return m_bShadowDepthTexture; } + bool MotionBlur() const { return m_bMotionBlur; } + bool SupportFlashlight() const { return m_bSupportFlashlight; } + bool UsePhong() const { return ( m_Flags & MATSYS_VIDCFG_FLAGS_DISABLE_PHONG ) == 0; } + + void SetFlag( unsigned int flag, bool val ) + { + if( val ) + { + m_Flags |= flag; + } + else + { + m_Flags &= ~flag; + } + } + + // control panel stuff + MaterialVideoMode_t m_VideoMode; + float m_fMonitorGamma; + float m_fGammaTVRangeMin; + float m_fGammaTVRangeMax; + float m_fGammaTVExponent; + bool m_bGammaTVEnabled; + + bool m_bWantTripleBuffered; // We only get triple buffering if fullscreen and vsync'd + int m_nAASamples; + int m_nForceAnisotropicLevel; + int skipMipLevels; + int dxSupportLevel; + unsigned int m_Flags; + bool bEditMode; // true if in Hammer. + unsigned char proxiesTestMode; // 0 = normal, 1 = no proxies, 2 = alpha test all, 3 = color mod all + bool bCompressedTextures; + bool bFilterLightmaps; + bool bFilterTextures; + bool bReverseDepth; + bool bBufferPrimitives; + bool bDrawFlat; + bool bMeasureFillRate; + bool bVisualizeFillRate; + bool bNoTransparency; + bool bSoftwareLighting; + bool bAllowCheats; + char nShowMipLevels; + bool bShowLowResImage; + bool bShowNormalMap; + bool bMipMapTextures; + unsigned char nFullbright; + bool m_bFastNoBump; + bool m_bSuppressRendering; + + // debug modes + bool bShowSpecular; // This is the fast version that doesn't require reloading materials + bool bShowDiffuse; // This is the fast version that doesn't require reloading materials + + uint m_WindowedSizeLimitWidth; + uint m_WindowedSizeLimitHeight; + int m_nAAQuality; + bool m_bShadowDepthTexture; + bool m_bMotionBlur; + bool m_bSupportFlashlight; + + bool m_bPaintInGame; + bool m_bPaintInMap; + + + MaterialSystem_Config_t() + { + memset( this, 0, sizeof( *this ) ); + + // video config defaults + SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_NO_WINDOW_BORDER, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_NO_WAIT_FOR_VSYNC, true ); + SetFlag( MATSYS_VIDCFG_FLAGS_STENCIL, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING, true ); + SetFlag( MATSYS_VIDCFG_FLAGS_USE_Z_PREFILL, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_LIMIT_WINDOWED_SIZE, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_SCALE_TO_OUTPUT_RESOLUTION, false ); + SetFlag( MATSYS_VIDCFG_FLAGS_USING_MULTIPLE_WINDOWS, false ); + + m_VideoMode.m_Width = 640; + m_VideoMode.m_Height = 480; + m_VideoMode.m_RefreshRate = 60; + dxSupportLevel = 0; + bCompressedTextures = true; + bFilterTextures = true; + bFilterLightmaps = true; + bMipMapTextures = true; + bBufferPrimitives = true; + + m_fMonitorGamma = 2.2f; + m_fGammaTVRangeMin = 16.0f; + m_fGammaTVRangeMax = 255.0f; + m_fGammaTVExponent = 2.5; + m_bGammaTVEnabled = IsX360(); + + m_bWantTripleBuffered = false; + m_nAASamples = 1; + m_bShadowDepthTexture = false; + m_bMotionBlur = false; + m_bSupportFlashlight = true; + + // misc defaults + bAllowCheats = false; + bCompressedTextures = true; + bEditMode = false; + + // debug modes + bShowSpecular = true; + bShowDiffuse = true; + nFullbright = 0; + bShowNormalMap = false; + bFilterLightmaps = true; + bFilterTextures = true; + bMipMapTextures = true; + nShowMipLevels = 0; + bShowLowResImage = false; + bReverseDepth = false; + bBufferPrimitives = true; + bDrawFlat = false; + bMeasureFillRate = false; + bVisualizeFillRate = false; + bSoftwareLighting = false; + bNoTransparency = false; + proxiesTestMode = 0; + m_bFastNoBump = false; + m_bSuppressRendering = false; + m_WindowedSizeLimitWidth = 1280; + m_WindowedSizeLimitHeight = 1024; + + // PAINT + m_bPaintInGame = false; + m_bPaintInMap = false; + } +}; + + +#endif // MATERIALSYSTEM_CONFIG_H diff --git a/public/materialsystem/meshreader.h b/public/materialsystem/meshreader.h new file mode 100644 index 0000000..f5688ee --- /dev/null +++ b/public/materialsystem/meshreader.h @@ -0,0 +1,268 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef MESHREADER_H +#define MESHREADER_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// This is used to read vertex and index data out of already-created meshes. +// xbox uses this a lot so it doesn't have to store sysmem backups of the +// vertex data. +//----------------------------------------------------------------------------- +class CBaseMeshReader : protected MeshDesc_t +{ +// Initialization. +public: + + CBaseMeshReader(); + ~CBaseMeshReader(); + + // Use BeginRead/EndRead to initialize the mesh reader. + void BeginRead( + IMesh* pMesh, + int firstVertex = 0, + int numVertices = 0, + int firstIndex = 0, + int numIndices = 0 ); + + void EndRead(); + + // PC can use this if it stores its own copy of meshes around, in case + // locking static buffers is too costly. + void BeginRead_Direct( const MeshDesc_t &desc, int numVertices, int nIndices ); + + // Resets the mesh builder so it points to the start of everything again + void Reset(); + + +protected: + IMesh *m_pMesh; + int m_MaxVertices; + int m_MaxIndices; +}; + + +// A bunch of accessors for the data that CBaseMeshReader sets up. +class CMeshReader : public CBaseMeshReader +{ +public: +// Access to vertex data. +public: + int NumIndices() const; + unsigned short Index( int index ) const; + + const Vector& Position( int iVertex ) const; + + unsigned int Color( int iVertex ) const; + + const float *TexCoord( int iVertex, int stage ) const; + void TexCoord2f( int iVertex, int stage, float &s, float &t ) const; + const Vector2D& TexCoordVector2D( int iVertex, int stage ) const; + + int NumBoneWeights() const; + float Wrinkle( int iVertex ) const; + + const Vector &Normal( int iVertex ) const; + void Normal( int iVertex, Vector &vNormal ) const; + + const Vector &TangentS( int iVertex ) const; + const Vector &TangentT( int iVertex ) const; + float BoneWeight( int iVertex ) const; + +#ifdef NEW_SKINNING + float* BoneMatrix( int iVertex ) const; +#else + unsigned char* BoneMatrix( int iVertex ) const; +#endif +}; + + +//----------------------------------------------------------------------------- +// CBaseMeshReader implementation. +//----------------------------------------------------------------------------- + +inline CBaseMeshReader::CBaseMeshReader() +{ + m_pMesh = NULL; +} + +inline CBaseMeshReader::~CBaseMeshReader() +{ + Assert( !m_pMesh ); +} + +inline void CBaseMeshReader::BeginRead( + IMesh* pMesh, + int firstVertex, + int numVertices, + int firstIndex, + int numIndices ) +{ + Assert( pMesh && (!m_pMesh) ); + + if ( numVertices < 0 ) + { + numVertices = pMesh->VertexCount(); + } + + if ( numIndices < 0 ) + { + numIndices = pMesh->IndexCount(); + } + + m_pMesh = pMesh; + m_MaxVertices = numVertices; + m_MaxIndices = numIndices; + + // UNDONE: support reading from compressed VBs if needed + VertexCompressionType_t compressionType = CompressionType( pMesh->GetVertexFormat() ); + Assert( compressionType == VERTEX_COMPRESSION_NONE ); + if ( compressionType != VERTEX_COMPRESSION_NONE ) + { + Warning( "Cannot use CBaseMeshReader with compressed vertices! Will get junk data or a crash.\n" ); + } + + // Locks mesh for modifying + pMesh->ModifyBeginEx( true, firstVertex, numVertices, firstIndex, numIndices, *this ); + + // Point to the start of the buffers.. + Reset(); +} + +inline void CBaseMeshReader::EndRead() +{ + Assert( m_pMesh ); + m_pMesh->ModifyEnd( *this ); + m_pMesh = NULL; +} + +inline void CBaseMeshReader::BeginRead_Direct( const MeshDesc_t &desc, int nVertices, int nIndices ) +{ + MeshDesc_t *pThis = this; + *pThis = desc; + m_MaxVertices = nVertices; + m_MaxIndices = nIndices; + + // UNDONE: support reading from compressed verts if necessary + Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); + if ( desc.m_CompressionType != VERTEX_COMPRESSION_NONE ) + { + Warning( "Cannot use CBaseMeshReader with compressed vertices!\n" ); + } +} + +inline void CBaseMeshReader::Reset() +{ +} + + + + +// -------------------------------------------------------------------------------------- // +// CMeshReader implementation. +// -------------------------------------------------------------------------------------- // + +inline int CMeshReader::NumIndices() const +{ + return m_MaxIndices; +} + +inline unsigned short CMeshReader::Index( int index ) const +{ + Assert( (index >= 0) && (index < m_MaxIndices) ); + return m_pIndices[index * m_nIndexSize]; +} + +inline const Vector& CMeshReader::Position( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return *(Vector*)((char*)m_pPosition + iVertex * m_VertexSize_Position); +} + +inline unsigned int CMeshReader::Color( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + unsigned char *pColor = m_pColor + iVertex * m_VertexSize_Color; + return (pColor[0] << 16) | (pColor[1] << 8) | (pColor[2]) | (pColor[3] << 24); +} + +inline const float *CMeshReader::TexCoord( int iVertex, int iStage ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return (float*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] ); +} + +inline void CMeshReader::TexCoord2f( int iVertex, int iStage, float &s, float &t ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + float *p = (float*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] ); + s = p[0]; + t = p[1]; +} + +inline const Vector2D& CMeshReader::TexCoordVector2D( int iVertex, int iStage ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + Vector2D *p = (Vector2D*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] ); + return *p; +} + +inline float CMeshReader::Wrinkle( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return *(float*)( (char*)m_pWrinkle + iVertex * m_VertexSize_Wrinkle ); +} + +inline int CMeshReader::NumBoneWeights() const +{ + return m_NumBoneWeights; +} + +inline const Vector &CMeshReader::Normal( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return *(const Vector *)(const float*)( (char*)m_pNormal + iVertex * m_VertexSize_Normal ); +} + +inline void CMeshReader::Normal( int iVertex, Vector &vNormal ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + const float *p = (const float*)( (char*)m_pNormal + iVertex * m_VertexSize_Normal ); + vNormal.Init( p[0], p[1], p[2] ); +} + +inline const Vector &CMeshReader::TangentS( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return *(const Vector*)( (char*)m_pTangentS + iVertex * m_VertexSize_TangentS ); +} + +inline const Vector &CMeshReader::TangentT( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + return *(const Vector*)( (char*)m_pTangentT + iVertex * m_VertexSize_TangentT ); +} + +inline float CMeshReader::BoneWeight( int iVertex ) const +{ + Assert( iVertex >= 0 && iVertex < m_MaxVertices ); + float *p = (float*)( (char*)m_pBoneWeight + iVertex * m_VertexSize_BoneWeight ); + return *p; +} + +#endif // MESHREADER_H + + + + + + + diff --git a/public/materialsystem/shader_vcs_version.h b/public/materialsystem/shader_vcs_version.h new file mode 100644 index 0000000..903e34f --- /dev/null +++ b/public/materialsystem/shader_vcs_version.h @@ -0,0 +1,75 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SHADER_VCS_VERSION_H +#define SHADER_VCS_VERSION_H +#ifdef _WIN32 +#pragma once +#endif + +// 1 = hl2 shipped +// 2 = compressed with diffs version (lostcoast) +// 3 = compressed with bzip +// 4 = v2 + crc32 +// 5 = v3 + crc32 +// 6 = v5 + duplicate static combo records +#define SHADER_VCS_VERSION_NUMBER 6 + +#define MAX_SHADER_UNPACKED_BLOCK_SIZE (1<<17) +#define MAX_SHADER_PACKED_SIZE (1+MAX_SHADER_UNPACKED_BLOCK_SIZE) + +#pragma pack(1) +struct ShaderHeader_t +{ + int32 m_nVersion; + int32 m_nTotalCombos; + int32 m_nDynamicCombos; + uint32 m_nFlags; + uint32 m_nCentroidMask; + uint32 m_nNumStaticCombos; // includes sentinal key + uint32 m_nSourceCRC32; // NOTE: If you move this, update copyshaders.pl, *_prep.pl, updateshaders.pl +}; +#pragma pack() + +#pragma pack(1) +struct ShaderHeader_t_v4 // still used for assembly shaders +{ + int32 m_nVersion; + int32 m_nTotalCombos; + int32 m_nDynamicCombos; + uint32 m_nFlags; + uint32 m_nCentroidMask; + uint32 m_nDiffReferenceSize; + uint32 m_nSourceCRC32; // NOTE: If you move this, update copyshaders.pl, *_prep.pl, updateshaders.pl +}; +#pragma pack() + +// for old format files +struct ShaderDictionaryEntry_t +{ + int m_Offset; + int m_Size; +}; + +// record for one static combo +struct StaticComboRecord_t +{ + uint32 m_nStaticComboID; + uint32 m_nFileOffset; +}; + + +struct StaticComboAliasRecord_t // for duplicate static combos +{ + uint32 m_nStaticComboID; // this combo + uint32 m_nSourceStaticCombo; // the combo it is the same as +}; + + + + +#endif // SHADER_VCS_VERSION_H + diff --git a/public/mathlib/IceKey.H b/public/mathlib/IceKey.H new file mode 100644 index 0000000..140fd16 --- /dev/null +++ b/public/mathlib/IceKey.H @@ -0,0 +1,66 @@ +// Purpose: Header file for the C++ ICE encryption class. +// Taken from public domain code, as written by Matthew Kwan - July 1996 +// http://www.darkside.com.au/ice/ + +#ifndef _IceKey_H +#define _IceKey_H + +/* +The IceKey class is used for encrypting and decrypting 64-bit blocks of data +with the ICE (Information Concealment Engine) encryption algorithm. + +The constructor creates a new IceKey object that can be used to encrypt and decrypt data. +The level of encryption determines the size of the key, and hence its speed. +Level 0 uses the Thin-ICE variant, which is an 8-round cipher taking an 8-byte key. +This is the fastest option, and is generally considered to be at least as secure as DES, +although it is not yet certain whether it is as secure as its key size. + +For levels n greater than zero, a 16n-round cipher is used, taking 8n-byte keys. +Although not as fast as level 0, these are very very secure. + +Before an IceKey can be used to encrypt data, its key schedule must be set with the set() member function. +The length of the key required is determined by the level, as described above. + +The member functions encrypt() and decrypt() encrypt and decrypt respectively data +in blocks of eight characters, using the specified key. + +Two functions keySize() and blockSize() are provided +which return the key and block size respectively, measured in bytes. +The key size is determined by the level, while the block size is always 8. + +The destructor zeroes out and frees up all memory associated with the key. +*/ + +class IceSubkey; + +class IceKey { + public: + IceKey (int n); + ~IceKey (); + + void set (const unsigned char *key); + + void encrypt (const unsigned char *plaintext, + unsigned char *ciphertext) const; + + void decrypt (const unsigned char *ciphertext, + unsigned char *plaintext) const; + + int keySize () const; + + int blockSize () const; + + private: + void scheduleBuild (unsigned short *k, int n, + const int *keyrot); + + int _size; + int _rounds; + IceSubkey *_keysched; +}; + +// Valve-written routine to decode a buffer +void DecodeICE( unsigned char *pBuffer, int nSize, const unsigned char *pKey ); + + +#endif // IceKey_H diff --git a/public/mathlib/anorms.h b/public/mathlib/anorms.h new file mode 100644 index 0000000..b27e5ce --- /dev/null +++ b/public/mathlib/anorms.h @@ -0,0 +1,25 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ANORMS_H +#define ANORMS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/vector.h" + + +#define NUMVERTEXNORMALS 162 + +// the angle between consecutive g_anorms[] vectors is ~14.55 degrees +#define VERTEXNORMAL_CONE_INNER_ANGLE DEG2RAD(7.275) + +extern Vector g_anorms[NUMVERTEXNORMALS]; + + +#endif // ANORMS_H diff --git a/public/mathlib/bumpvects.h b/public/mathlib/bumpvects.h new file mode 100644 index 0000000..6b859ad --- /dev/null +++ b/public/mathlib/bumpvects.h @@ -0,0 +1,37 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUMPVECTS_H +#define BUMPVECTS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/mathlib.h" + +#define OO_SQRT_2 0.70710676908493042f +#define OO_SQRT_3 0.57735025882720947f +#define OO_SQRT_6 0.40824821591377258f +// sqrt( 2 / 3 ) +#define OO_SQRT_2_OVER_3 0.81649661064147949f + +#define NUM_BUMP_VECTS 3 + +const TableVector g_localBumpBasis[NUM_BUMP_VECTS] = +{ + { OO_SQRT_2_OVER_3, 0.0f, OO_SQRT_3 }, + { -OO_SQRT_6, OO_SQRT_2, OO_SQRT_3 }, + { -OO_SQRT_6, -OO_SQRT_2, OO_SQRT_3 } +}; + +void GetBumpNormals( const Vector& sVect, const Vector& tVect, const Vector& flatNormal, + const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] ); + +#endif // BUMPVECTS_H diff --git a/public/mathlib/camera.h b/public/mathlib/camera.h new file mode 100644 index 0000000..6e6b8bc --- /dev/null +++ b/public/mathlib/camera.h @@ -0,0 +1,302 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef CAMERA_H +#define CAMERA_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// For vec_t, put this somewhere else? +#include "tier0/basetypes.h" +#include "mathlib/vector.h" + +#include "tier0/dbg.h" +#include "mathlib/vector2d.h" +#include "mathlib/math_pfns.h" +#include "mathlib/vmatrix.h" +#include "mathlib/ssemath.h" +#include "datamap.h" + +#include "tier0/memalloc.h" +// declarations for camera and frustum + +struct Camera_t +{ + Vector m_origin; + QAngle m_angles; + float m_flFOVX; // FOV for X/width + float m_flZNear; + float m_flZFar; +}; + + +//----------------------------------------------------------------------------- +// accessors for generated matrices +//----------------------------------------------------------------------------- +void ComputeViewMatrix( VMatrix *pWorldToView, const Camera_t& camera ); +void ComputeViewMatrix( matrix3x4_t *pWorldToView, const Camera_t& camera ); +void ComputeViewMatrix( matrix3x4_t *pWorldToView, matrix3x4_t *pWorldToCamera, const Camera_t &camera ); +void ComputeProjectionMatrix( VMatrix *pCameraToProjection, const Camera_t& camera, int width, int height ); +void ComputeProjectionMatrix( VMatrix *pCameraToProjection, float flZNear, float flZFar, float flFOVX, float flAspectRatio ); + + +//----------------------------------------------------------------------------- +// Computes the screen space position given a screen size +//----------------------------------------------------------------------------- +void ComputeScreenSpacePosition( Vector2D *pScreenPosition, const Vector &vecWorldPosition, + const Camera_t &camera, int width, int height ); + + +//-------------------------------------------------------------------------------------- +// AABB +//-------------------------------------------------------------------------------------- +struct AABB_t +{ + DECLARE_BYTESWAP_DATADESC(); + + Vector m_vMinBounds; + Vector m_vMaxBounds; + + Vector GetCenter() const { return ( m_vMaxBounds + m_vMinBounds ) / 2.0f; } + + float GetMinDistToPoint( const Vector &vPoint ) const + { + return CalcDistanceToAABB( m_vMinBounds, m_vMaxBounds, vPoint ); + } + + void CreatePlanesFrom( Vector4D *pPlanes ) const + { + // X + pPlanes[0] = Vector4D( 1, 0, 0, -m_vMaxBounds.x ); + pPlanes[1] = Vector4D( -1, 0, 0, m_vMinBounds.x ); + + // Y + pPlanes[2] = Vector4D( 0, 1, 0, -m_vMaxBounds.y ); + pPlanes[3] = Vector4D( 0, -1, 0, m_vMinBounds.y ); + + // Z + pPlanes[4] = Vector4D( 0, 0, 1, -m_vMaxBounds.z ); + pPlanes[5] = Vector4D( 0, 0, -1, m_vMinBounds.z ); + } + + // set the aabb to be invalid (max < min ) + void MakeInvalid( void ) + { + m_vMinBounds.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + m_vMaxBounds.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + } + +}; + +class CFrustum +{ +public: + CFrustum() + { + } + + ~CFrustum() + { + } + + bool BoundingVolumeIntersectsFrustum( AABB_t const &box ) const + { + Vector vMins = box.m_vMinBounds - m_camera.m_origin; + Vector vMaxs = box.m_vMaxBounds - m_camera.m_origin; + return !m_frustum.CullBox( vMins, vMaxs ); + } + + bool BoundingVolumeIntersectsFrustum( Vector const &mins, Vector const &maxes ) const + { + Vector vMins = mins - m_camera.m_origin; + Vector vMaxs = maxes - m_camera.m_origin; + return !m_frustum.CullBox( vMins, vMaxs ); + } + + bool BoundingVolumeIntersectsFrustum( AABB_t const &box, Vector &vOriginShift ) const + { + Vector vMins = box.m_vMinBounds - m_camera.m_origin - vOriginShift; + Vector vMaxs = box.m_vMaxBounds - m_camera.m_origin - vOriginShift; + return !m_frustum.CullBox( vMins, vMaxs ); + } + + FORCEINLINE bool CheckBoxInline( const VectorAligned &mins, const VectorAligned &maxs ) + { + fltx4 fl4Origin = LoadAlignedSIMD( &m_camera.m_origin ); + fltx4 mins4 = SubSIMD( LoadAlignedSIMD( &mins.x ), fl4Origin ); + fltx4 maxs4 = SubSIMD( LoadAlignedSIMD( &maxs.x ), fl4Origin ); + + fltx4 minx = SplatXSIMD(mins4); + fltx4 miny = SplatYSIMD(mins4); + fltx4 minz = SplatZSIMD(mins4); + fltx4 maxx = SplatXSIMD(maxs4); + fltx4 maxy = SplatYSIMD(maxs4); + fltx4 maxz = SplatZSIMD(maxs4); + + // compute the dot product of the normal and the farthest corner + // dotBack0 = DotProduct( normal, normals.x < 0 ? mins.x : maxs.x ); + for ( int i = 0; i < 2; i++ ) + { + fltx4 xTotalBack = MulSIMD( m_frustum.planes[i].nX, MaskedAssign( m_frustum.planes[i].xSign, minx, maxx ) ); + fltx4 yTotalBack = MulSIMD( m_frustum.planes[i].nY, MaskedAssign( m_frustum.planes[i].ySign, miny, maxy ) ); + fltx4 zTotalBack = MulSIMD( m_frustum.planes[i].nZ, MaskedAssign( m_frustum.planes[i].zSign, minz, maxz ) ); + fltx4 dotBack = AddSIMD( xTotalBack, AddSIMD(yTotalBack, zTotalBack) ); + // if plane of the farthest corner is behind the plane, then the box is completely outside this plane +#if _X360 + if ( !XMVector3GreaterOrEqual( dotBack, m_frustum.planes[i].dist ) ) + return false; +#else + fltx4 isOut = CmpLtSIMD( dotBack, m_frustum.planes[i].dist ); + if ( IsAnyNegative(isOut) ) + return false; +#endif + } + return true; + } + + void GetNearAndFarPlanesAroundBox( float *pNear, float *pFar, AABB_t const &inBox, Vector &vOriginShift ) const + { + AABB_t box = inBox; + box.m_vMinBounds -= vOriginShift; + box.m_vMaxBounds -= vOriginShift; + + Vector vCorners[8]; + vCorners[0] = box.m_vMinBounds; + vCorners[1] = Vector( box.m_vMinBounds.x, box.m_vMinBounds.y, box.m_vMaxBounds.z ); + vCorners[2] = Vector( box.m_vMinBounds.x, box.m_vMaxBounds.y, box.m_vMinBounds.z ); + vCorners[3] = Vector( box.m_vMinBounds.x, box.m_vMaxBounds.y, box.m_vMaxBounds.z ); + + vCorners[4] = Vector( box.m_vMaxBounds.x, box.m_vMinBounds.y, box.m_vMinBounds.z ); + vCorners[5] = Vector( box.m_vMaxBounds.x, box.m_vMinBounds.y, box.m_vMaxBounds.z ); + vCorners[6] = Vector( box.m_vMaxBounds.x, box.m_vMaxBounds.y, box.m_vMinBounds.z ); + vCorners[7] = box.m_vMaxBounds; + + float flNear = FLT_MAX;//m_camera.m_flZNear; + float flFar = -FLT_MAX;//m_camera.m_flZFar; + for ( int i=0; i<8; ++i ) + { + Vector vDelta = vCorners[i] - m_camera.m_origin; + float flDist = DotProduct( m_forward, vDelta ); + flNear = MIN( flNear, flDist ); + flFar = MAX( flFar, flDist ); + } + + *pNear = flNear; + *pFar = flFar; + } + + void UpdateFrustumFromCamera() + { + ComputeViewMatrix( &m_worldToView, &m_cameraToWorld, m_camera ); + ComputeProjectionMatrix( &m_projection, m_camera.m_flZNear, m_camera.m_flZFar, m_camera.m_flFOVX, m_flAspect ); + + m_viewProj = (m_projection * VMatrix(m_worldToView)).Transpose(); + MatrixGetColumn( m_cameraToWorld, 0, m_forward ); + MatrixGetColumn( m_cameraToWorld, 1, m_left ); + MatrixGetColumn( m_cameraToWorld, 2, m_up ); + + // NOTE: Don't pass camera origin here. Compute it locally so that voxels can be translated here + // and the plane constants retain precision over large world coordinate spaces + // Also the voxel renderer needs the camera information anyway to compute LOD so this is + // convenient + GeneratePerspectiveFrustum( vec3_origin, m_camera.m_angles, m_camera.m_flZNear, m_camera.m_flZFar, m_camera.m_flFOVX, m_flAspect, m_frustum ); + } + + + + // Just cram this here since until I figure out how to convert from direction to angles + static VMatrix ViewMatrixRH( Vector &vEye, Vector &vAt, Vector &vUp ) + { + Vector xAxis, yAxis; + Vector zAxis = vEye - vAt; + xAxis = CrossProduct( vUp, zAxis ); + yAxis = CrossProduct( zAxis, xAxis ); + xAxis.NormalizeInPlace(); + yAxis.NormalizeInPlace(); + zAxis.NormalizeInPlace(); + float flDotX = -DotProduct( xAxis, vEye ); + float flDotY = -DotProduct( yAxis, vEye ); + float flDotZ = -DotProduct( zAxis, vEye ); + VMatrix mRet( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + flDotX, flDotY, flDotZ, 1 ); + return mRet.Transpose(); + } + + VMatrix OrthoMatrixRH( float flWidth, float flHeight, float flNear, float flFar ) + { + float flDelta = flNear - flFar; + + VMatrix mRet( + 2.0f / flWidth, 0, 0, 0, + 0, 2.0f / flHeight, 0, 0, + 0, 0, 1.0f / flDelta, 0, + 0, 0, flNear / flDelta, 1 ); + return mRet.Transpose(); + } + + + const Vector &Forward() const { return m_forward; } + const Vector &Left() const { return m_left; } + const Vector &Up() const { return m_up; } + + void SetView( VMatrix &mView ) { m_worldToView = mView.As3x4(); } + void SetProj( VMatrix &mProj ) { m_projection = mProj; } + void CalcViewProj( ) { m_viewProj = (m_projection * VMatrix(m_worldToView)).Transpose(); } + void SetFrustum( const Frustum_t &frustum ) { m_frustum = frustum; } + void SetForward( Vector &forward ) { m_forward = forward; } + void SetNearFarPlanes( float flNear, float flFar ) { m_camera.m_flZNear = flNear; m_camera.m_flZFar = flFar; } + void SetCameraPosition( const Vector &origin ) { m_camera.m_origin = origin; } + void SetCameraAngles( const QAngle &angles ) { m_camera.m_angles = angles; } + void SetAspect( float flAspect ) { m_flAspect = flAspect; } + void SetFOV( float flFOV ) { m_camera.m_flFOVX = flFOV; } + + const Vector &GetCameraPosition() const { return m_camera.m_origin; } + const QAngle &GetCameraAngles() const { return m_camera.m_angles; } + const matrix3x4_t &GetView() const { return m_worldToView; } + const VMatrix &GetProj() const { return m_projection; } + const VMatrix &GetViewProj() const { return m_viewProj; } + float GetAspect() const { return m_flAspect; } + float GetNearPlane() const { return m_camera.m_flZNear; } + float GetFarPlane() const { return m_camera.m_flZFar; } + float GetFOV() const { return m_camera.m_flFOVX; } + + void InitCamera( const Vector &origin, const QAngle &angles, float flNear, float flFar, float flFOV, float flAspect ) + { + m_camera.m_origin = origin; + m_camera.m_angles = angles; + m_camera.m_flZNear = flNear; + m_camera.m_flZFar = flFar; + m_camera.m_flFOVX = flFOV; + m_flAspect = flAspect; + } + +protected: + ALIGN16 Frustum_t m_frustum; + ALIGN16 Camera_t m_camera; + float m_flAspect; + Vector m_forward; + Vector m_left; + Vector m_up; + + matrix3x4_t m_cameraToWorld; + matrix3x4_t m_worldToView; + VMatrix m_viewToWorld; + VMatrix m_projection; + VMatrix m_viewProj; +}; + +#endif // CAMERA_H + diff --git a/public/mathlib/compressed_3d_unitvec.h b/public/mathlib/compressed_3d_unitvec.h new file mode 100644 index 0000000..4adcc1f --- /dev/null +++ b/public/mathlib/compressed_3d_unitvec.h @@ -0,0 +1,284 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef _3D_UNITVEC_H +#define _3D_UNITVEC_H + + +#define UNITVEC_DECLARE_STATICS \ + float cUnitVector::mUVAdjustment[0x2000]; \ + Vector cUnitVector::mTmpVec; + +// upper 3 bits +#define SIGN_MASK 0xe000 +#define XSIGN_MASK 0x8000 +#define YSIGN_MASK 0x4000 +#define ZSIGN_MASK 0x2000 + +// middle 6 bits - xbits +#define TOP_MASK 0x1f80 + +// lower 7 bits - ybits +#define BOTTOM_MASK 0x007f + +// unitcomp.cpp : A Unit Vector to 16-bit word conversion +// algorithm based on work of Rafael Baptista (rafael@oroboro.com) +// Accuracy improved by O.D. (punkfloyd@rocketmail.com) +// Used with Permission. + +// a compressed unit vector. reasonable fidelty for unit +// vectors in a 16 bit package. Good enough for surface normals +// we hope. +class cUnitVector // : public c3dMathObject +{ +public: + cUnitVector() { mVec = 0; } + cUnitVector( const Vector& vec ) + { + packVector( vec ); + } + cUnitVector( unsigned short val ) { mVec = val; } + + cUnitVector& operator=( const Vector& vec ) + { packVector( vec ); return *this; } + + operator Vector() + { + unpackVector( mTmpVec ); + return mTmpVec; + } + + void packVector( const Vector& vec ) + { + // convert from Vector to cUnitVector + + Assert( vec.IsValid()); + Vector tmp = vec; + + // input vector does not have to be unit length + // Assert( tmp.length() <= 1.001f ); + + mVec = 0; + if ( tmp.x < 0 ) { mVec |= XSIGN_MASK; tmp.x = -tmp.x; } + if ( tmp.y < 0 ) { mVec |= YSIGN_MASK; tmp.y = -tmp.y; } + if ( tmp.z < 0 ) { mVec |= ZSIGN_MASK; tmp.z = -tmp.z; } + + // project the normal onto the plane that goes through + // X0=(1,0,0),Y0=(0,1,0),Z0=(0,0,1). + // on that plane we choose an (projective!) coordinate system + // such that X0->(0,0), Y0->(126,0), Z0->(0,126),(0,0,0)->Infinity + + // a little slower... old pack was 4 multiplies and 2 adds. + // This is 2 multiplies, 2 adds, and a divide.... + float w = 126.0f / ( tmp.x + tmp.y + tmp.z ); + long xbits = (long)( tmp.x * w ); + long ybits = (long)( tmp.y * w ); + + Assert( xbits < 127 ); + Assert( xbits >= 0 ); + Assert( ybits < 127 ); + Assert( ybits >= 0 ); + + // Now we can be sure that 0<=xp<=126, 0<=yp<=126, 0<=xp+yp<=126 + // however for the sampling we want to transform this triangle + // into a rectangle. + if ( xbits >= 64 ) + { + xbits = 127 - xbits; + ybits = 127 - ybits; + } + + // now we that have xp in the range (0,127) and yp in + // the range (0,63), we can pack all the bits together + mVec |= ( xbits << 7 ); + mVec |= ybits; + } + + void unpackVector( Vector& vec ) + { + // if we do a straightforward backward transform + // we will get points on the plane X0,Y0,Z0 + // however we need points on a sphere that goes through + // these points. Therefore we need to adjust x,y,z so + // that x^2+y^2+z^2=1 by normalizing the vector. We have + // already precalculated the amount by which we need to + // scale, so all we do is a table lookup and a + // multiplication + + // get the x and y bits + long xbits = (( mVec & TOP_MASK ) >> 7 ); + long ybits = ( mVec & BOTTOM_MASK ); + + // map the numbers back to the triangle (0,0)-(0,126)-(126,0) + if (( xbits + ybits ) >= 127 ) + { + xbits = 127 - xbits; + ybits = 127 - ybits; + } + + // do the inverse transform and normalization + // costs 3 extra multiplies and 2 subtracts. No big deal. + float uvadj = mUVAdjustment[mVec & ~SIGN_MASK]; + vec.x = uvadj * (float) xbits; + vec.y = uvadj * (float) ybits; + vec.z = uvadj * (float)( 126 - xbits - ybits ); + + // set all the sign bits + if ( mVec & XSIGN_MASK ) vec.x = -vec.x; + if ( mVec & YSIGN_MASK ) vec.y = -vec.y; + if ( mVec & ZSIGN_MASK ) vec.z = -vec.z; + + Assert( vec.IsValid()); + } + + static void initializeStatics() + { + for ( int idx = 0; idx < 0x2000; idx++ ) + { + long xbits = idx >> 7; + long ybits = idx & BOTTOM_MASK; + + // map the numbers back to the triangle (0,0)-(0,127)-(127,0) + if (( xbits + ybits ) >= 127 ) + { + xbits = 127 - xbits; + ybits = 127 - ybits; + } + + // convert to 3D vectors + float x = (float)xbits; + float y = (float)ybits; + float z = (float)( 126 - xbits - ybits ); + + // calculate the amount of normalization required + mUVAdjustment[idx] = 1.0f / sqrtf( y*y + z*z + x*x ); + Assert( _finite( mUVAdjustment[idx])); + + //cerr << mUVAdjustment[idx] << "\t"; + //if ( xbits == 0 ) cerr << "\n"; + } + } + +#if 0 + void test() + { + #define TEST_RANGE 4 + #define TEST_RANDOM 100 + #define TEST_ANGERROR 1.0 + + float maxError = 0; + float avgError = 0; + int numVecs = 0; + + {for ( int x = -TEST_RANGE; x < TEST_RANGE; x++ ) + { + for ( int y = -TEST_RANGE; y < TEST_RANGE; y++ ) + { + for ( int z = -TEST_RANGE; z < TEST_RANGE; z++ ) + { + if (( x + y + z ) == 0 ) continue; + + Vector vec( (float)x, (float)y, (float)z ); + Vector vec2; + + vec.normalize(); + packVector( vec ); + unpackVector( vec2 ); + + float ang = vec.dot( vec2 ); + ang = (( fabs( ang ) > 0.99999f ) ? 0 : (float)acos(ang)); + + if (( ang > TEST_ANGERROR ) | ( !_finite( ang ))) + { + cerr << "error: " << ang << endl; + cerr << "orig vec: " << vec.x << ",\t" + << vec.y << ",\t" << vec.z << "\tmVec: " + << mVec << endl; + cerr << "quantized vec2: " << vec2.x + << ",\t" << vec2.y << ",\t" + << vec2.z << endl << endl; + } + avgError += ang; + numVecs++; + if ( maxError < ang ) maxError = ang; + } + } + }} + + for ( int w = 0; w < TEST_RANDOM; w++ ) + { + Vector vec( genRandom(), genRandom(), genRandom()); + Vector vec2; + vec.normalize(); + + packVector( vec ); + unpackVector( vec2 ); + + float ang =vec.dot( vec2 ); + ang = (( ang > 0.999f ) ? 0 : (float)acos(ang)); + + if (( ang > TEST_ANGERROR ) | ( !_finite( ang ))) + { + cerr << "error: " << ang << endl; + cerr << "orig vec: " << vec.x << ",\t" + << vec.y << ",\t" << vec.z << "\tmVec: " + << mVec << endl; + cerr << "quantized vec2: " << vec2.x << ",\t" + << vec2.y << ",\t" + << vec2.z << endl << endl; + } + avgError += ang; + numVecs++; + if ( maxError < ang ) maxError = ang; + } + + { for ( int x = 0; x < 50; x++ ) + { + Vector vec( (float)x, 25.0f, 0.0f ); + Vector vec2; + + vec.normalize(); + packVector( vec ); + unpackVector( vec2 ); + + float ang = vec.dot( vec2 ); + ang = (( fabs( ang ) > 0.999f ) ? 0 : (float)acos(ang)); + + if (( ang > TEST_ANGERROR ) | ( !_finite( ang ))) + { + cerr << "error: " << ang << endl; + cerr << "orig vec: " << vec.x << ",\t" + << vec.y << ",\t" << vec.z << "\tmVec: " + << mVec << endl; + cerr << " quantized vec2: " << vec2.x << ",\t" + << vec2.y << ",\t" << vec2.z << endl << endl; + } + + avgError += ang; + numVecs++; + if ( maxError < ang ) maxError = ang; + }} + + cerr << "max angle error: " << maxError + << ", average error: " << avgError / numVecs + << ", num tested vecs: " << numVecs << endl; + } + + friend ostream& operator<< ( ostream& os, const cUnitVector& vec ) + { os << vec.mVec; return os; } +#endif + +//protected: // !!!! + + unsigned short mVec; + static float mUVAdjustment[0x2000]; + static Vector mTmpVec; +}; + +#endif // _3D_VECTOR_H + + diff --git a/public/mathlib/compressed_light_cube.h b/public/mathlib/compressed_light_cube.h new file mode 100644 index 0000000..0b26bdf --- /dev/null +++ b/public/mathlib/compressed_light_cube.h @@ -0,0 +1,24 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef COMPRESSED_LIGHT_CUBE_H +#define COMPRESSED_LIGHT_CUBE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/mathlib.h" + + +struct CompressedLightCube +{ + DECLARE_BYTESWAP_DATADESC(); + ColorRGBExp32 m_Color[6]; +}; + + +#endif // COMPRESSED_LIGHT_CUBE_H diff --git a/public/mathlib/compressed_vector.h b/public/mathlib/compressed_vector.h new file mode 100644 index 0000000..c3cb5fb --- /dev/null +++ b/public/mathlib/compressed_vector.h @@ -0,0 +1,608 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef COMPRESSED_VECTOR_H +#define COMPRESSED_VECTOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// For vec_t, put this somewhere else? +#include "basetypes.h" + +// For rand(). We really need a library! +#include + +#include "tier0/dbg.h" +#include "mathlib/vector.h" + +#include "mathlib/mathlib.h" + +#if defined( _X360 ) +#pragma bitfield_order( push, lsb_to_msb ) +#endif +//========================================================= +// fit a 3D vector into 32 bits +//========================================================= + +class Vector32 +{ +public: + // Construction/destruction: + Vector32(void); + Vector32(vec_t X, vec_t Y, vec_t Z); + + // assignment + Vector32& operator=(const Vector &vOther); + operator Vector (); + +private: + unsigned short x:10; + unsigned short y:10; + unsigned short z:10; + unsigned short exp:2; +}; + +inline Vector32& Vector32::operator=(const Vector &vOther) +{ + CHECK_VALID(vOther); + + static float expScale[4] = { 4.0f, 16.0f, 32.f, 64.f }; + + float fmax = MAX( fabs( vOther.x ), fabs( vOther.y ) ); + fmax = fpmax( fmax, fabs( vOther.z ) ); + + for (exp = 0; exp < 3; exp++) + { + if (fmax < expScale[exp]) + break; + } + Assert( fmax < expScale[exp] ); + + float fexp = 512.0f / expScale[exp]; + + x = clamp( (int)(vOther.x * fexp) + 512, 0, 1023 ); + y = clamp( (int)(vOther.y * fexp) + 512, 0, 1023 ); + z = clamp( (int)(vOther.z * fexp) + 512, 0, 1023 ); + return *this; +} + + +inline Vector32::operator Vector () +{ + Vector tmp; + + static float expScale[4] = { 4.0f, 16.0f, 32.f, 64.f }; + + float fexp = expScale[exp] / 512.0f; + + tmp.x = (((int)x) - 512) * fexp; + tmp.y = (((int)y) - 512) * fexp; + tmp.z = (((int)z) - 512) * fexp; + return tmp; +} + + +//========================================================= +// Fit a unit vector into 32 bits +//========================================================= + +class Normal32 +{ +public: + // Construction/destruction: + Normal32(void); + Normal32(vec_t X, vec_t Y, vec_t Z); + + // assignment + Normal32& operator=(const Vector &vOther); + operator Vector (); + +private: + unsigned short x:15; + unsigned short y:15; + unsigned short zneg:1; +}; + + +inline Normal32& Normal32::operator=(const Vector &vOther) +{ + CHECK_VALID(vOther); + + x = clamp( (int)(vOther.x * 16384) + 16384, 0, 32767 ); + y = clamp( (int)(vOther.y * 16384) + 16384, 0, 32767 ); + zneg = (vOther.z < 0); + //x = vOther.x; + //y = vOther.y; + //z = vOther.z; + return *this; +} + + +inline Normal32::operator Vector () +{ + Vector tmp; + + tmp.x = ((int)x - 16384) * (1 / 16384.0); + tmp.y = ((int)y - 16384) * (1 / 16384.0); + tmp.z = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y ); + if (zneg) + tmp.z = -tmp.z; + return tmp; +} + + +//========================================================= +// 64 bit Quaternion +//========================================================= + +class Quaternion64 +{ +public: + // Construction/destruction: + Quaternion64(void); + Quaternion64(vec_t X, vec_t Y, vec_t Z); + + // assignment + // Quaternion& operator=(const Quaternion64 &vOther); + Quaternion64& operator=(const Quaternion &vOther); + operator Quaternion (); +private: + uint64 x:21; + uint64 y:21; + uint64 z:21; + uint64 wneg:1; +}; + + +inline Quaternion64::operator Quaternion () +{ + Quaternion tmp; + + // shift to -1048576, + 1048575, then round down slightly to -1.0 < x < 1.0 + tmp.x = ((int)x - 1048576) * (1 / 1048576.5f); + tmp.y = ((int)y - 1048576) * (1 / 1048576.5f); + tmp.z = ((int)z - 1048576) * (1 / 1048576.5f); + tmp.w = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y - tmp.z * tmp.z ); + if (wneg) + tmp.w = -tmp.w; + return tmp; +} + +inline Quaternion64& Quaternion64::operator=(const Quaternion &vOther) +{ + CHECK_VALID(vOther); + + x = clamp( (int)(vOther.x * 1048576) + 1048576, 0, 2097151 ); + y = clamp( (int)(vOther.y * 1048576) + 1048576, 0, 2097151 ); + z = clamp( (int)(vOther.z * 1048576) + 1048576, 0, 2097151 ); + wneg = (vOther.w < 0); + return *this; +} + +//========================================================= +// 48 bit Quaternion +//========================================================= + +class Quaternion48 +{ +public: + // Construction/destruction: + Quaternion48(void); + Quaternion48(vec_t X, vec_t Y, vec_t Z); + + // assignment + // Quaternion& operator=(const Quaternion48 &vOther); + Quaternion48& operator=(const Quaternion &vOther); + operator Quaternion (); +//private: + unsigned short x:16; + unsigned short y:16; + unsigned short z:15; + unsigned short wneg:1; +}; + + +inline Quaternion48::operator Quaternion () +{ + Quaternion tmp; + + tmp.x = ((int)x - 32768) * (1 / 32768.5); + tmp.y = ((int)y - 32768) * (1 / 32768.5); + tmp.z = ((int)z - 16384) * (1 / 16384.5); + tmp.w = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y - tmp.z * tmp.z ); + if (wneg) + tmp.w = -tmp.w; + return tmp; +} + +inline Quaternion48& Quaternion48::operator=(const Quaternion &vOther) +{ + CHECK_VALID(vOther); + + x = clamp( (int)(vOther.x * 32768) + 32768, 0, 65535 ); + y = clamp( (int)(vOther.y * 32768) + 32768, 0, 65535 ); + z = clamp( (int)(vOther.z * 16384) + 16384, 0, 32767 ); + wneg = (vOther.w < 0); + return *this; +} + +//========================================================= +// 32 bit Quaternion +//========================================================= + +class Quaternion32 +{ +public: + // Construction/destruction: + Quaternion32(void); + Quaternion32(vec_t X, vec_t Y, vec_t Z); + + // assignment + // Quaternion& operator=(const Quaternion48 &vOther); + Quaternion32& operator=(const Quaternion &vOther); + operator Quaternion (); +private: + unsigned int x:11; + unsigned int y:10; + unsigned int z:10; + unsigned int wneg:1; +}; + + +inline Quaternion32::operator Quaternion () +{ + Quaternion tmp; + + tmp.x = ((int)x - 1024) * (1 / 1024.0); + tmp.y = ((int)y - 512) * (1 / 512.0); + tmp.z = ((int)z - 512) * (1 / 512.0); + tmp.w = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y - tmp.z * tmp.z ); + if (wneg) + tmp.w = -tmp.w; + return tmp; +} + +inline Quaternion32& Quaternion32::operator=(const Quaternion &vOther) +{ + CHECK_VALID(vOther); + + x = clamp( (int)(vOther.x * 1024) + 1024, 0, 2047 ); + y = clamp( (int)(vOther.y * 512) + 512, 0, 1023 ); + z = clamp( (int)(vOther.z * 512) + 512, 0, 1023 ); + wneg = (vOther.w < 0); + return *this; +} + +//========================================================= +// 16 bit float +//========================================================= + + +const int float32bias = 127; +const int float16bias = 15; + +const float maxfloat16bits = 65504.0f; + +class float16 +{ +public: + //float16() {} + //float16( float f ) { m_storage.rawWord = ConvertFloatTo16bits(f); } + + void Init() { m_storage.rawWord = 0; } +// float16& operator=(const float16 &other) { m_storage.rawWord = other.m_storage.rawWord; return *this; } +// float16& operator=(const float &other) { m_storage.rawWord = ConvertFloatTo16bits(other); return *this; } +// operator unsigned short () { return m_storage.rawWord; } +// operator float () { return Convert16bitFloatTo32bits( m_storage.rawWord ); } + unsigned short GetBits() const + { + return m_storage.rawWord; + } + float GetFloat() const + { + return Convert16bitFloatTo32bits( m_storage.rawWord ); + } + void SetFloat( float in ) + { + m_storage.rawWord = ConvertFloatTo16bits( in ); + } + + bool IsInfinity() const + { + return m_storage.bits.biased_exponent == 31 && m_storage.bits.mantissa == 0; + } + bool IsNaN() const + { + return m_storage.bits.biased_exponent == 31 && m_storage.bits.mantissa != 0; + } + + bool operator==(const float16 other) const { return m_storage.rawWord == other.m_storage.rawWord; } + bool operator!=(const float16 other) const { return m_storage.rawWord != other.m_storage.rawWord; } + +// bool operator< (const float other) const { return GetFloat() < other; } +// bool operator> (const float other) const { return GetFloat() > other; } + +protected: + union float32bits + { + float rawFloat; + struct + { + unsigned int mantissa : 23; + unsigned int biased_exponent : 8; + unsigned int sign : 1; + } bits; + }; + + union float16bits + { + unsigned short rawWord; + struct + { + unsigned short mantissa : 10; + unsigned short biased_exponent : 5; + unsigned short sign : 1; + } bits; + }; + + static bool IsNaN( float16bits in ) + { + return in.bits.biased_exponent == 31 && in.bits.mantissa != 0; + } + static bool IsInfinity( float16bits in ) + { + return in.bits.biased_exponent == 31 && in.bits.mantissa == 0; + } + + // 0x0001 - 0x03ff + static unsigned short ConvertFloatTo16bits( float input ) + { + if ( input > maxfloat16bits ) + input = maxfloat16bits; + else if ( input < -maxfloat16bits ) + input = -maxfloat16bits; + + float16bits output; + float32bits inFloat; + + inFloat.rawFloat = input; + + output.bits.sign = inFloat.bits.sign; + + if ( (inFloat.bits.biased_exponent==0) && (inFloat.bits.mantissa==0) ) + { + // zero + output.bits.mantissa = 0; + output.bits.biased_exponent = 0; + } + else if ( (inFloat.bits.biased_exponent==0) && (inFloat.bits.mantissa!=0) ) + { + // denorm -- denorm float maps to 0 half + output.bits.mantissa = 0; + output.bits.biased_exponent = 0; + } + else if ( (inFloat.bits.biased_exponent==0xff) && (inFloat.bits.mantissa==0) ) + { +#if 0 + // infinity + output.bits.mantissa = 0; + output.bits.biased_exponent = 31; +#else + // infinity maps to maxfloat + output.bits.mantissa = 0x3ff; + output.bits.biased_exponent = 0x1e; +#endif + } + else if ( (inFloat.bits.biased_exponent==0xff) && (inFloat.bits.mantissa!=0) ) + { +#if 0 + // NaN + output.bits.mantissa = 1; + output.bits.biased_exponent = 31; +#else + // NaN maps to zero + output.bits.mantissa = 0; + output.bits.biased_exponent = 0; +#endif + } + else + { + // regular number + int new_exp = inFloat.bits.biased_exponent-127; + + if (new_exp<-24) + { + // this maps to 0 + output.bits.mantissa = 0; + output.bits.biased_exponent = 0; + } + + if (new_exp<-14) + { + // this maps to a denorm + output.bits.biased_exponent = 0; + unsigned int exp_val = ( unsigned int )( -14 - ( inFloat.bits.biased_exponent - float32bias ) ); + if( exp_val > 0 && exp_val < 11 ) + { + output.bits.mantissa = ( 1 << ( 10 - exp_val ) ) + ( inFloat.bits.mantissa >> ( 13 + exp_val ) ); + } + } + else if (new_exp>15) + { +#if 0 + // map this value to infinity + output.bits.mantissa = 0; + output.bits.biased_exponent = 31; +#else + // to big. . . maps to maxfloat + output.bits.mantissa = 0x3ff; + output.bits.biased_exponent = 0x1e; +#endif + } + else + { + output.bits.biased_exponent = new_exp+15; + output.bits.mantissa = (inFloat.bits.mantissa >> 13); + } + } + return output.rawWord; + } + + static float Convert16bitFloatTo32bits( unsigned short input ) + { + float32bits output; + const float16bits &inFloat = *((float16bits *)&input); + + if( IsInfinity( inFloat ) ) + { + return maxfloat16bits * ( ( inFloat.bits.sign == 1 ) ? -1.0f : 1.0f ); + } + if( IsNaN( inFloat ) ) + { + return 0.0; + } + if( inFloat.bits.biased_exponent == 0 && inFloat.bits.mantissa != 0 ) + { + // denorm + const float half_denorm = (1.0f/16384.0f); // 2^-14 + float mantissa = ((float)(inFloat.bits.mantissa)) / 1024.0f; + float sgn = (inFloat.bits.sign)? -1.0f :1.0f; + output.rawFloat = sgn*mantissa*half_denorm; + } + else + { + // regular number + unsigned mantissa = inFloat.bits.mantissa; + unsigned biased_exponent = inFloat.bits.biased_exponent; + unsigned sign = ((unsigned)inFloat.bits.sign) << 31; + biased_exponent = ( (biased_exponent - float16bias + float32bias) * (biased_exponent != 0) ) << 23; + mantissa <<= (23-10); + + *((unsigned *)&output) = ( mantissa | biased_exponent | sign ); + } + + return output.rawFloat; + } + + + float16bits m_storage; +}; + +class float16_with_assign : public float16 +{ +public: + float16_with_assign() {} + float16_with_assign( float f ) { m_storage.rawWord = ConvertFloatTo16bits(f); } + + float16& operator=(const float16 &other) { m_storage.rawWord = ((float16_with_assign &)other).m_storage.rawWord; return *this; } + float16& operator=(const float &other) { m_storage.rawWord = ConvertFloatTo16bits(other); return *this; } +// operator unsigned short () const { return m_storage.rawWord; } + operator float () const { return Convert16bitFloatTo32bits( m_storage.rawWord ); } +}; + +//========================================================= +// Fit a 3D vector in 48 bits +//========================================================= + +class Vector48 +{ +public: + // Construction/destruction: + Vector48(void) {} + Vector48(vec_t X, vec_t Y, vec_t Z) { x.SetFloat( X ); y.SetFloat( Y ); z.SetFloat( Z ); } + + // assignment + Vector48& operator=(const Vector &vOther); + operator Vector (); + + const float operator[]( int i ) const { return (((float16 *)this)[i]).GetFloat(); } + + float16 x; + float16 y; + float16 z; +}; + +inline Vector48& Vector48::operator=(const Vector &vOther) +{ + CHECK_VALID(vOther); + + x.SetFloat( vOther.x ); + y.SetFloat( vOther.y ); + z.SetFloat( vOther.z ); + return *this; +} + + +inline Vector48::operator Vector () +{ + Vector tmp; + + tmp.x = x.GetFloat(); + tmp.y = y.GetFloat(); + tmp.z = z.GetFloat(); + + return tmp; +} + +//========================================================= +// Fit a 2D vector in 32 bits +//========================================================= + +class Vector2d32 +{ +public: + // Construction/destruction: + Vector2d32(void) {} + Vector2d32(vec_t X, vec_t Y) { x.SetFloat( X ); y.SetFloat( Y ); } + + // assignment + Vector2d32& operator=(const Vector &vOther); + Vector2d32& operator=(const Vector2D &vOther); + + operator Vector2D (); + + void Init( vec_t ix = 0.f, vec_t iy = 0.f); + + float16_with_assign x; + float16_with_assign y; +}; + +inline Vector2d32& Vector2d32::operator=(const Vector2D &vOther) +{ + x.SetFloat( vOther.x ); + y.SetFloat( vOther.y ); + return *this; +} + +inline Vector2d32::operator Vector2D () +{ + Vector2D tmp; + + tmp.x = x.GetFloat(); + tmp.y = y.GetFloat(); + + return tmp; +} + +inline void Vector2d32::Init( vec_t ix, vec_t iy ) +{ + x.SetFloat(ix); + y.SetFloat(iy); +} + +#if defined( _X360 ) +#pragma bitfield_order( pop ) +#endif + +#endif + diff --git a/public/mathlib/halton.h b/public/mathlib/halton.h new file mode 100644 index 0000000..2b75da1 --- /dev/null +++ b/public/mathlib/halton.h @@ -0,0 +1,70 @@ +// $Id$ + +// halton.h - classes, etc for generating numbers using the Halton pseudo-random sequence. See +// http://halton-sequences.wikiverse.org/. +// +// what this function is useful for is any sort of sampling/integration problem where +// you want to solve it by random sampling. Each call the NextValue() generates +// a random number between 0 and 1, in an unclumped manner, so that the space can be more +// or less evenly sampled with a minimum number of samples. +// +// It is NOT useful for generating random numbers dynamically, since the outputs aren't +// particularly random. +// +// To generate multidimensional sample values (points in a plane, etc), use two +// HaltonSequenceGenerator_t's, with different (primes) bases. + +#ifndef HALTON_H +#define HALTON_H + +#include +#include + +class HaltonSequenceGenerator_t +{ + int seed; + int base; + float fbase; //< base as a float + +public: + HaltonSequenceGenerator_t(int base); //< base MUST be prime, >=2 + + float GetElement(int element); + + inline float NextValue(void) + { + return GetElement(seed++); + } + +}; + + +class DirectionalSampler_t //< pseudo-random sphere sampling +{ + HaltonSequenceGenerator_t zdot; + HaltonSequenceGenerator_t vrot; +public: + DirectionalSampler_t(void) + : zdot(2),vrot(3) + { + } + + Vector NextValue(void) + { + float zvalue=zdot.NextValue(); + zvalue=2*zvalue-1.0; // map from 0..1 to -1..1 + float phi=acos(zvalue); + // now, generate a random rotation angle for x/y + float theta=2.0*M_PI*vrot.NextValue(); + float sin_p=sin(phi); + return Vector(cos(theta)*sin_p, + sin(theta)*sin_p, + zvalue); + + } +}; + + + + +#endif // halton_h diff --git a/public/mathlib/lightdesc.h b/public/mathlib/lightdesc.h new file mode 100644 index 0000000..1e0b7a4 --- /dev/null +++ b/public/mathlib/lightdesc.h @@ -0,0 +1,185 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +// light structure definitions. +#ifndef LIGHTDESC_H +#define LIGHTDESC_H + +#include +#include + +//----------------------------------------------------------------------------- +// Light structure +//----------------------------------------------------------------------------- +enum LightType_t +{ + MATERIAL_LIGHT_DISABLE = 0, + MATERIAL_LIGHT_POINT, + MATERIAL_LIGHT_DIRECTIONAL, + MATERIAL_LIGHT_SPOT, +}; + +enum LightType_OptimizationFlags_t +{ + LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 = 1, + LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 = 2, + LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 = 4, + LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED = 8, +}; + +struct LightDesc_t +{ + LightType_t m_Type; //< MATERIAL_LIGHT_xxx + Vector m_Color; //< color+intensity + Vector m_Position; //< light source center position + Vector m_Direction; //< for SPOT, direction it is pointing + float m_Range; //< distance range for light.0=infinite + float m_Falloff; //< angular falloff exponent for spot lights + float m_Attenuation0; //< constant distance falloff term + float m_Attenuation1; //< linear term of falloff + float m_Attenuation2; //< quadatic term of falloff + + // NOTE: theta and phi are *half angles* + float m_Theta; //< inner cone angle. no angular falloff + //< within this cone + float m_Phi; //< outer cone angle + + // the values below are derived from the above settings for optimizations + // These aren't used by DX8. . used for software lighting. + + // NOTE: These dots are cos( m_Theta ), cos( m_Phi ) + float m_ThetaDot; + float m_PhiDot; + float m_OneOverThetaDotMinusPhiDot; + unsigned int m_Flags; +protected: + float m_RangeSquared; +public: + + void RecalculateDerivedValues(void); // calculate m_xxDot, m_Type for changed parms + void RecalculateOneOverThetaDotMinusPhiDot(); + + LightDesc_t(void) + { + } + + // constructors for various useful subtypes + + // a point light with infinite range + LightDesc_t( const Vector &pos, const Vector &color ) + { + InitPoint( pos, color ); + } + + LightDesc_t &operator=( const LightDesc_t &src ) + { + memcpy( this, &src, sizeof(LightDesc_t) ); + return *this; + } + + /// a simple light. cone boundaries in radians. you pass a look_at point and the + /// direciton is derived from that. + LightDesc_t( const Vector &pos, const Vector &color, const Vector &point_at, + float inner_cone_boundary, float outer_cone_boundary ) + { + InitSpot( pos, color, point_at, inner_cone_boundary, outer_cone_boundary ); + } + + void InitPoint( const Vector &pos, const Vector &color ); + void InitDirectional( const Vector &dir, const Vector &color ); + void InitSpot(const Vector &pos, const Vector &color, const Vector &point_at, + float inner_cone_boundary, float outer_cone_boundary ); + + /// Given 4 points and 4 normals, ADD lighting from this light into "color". + void ComputeLightAtPoints( const FourVectors &pos, const FourVectors &normal, + FourVectors &color, bool DoHalfLambert=false ) const; + void ComputeNonincidenceLightAtPoints( const FourVectors &pos, FourVectors &color ) const; + void ComputeLightAtPointsForDirectional( const FourVectors &pos, + const FourVectors &normal, + FourVectors &color, bool DoHalfLambert=false ) const; + + // warning - modifies color!!! set color first!! + void SetupOldStyleAttenuation( float fQuadatricAttn, float fLinearAttn, float fConstantAttn ); + + void SetupNewStyleAttenuation( float fFiftyPercentDistance, float fZeroPercentDistance ); + + +/// given a direction relative to the light source position, is this ray within the + /// light cone (for spotlights..non spots consider all rays to be within their cone) + bool IsDirectionWithinLightCone(const Vector &rdir) const + { + return ( ( m_Type != MATERIAL_LIGHT_SPOT ) || ( rdir.Dot(m_Direction) >= m_PhiDot ) ); + } + + float OneOverThetaDotMinusPhiDot() const + { + return m_OneOverThetaDotMinusPhiDot; + } + + float DistanceAtWhichBrightnessIsLessThan( float flAmount ) const; +}; + + +//----------------------------------------------------------------------------- +// a point light with infinite range +//----------------------------------------------------------------------------- +inline void LightDesc_t::InitPoint( const Vector &pos, const Vector &color ) +{ + m_Type=MATERIAL_LIGHT_POINT; + m_Color=color; + m_Position=pos; + m_Range=0.0; // infinite + m_Attenuation0=1.0; + m_Attenuation1=0; + m_Attenuation2=0; + RecalculateDerivedValues(); +} + + +//----------------------------------------------------------------------------- +// a directional light with infinite range +//----------------------------------------------------------------------------- +inline void LightDesc_t::InitDirectional( const Vector &dir, const Vector &color ) +{ + m_Type=MATERIAL_LIGHT_DIRECTIONAL; + m_Color=color; + m_Direction=dir; + m_Range=0.0; // infinite + m_Attenuation0=1.0; + m_Attenuation1=0; + m_Attenuation2=0; + RecalculateDerivedValues(); +} + + +//----------------------------------------------------------------------------- +// a simple light. cone boundaries in radians. you pass a look_at point and the +// direciton is derived from that. +//----------------------------------------------------------------------------- +inline void LightDesc_t::InitSpot(const Vector &pos, const Vector &color, const Vector &point_at, + float inner_cone_boundary, float outer_cone_boundary) +{ + m_Type=MATERIAL_LIGHT_SPOT; + m_Color=color; + m_Position=pos; + m_Direction=point_at; + m_Direction-=pos; + VectorNormalizeFast(m_Direction); + m_Falloff=5.0; // linear angle falloff + m_Theta=inner_cone_boundary; + m_Phi=outer_cone_boundary; + + m_Range=0.0; // infinite + + m_Attenuation0=1.0; + m_Attenuation1=0; + m_Attenuation2=0; + RecalculateDerivedValues(); +} + + +#endif + diff --git a/public/mathlib/math_pfns.h b/public/mathlib/math_pfns.h new file mode 100644 index 0000000..fbef84d --- /dev/null +++ b/public/mathlib/math_pfns.h @@ -0,0 +1,110 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef _MATH_PFNS_H_ +#define _MATH_PFNS_H_ + +#include + +#if defined( _X360 ) +#include +#endif + +#if !defined( _X360 ) + +#include + +// These globals are initialized by mathlib and redirected based on available fpu features + +// The following are not declared as macros because they are often used in limiting situations, +// and sometimes the compiler simply refuses to inline them for some reason +FORCEINLINE float FastSqrt( float x ) +{ + __m128 root = _mm_sqrt_ss( _mm_load_ss( &x ) ); + return *( reinterpret_cast( &root ) ); +} + +FORCEINLINE float FastRSqrtFast( float x ) +{ + // use intrinsics + __m128 rroot = _mm_rsqrt_ss( _mm_load_ss( &x ) ); + return *( reinterpret_cast( &rroot ) ); +} +// Single iteration NewtonRaphson reciprocal square root: +// 0.5 * rsqrtps * (3 - x * rsqrtps(x) * rsqrtps(x)) +// Very low error, and fine to use in place of 1.f / sqrtf(x). +FORCEINLINE float FastRSqrt( float x ) +{ + float rroot = FastRSqrtFast( x ); + return (0.5f * rroot) * (3.f - (x * rroot) * rroot); +} + +void FastSinCos( float x, float* s, float* c ); // any x +float FastCos( float x ); + + + +inline float FastRecip(float x) {return 1.0f / x;} +// Simple SSE rsqrt. Usually accurate to around 6 (relative) decimal places +// or so, so ok for closed transforms. (ie, computing lighting normals) +inline float FastSqrtEst(float x) { return FastRSqrtFast(x) * x; } + + +#endif // !_X360 + +#if defined( _X360 ) + +FORCEINLINE float _VMX_Sqrt( float x ) +{ + return __fsqrts( x ); +} + +FORCEINLINE float _VMX_RSqrt( float x ) +{ + float rroot = __frsqrte( x ); + + // Single iteration NewtonRaphson on reciprocal square root estimate + return (0.5f * rroot) * (3.0f - (x * rroot) * rroot); +} + +FORCEINLINE float _VMX_RSqrtFast( float x ) +{ + return __frsqrte( x ); +} + +FORCEINLINE void _VMX_SinCos( float a, float *pS, float *pC ) +{ + XMScalarSinCos( pS, pC, a ); +} + +FORCEINLINE float _VMX_Cos( float a ) +{ + return XMScalarCos( a ); +} + +// the 360 has fixed hw and calls directly +#define FastSqrt(x) _VMX_Sqrt(x) +#define FastRSqrt(x) _VMX_RSqrt(x) +#define FastRSqrtFast(x) _VMX_RSqrtFast(x) +#define FastSinCos(x,s,c) _VMX_SinCos(x,s,c) +#define FastCos(x) _VMX_Cos(x) + +inline float FastRecip(float x) {return __fres(x);} +inline float FastSqrtEst(float x) { return __frsqrte(x) * x; } + +#endif // _X360 + +// if x is infinite, return FLT_MAX +inline float FastClampInfinity( float x ) +{ +#ifdef _X360 + return fsel( std::numeric_limits::infinity() - x, x, FLT_MAX ); +#else + return ( x > FLT_MAX ? FLT_MAX : x ); +#endif +} + +#endif // _MATH_PFNS_H_ diff --git a/public/mathlib/mathlib.h b/public/mathlib/mathlib.h new file mode 100644 index 0000000..1895b09 --- /dev/null +++ b/public/mathlib/mathlib.h @@ -0,0 +1,2246 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef MATH_LIB_H +#define MATH_LIB_H + +#include +#include "tier0/basetypes.h" +#include "mathlib/vector.h" +#include "mathlib/vector2d.h" +#include "tier0/dbg.h" + +#include "mathlib/math_pfns.h" +#ifndef ALIGN8_POST +#define ALIGN8_POST +#endif +// plane_t structure +// !!! if this is changed, it must be changed in asm code too !!! +// FIXME: does the asm code even exist anymore? +// FIXME: this should move to a different file +struct cplane_t +{ + Vector normal; + float dist; + byte type; // for fast side tests + byte signbits; // signx + (signy<<1) + (signz<<1) + byte pad[2]; + +#ifdef VECTOR_NO_SLOW_OPERATIONS + cplane_t() {} + +private: + // No copy constructors allowed if we're in optimal mode + cplane_t(const cplane_t& vOther); +#endif +}; + +// structure offset for asm code +#define CPLANE_NORMAL_X 0 +#define CPLANE_NORMAL_Y 4 +#define CPLANE_NORMAL_Z 8 +#define CPLANE_DIST 12 +#define CPLANE_TYPE 16 +#define CPLANE_SIGNBITS 17 +#define CPLANE_PAD0 18 +#define CPLANE_PAD1 19 + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + + +//----------------------------------------------------------------------------- +// Frustum plane indices. +// WARNING: there is code that depends on these values +//----------------------------------------------------------------------------- + +enum +{ + FRUSTUM_RIGHT = 0, + FRUSTUM_LEFT = 1, + FRUSTUM_TOP = 2, + FRUSTUM_BOTTOM = 3, + FRUSTUM_NEARZ = 4, + FRUSTUM_FARZ = 5, + FRUSTUM_NUMPLANES = 6 +}; + +extern int SignbitsForPlane( cplane_t *out ); +class Frustum_t; + +// Computes Y fov from an X fov and a screen aspect ratio + X from Y +float CalcFovY( float flFovX, float flScreenAspect ); +float CalcFovX( float flFovY, float flScreenAspect ); + +// Generate a frustum based on perspective view parameters +// NOTE: FOV is specified in degrees, as the *full* view angle (not half-angle) +class VPlane; +void GeneratePerspectiveFrustum( const Vector& origin, const QAngle &angles, float flZNear, float flZFar, float flFovX, float flAspectRatio, Frustum_t &frustum ); +void GeneratePerspectiveFrustum( const Vector& origin, const Vector &forward, const Vector &right, const Vector &up, float flZNear, float flZFar, float flFovX, float flFovY, VPlane *pPlanesOut ); +// Cull the world-space bounding box to the specified frustum. +// bool R_CullBox( const Vector& mins, const Vector& maxs, const Frustum_t &frustum ); +// bool R_CullBoxSkipNear( const Vector& mins, const Vector& maxs, const Frustum_t &frustum ); +void GenerateOrthoFrustum( const Vector &origin, const Vector &forward, const Vector &right, const Vector &up, float flLeft, float flRight, float flBottom, float flTop, float flZNear, float flZFar, VPlane *pPlanesOut ); + +class matrix3x4a_t; + +struct matrix3x4_t +{ + matrix3x4_t() {} + matrix3x4_t( + float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23 ) + { + m_flMatVal[0][0] = m00; m_flMatVal[0][1] = m01; m_flMatVal[0][2] = m02; m_flMatVal[0][3] = m03; + m_flMatVal[1][0] = m10; m_flMatVal[1][1] = m11; m_flMatVal[1][2] = m12; m_flMatVal[1][3] = m13; + m_flMatVal[2][0] = m20; m_flMatVal[2][1] = m21; m_flMatVal[2][2] = m22; m_flMatVal[2][3] = m23; + } + + //----------------------------------------------------------------------------- + // Creates a matrix where the X axis = forward + // the Y axis = left, and the Z axis = up + //----------------------------------------------------------------------------- + void Init( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector &vecOrigin ) + { + m_flMatVal[0][0] = xAxis.x; m_flMatVal[0][1] = yAxis.x; m_flMatVal[0][2] = zAxis.x; m_flMatVal[0][3] = vecOrigin.x; + m_flMatVal[1][0] = xAxis.y; m_flMatVal[1][1] = yAxis.y; m_flMatVal[1][2] = zAxis.y; m_flMatVal[1][3] = vecOrigin.y; + m_flMatVal[2][0] = xAxis.z; m_flMatVal[2][1] = yAxis.z; m_flMatVal[2][2] = zAxis.z; m_flMatVal[2][3] = vecOrigin.z; + } + + //----------------------------------------------------------------------------- + // Creates a matrix where the X axis = forward + // the Y axis = left, and the Z axis = up + //----------------------------------------------------------------------------- + matrix3x4_t( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis, const Vector &vecOrigin ) + { + Init( xAxis, yAxis, zAxis, vecOrigin ); + } + + inline void SetOrigin( Vector const & p ) + { + m_flMatVal[0][3] = p.x; + m_flMatVal[1][3] = p.y; + m_flMatVal[2][3] = p.z; + } + + inline void Invalidate( void ) + { + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 4; j++) + { + m_flMatVal[i][j] = VEC_T_NAN; + } + } + } + + float *operator[]( int i ) { Assert(( i >= 0 ) && ( i < 3 )); return m_flMatVal[i]; } + const float *operator[]( int i ) const { Assert(( i >= 0 ) && ( i < 3 )); return m_flMatVal[i]; } + float *Base() { return &m_flMatVal[0][0]; } + const float *Base() const { return &m_flMatVal[0][0]; } + + float m_flMatVal[3][4]; +}; + +class ALIGN16 matrix3x4a_t : public matrix3x4_t +{ +public: + /* + matrix3x4a_t() { if (((size_t)Base()) % 16 != 0) { Error( "matrix3x4a_t missaligned" ); } } + */ + matrix3x4a_t& operator=( const matrix3x4_t& src ) { memcpy( Base(), src.Base(), sizeof( float ) * 3 * 4 ); return *this; }; +}; + +#ifndef M_PI + #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +#define M_PI_F ((float)(M_PI)) // Shouldn't collide with anything. + +// NJS: Inlined to prevent floats from being autopromoted to doubles, as with the old system. +#ifndef RAD2DEG + #define RAD2DEG( x ) ( (float)(x) * (float)(180.f / M_PI_F) ) +#endif + +#ifndef DEG2RAD + #define DEG2RAD( x ) ( (float)(x) * (float)(M_PI_F / 180.f) ) +#endif + +// Used to represent sides of things like planes. +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 +#define SIDE_CROSS -2 // necessary for polylib.c + +#define ON_VIS_EPSILON 0.01 // necessary for vvis (flow.c) -- again look into moving later! +#define EQUAL_EPSILON 0.001 // necessary for vbsp (faces.c) -- should look into moving it there? + +extern bool s_bMathlibInitialized; + +extern const Vector vec3_origin; +extern const QAngle vec3_angle; +extern const Quaternion quat_identity; +extern const Vector vec3_invalid; +extern const int nanmask; + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +FORCEINLINE vec_t DotProduct(const vec_t *v1, const vec_t *v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} +FORCEINLINE void VectorSubtract(const vec_t *a, const vec_t *b, vec_t *c) +{ + c[0]=a[0]-b[0]; + c[1]=a[1]-b[1]; + c[2]=a[2]-b[2]; +} +FORCEINLINE void VectorAdd(const vec_t *a, const vec_t *b, vec_t *c) +{ + c[0]=a[0]+b[0]; + c[1]=a[1]+b[1]; + c[2]=a[2]+b[2]; +} +FORCEINLINE void VectorCopy(const vec_t *a, vec_t *b) +{ + b[0]=a[0]; + b[1]=a[1]; + b[2]=a[2]; +} +FORCEINLINE void VectorClear(vec_t *a) +{ + a[0]=a[1]=a[2]=0; +} + +FORCEINLINE float VectorMaximum(const vec_t *v) +{ + return MAX( v[0], MAX( v[1], v[2] ) ); +} + +FORCEINLINE float VectorMaximum(const Vector& v) +{ + return MAX( v.x, MAX( v.y, v.z ) ); +} + +FORCEINLINE void VectorScale (const float* in, vec_t scale, float* out) +{ + out[0] = in[0]*scale; + out[1] = in[1]*scale; + out[2] = in[2]*scale; +} + + +// Cannot be forceinline as they have overloads: +inline void VectorFill(vec_t *a, float b) +{ + a[0]=a[1]=a[2]=b; +} + +inline void VectorNegate(vec_t *a) +{ + a[0]=-a[0]; + a[1]=-a[1]; + a[2]=-a[2]; +} + + +//#define VectorMaximum(a) ( max( (a)[0], max( (a)[1], (a)[2] ) ) ) +#define Vector2Clear(x) {(x)[0]=(x)[1]=0;} +#define Vector2Negate(x) {(x)[0]=-((x)[0]);(x)[1]=-((x)[1]);} +#define Vector2Copy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];} +#define Vector2Subtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];} +#define Vector2Add(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];} +#define Vector2Scale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];} + +// NJS: Some functions in VBSP still need to use these for dealing with mixing vec4's and shorts with vec_t's. +// remove when no longer needed. +#define VECTOR_COPY( A, B ) do { (B)[0] = (A)[0]; (B)[1] = (A)[1]; (B)[2]=(A)[2]; } while(0) +#define DOT_PRODUCT( A, B ) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] ) + +FORCEINLINE void VectorMAInline( const float* start, float scale, const float* direction, float* dest ) +{ + dest[0]=start[0]+direction[0]*scale; + dest[1]=start[1]+direction[1]*scale; + dest[2]=start[2]+direction[2]*scale; +} + +FORCEINLINE void VectorMAInline( const Vector& start, float scale, const Vector& direction, Vector& dest ) +{ + dest.x=start.x+direction.x*scale; + dest.y=start.y+direction.y*scale; + dest.z=start.z+direction.z*scale; +} + +FORCEINLINE void VectorMA( const Vector& start, float scale, const Vector& direction, Vector& dest ) +{ + VectorMAInline(start, scale, direction, dest); +} + +FORCEINLINE void VectorMA( const float * start, float scale, const float *direction, float *dest ) +{ + VectorMAInline(start, scale, direction, dest); +} + + +int VectorCompare (const float *v1, const float *v2); + +inline float VectorLength(const float *v) +{ + return FastSqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + FLT_EPSILON ); +} + +void CrossProduct (const float *v1, const float *v2, float *cross); + +qboolean VectorsEqual( const float *v1, const float *v2 ); + +inline vec_t RoundInt (vec_t in) +{ + return floor(in + 0.5f); +} + +size_t Q_log2( unsigned int val ); + +// Math routines done in optimized assembly math package routines +void inline SinCos( float radians, float *sine, float *cosine ) +{ +#if defined( _X360 ) + XMScalarSinCos( sine, cosine, radians ); +#elif defined( COMPILER_MSVC32 ) + _asm + { + fld DWORD PTR [radians] + fsincos + + mov edx, DWORD PTR [cosine] + mov eax, DWORD PTR [sine] + + fstp DWORD PTR [edx] + fstp DWORD PTR [eax] + } +#elif defined( GNUC ) + register double __cosr, __sinr; + __asm __volatile__ ("fsincos" : "=t" (__cosr), "=u" (__sinr) : "0" (radians)); + + *sine = __sinr; + *cosine = __cosr; +#else + *sine = sinf(radians); + *cosine = cosf(radians); +#endif +} + +#define SIN_TABLE_SIZE 256 +#define FTOIBIAS 12582912.f +extern float SinCosTable[SIN_TABLE_SIZE]; + +inline float TableCos( float theta ) +{ + union + { + int i; + float f; + } ftmp; + + // ideally, the following should compile down to: theta * constant + constant, changing any of these constants from defines sometimes fubars this. + ftmp.f = theta * ( float )( SIN_TABLE_SIZE / ( 2.0f * M_PI ) ) + ( FTOIBIAS + ( SIN_TABLE_SIZE / 4 ) ); + return SinCosTable[ ftmp.i & ( SIN_TABLE_SIZE - 1 ) ]; +} + +inline float TableSin( float theta ) +{ + union + { + int i; + float f; + } ftmp; + + // ideally, the following should compile down to: theta * constant + constant + ftmp.f = theta * ( float )( SIN_TABLE_SIZE / ( 2.0f * M_PI ) ) + FTOIBIAS; + return SinCosTable[ ftmp.i & ( SIN_TABLE_SIZE - 1 ) ]; +} + +template +FORCEINLINE T Square( T const &a ) +{ + return a * a; +} + + +// return the smallest power of two >= x. +// returns 0 if x == 0 or x > 0x80000000 (ie numbers that would be negative if x was signed) +// NOTE: the old code took an int, and if you pass in an int of 0x80000000 casted to a uint, +// you'll get 0x80000000, which is correct for uints, instead of 0, which was correct for ints +FORCEINLINE uint SmallestPowerOfTwoGreaterOrEqual( uint x ) +{ + x -= 1; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +// return the largest power of two <= x. Will return 0 if passed 0 +FORCEINLINE uint LargestPowerOfTwoLessThanOrEqual( uint x ) +{ + if ( x >= 0x80000000 ) + return 0x80000000; + + return SmallestPowerOfTwoGreaterOrEqual( x + 1 ) >> 1; +} + + +// Math routines for optimizing division +void FloorDivMod (double numer, double denom, int *quotient, int *rem); +int GreatestCommonDivisor (int i1, int i2); + +// Test for FPU denormal mode +bool IsDenormal( const float &val ); + +// MOVEMENT INFO +enum +{ + PITCH = 0, // up / down + YAW, // left / right + ROLL // fall over +}; + +void MatrixAngles( const matrix3x4_t & matrix, float *angles ); // !!!! +void MatrixVectors( const matrix3x4_t &matrix, Vector* pForward, Vector *pRight, Vector *pUp ); +void VectorTransform (const float *in1, const matrix3x4_t & in2, float *out); +void VectorITransform (const float *in1, const matrix3x4_t & in2, float *out); +void VectorRotate( const float *in1, const matrix3x4_t & in2, float *out); +void VectorRotate( const Vector &in1, const QAngle &in2, Vector &out ); +void VectorRotate( const Vector &in1, const Quaternion &in2, Vector &out ); +void VectorIRotate( const float *in1, const matrix3x4_t & in2, float *out); + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +QAngle TransformAnglesToLocalSpace( const QAngle &angles, const matrix3x4_t &parentMatrix ); +QAngle TransformAnglesToWorldSpace( const QAngle &angles, const matrix3x4_t &parentMatrix ); + +#endif + +void MatrixInitialize( matrix3x4_t &mat, const Vector &vecOrigin, const Vector &vecXAxis, const Vector &vecYAxis, const Vector &vecZAxis ); +void MatrixCopy( const matrix3x4_t &in, matrix3x4_t &out ); +void MatrixInvert( const matrix3x4_t &in, matrix3x4_t &out ); + +// Matrix equality test +bool MatricesAreEqual( const matrix3x4_t &src1, const matrix3x4_t &src2, float flTolerance = 1e-5 ); + +void MatrixGetColumn( const matrix3x4_t &in, int column, Vector &out ); +void MatrixSetColumn( const Vector &in, int column, matrix3x4_t &out ); + +//void DecomposeRotation( const matrix3x4_t &mat, float *out ); +void ConcatRotations (const matrix3x4_t &in1, const matrix3x4_t &in2, matrix3x4_t &out); +void ConcatTransforms (const matrix3x4_t &in1, const matrix3x4_t &in2, matrix3x4_t &out); +// faster version assumes m0, m1, out are 16-byte aligned addresses +void ConcatTransforms_Aligned( const matrix3x4a_t &m0, const matrix3x4a_t &m1, matrix3x4a_t &out ); + +// For identical interface w/ VMatrix +inline void MatrixMultiply ( const matrix3x4_t &in1, const matrix3x4_t &in2, matrix3x4_t &out ) +{ + ConcatTransforms( in1, in2, out ); +} + +void QuaternionSlerp( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt ); +void QuaternionSlerpNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt ); +void QuaternionBlend( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt ); +void QuaternionBlendNoAlign( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt ); +void QuaternionIdentityBlend( const Quaternion &p, float t, Quaternion &qt ); +float QuaternionAngleDiff( const Quaternion &p, const Quaternion &q ); +void QuaternionScale( const Quaternion &p, float t, Quaternion &q ); +void QuaternionAlign( const Quaternion &p, const Quaternion &q, Quaternion &qt ); +float QuaternionDotProduct( const Quaternion &p, const Quaternion &q ); +void QuaternionConjugate( const Quaternion &p, Quaternion &q ); +void QuaternionInvert( const Quaternion &p, Quaternion &q ); +float QuaternionNormalize( Quaternion &q ); +void QuaternionAdd( const Quaternion &p, const Quaternion &q, Quaternion &qt ); +void QuaternionMult( const Quaternion &p, const Quaternion &q, Quaternion &qt ); +void QuaternionExp( const Quaternion &p, Quaternion &q ); +void QuaternionLn( const Quaternion &p, Quaternion &q ); +void QuaternionAverageExponential( Quaternion &q, int nCount, const Quaternion *pQuaternions, const float *pflWeights = NULL ); +void QuaternionLookAt( const Vector &vecForward, const Vector &referenceUp, Quaternion &q ); +void QuaternionMatrix( const Quaternion &q, matrix3x4_t &matrix ); +void QuaternionMatrix( const Quaternion &q, const Vector &pos, matrix3x4_t &matrix ); +void QuaternionAngles( const Quaternion &q, QAngle &angles ); +void AngleQuaternion( const QAngle& angles, Quaternion &qt ); +void QuaternionAngles( const Quaternion &q, RadianEuler &angles ); +void AngleQuaternion( RadianEuler const &angles, Quaternion &qt ); +void QuaternionAxisAngle( const Quaternion &q, Vector &axis, float &angle ); +void AxisAngleQuaternion( const Vector &axis, float angle, Quaternion &q ); +void BasisToQuaternion( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, Quaternion &q ); +void MatrixQuaternion( const matrix3x4_t &mat, Quaternion &q ); + +// A couple methods to find the dot product of a vector with a matrix row or column... +inline float MatrixRowDotProduct( const matrix3x4_t &in1, int row, const Vector& in2 ) +{ + Assert( (row >= 0) && (row < 3) ); + return DotProduct( in1[row], in2.Base() ); +} + +inline float MatrixColumnDotProduct( const matrix3x4_t &in1, int col, const Vector& in2 ) +{ + Assert( (col >= 0) && (col < 4) ); + return in1[0][col] * in2[0] + in1[1][col] * in2[1] + in1[2][col] * in2[2]; +} + +int __cdecl BoxOnPlaneSide (const float *emins, const float *emaxs, const cplane_t *plane); + +inline float anglemod(float a) +{ + a = (360.f/65536) * ((int)(a*(65536.f/360.0f)) & 65535); + return a; +} + +// Remap a value in the range [A,B] to [C,D]. +inline float RemapVal( float val, float A, float B, float C, float D) +{ + if ( A == B ) + return fsel( val - B , D , C ); + return C + (D - C) * (val - A) / (B - A); +} + +inline float RemapValClamped( float val, float A, float B, float C, float D) +{ + if ( A == B ) + return fsel( val - B , D , C ); + float cVal = (val - A) / (B - A); + cVal = clamp( cVal, 0.0f, 1.0f ); + + return C + (D - C) * cVal; +} + +// Returns A + (B-A)*flPercent. +// float Lerp( float flPercent, float A, float B ); +template +FORCEINLINE T Lerp( float flPercent, T const &A, T const &B ) +{ + return A + (B - A) * flPercent; +} + +FORCEINLINE float Sqr( float f ) +{ + return f*f; +} + +// 5-argument floating point linear interpolation. +// FLerp(f1,f2,i1,i2,x)= +// f1 at x=i1 +// f2 at x=i2 +// smooth lerp between f1 and f2 at x>i1 and xi2 +// +// If you know a function f(x)'s value (f1) at position i1, and its value (f2) at position i2, +// the function can be linearly interpolated with FLerp(f1,f2,i1,i2,x) +// i2=i1 will cause a divide by zero. +static inline float FLerp(float f1, float f2, float i1, float i2, float x) +{ + return f1+(f2-f1)*(x-i1)/(i2-i1); +} + + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +// YWB: Specialization for interpolating euler angles via quaternions... +template<> FORCEINLINE QAngle Lerp( float flPercent, const QAngle& q1, const QAngle& q2 ) +{ + // Avoid precision errors + if ( q1 == q2 ) + return q1; + + Quaternion src, dest; + + // Convert to quaternions + AngleQuaternion( q1, src ); + AngleQuaternion( q2, dest ); + + Quaternion result; + + // Slerp + QuaternionSlerp( src, dest, flPercent, result ); + + // Convert to euler + QAngle output; + QuaternionAngles( result, output ); + return output; +} + +#else + +#pragma error + +// NOTE NOTE: I haven't tested this!! It may not work! Check out interpolatedvar.cpp in the client dll to try it +template<> FORCEINLINE QAngleByValue Lerp( float flPercent, const QAngleByValue& q1, const QAngleByValue& q2 ) +{ + // Avoid precision errors + if ( q1 == q2 ) + return q1; + + Quaternion src, dest; + + // Convert to quaternions + AngleQuaternion( q1, src ); + AngleQuaternion( q2, dest ); + + Quaternion result; + + // Slerp + QuaternionSlerp( src, dest, flPercent, result ); + + // Convert to euler + QAngleByValue output; + QuaternionAngles( result, output ); + return output; +} + +#endif // VECTOR_NO_SLOW_OPERATIONS + + +// Swap two of anything. +template +FORCEINLINE void V_swap( T& x, T& y ) +{ + T temp = x; + x = y; + y = temp; +} + +template FORCEINLINE T AVG(T a, T b) +{ + return (a+b)/2; +} + +// number of elements in an array of static size +#define NELEMS(x) ((sizeof(x))/sizeof(x[0])) + +// XYZ macro, for printf type functions - ex printf("%f %f %f",XYZ(myvector)); +#define XYZ(v) (v).x,(v).y,(v).z + +// +// Returns a clamped value in the range [min, max]. +// +#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) + +inline float Sign( float x ) +{ + return fsel( x, 1.0f, -1.0f ); // x >= 0 ? 1.0f : -1.0f + //return (x <0.0f) ? -1.0f : 1.0f; +} + +// +// Clamps the input integer to the given array bounds. +// Equivalent to the following, but without using any branches: +// +// if( n < 0 ) return 0; +// else if ( n > maxindex ) return maxindex; +// else return n; +// +// This is not always a clear performance win, but when you have situations where a clamped +// value is thrashing against a boundary this is a big win. (ie, valid, invalid, valid, invalid, ...) +// +// Note: This code has been run against all possible integers. +// +inline int ClampArrayBounds( int n, unsigned maxindex ) +{ + // mask is 0 if less than 4096, 0xFFFFFFFF if greater than + unsigned int inrangemask = 0xFFFFFFFF + (((unsigned) n) > maxindex ); + unsigned int lessthan0mask = 0xFFFFFFFF + ( n >= 0 ); + + // If the result was valid, set the result, (otherwise sets zero) + int result = (inrangemask & n); + + // if the result was out of range or zero. + result |= ((~inrangemask) & (~lessthan0mask)) & maxindex; + + return result; +} + + + +// Turn a number "inside out". +// See Recording Animation in Binary Order for Progressive Temporal Refinement +// by Paul Heckbert from "Graphics Gems". +// +// If you want to iterate something from 0 to n, you can use this to iterate non-sequentially, in +// such a way that you will start with widely separated values and then refine the gaps between +// them, as you would for progressive refinement. This works with non-power of two ranges. +int InsideOut( int nTotal, int nCounter ); + +#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ + (((p)->type < 3)? \ + ( \ + ((p)->dist <= (emins)[(p)->type])? \ + 1 \ + : \ + ( \ + ((p)->dist >= (emaxs)[(p)->type])?\ + 2 \ + : \ + 3 \ + ) \ + ) \ + : \ + BoxOnPlaneSide( (emins), (emaxs), (p))) + +//----------------------------------------------------------------------------- +// FIXME: Vector versions.... the float versions will go away hopefully soon! +//----------------------------------------------------------------------------- + +void AngleVectors (const QAngle& angles, Vector *forward); +void AngleVectors (const QAngle& angles, Vector *forward, Vector *right, Vector *up); +void AngleVectorsTranspose (const QAngle& angles, Vector *forward, Vector *right, Vector *up); +void AngleMatrix (const QAngle &angles, matrix3x4_t &mat ); +void AngleMatrix( const QAngle &angles, const Vector &position, matrix3x4_t &mat ); +void AngleMatrix (const RadianEuler &angles, matrix3x4_t &mat ); +void AngleMatrix( RadianEuler const &angles, const Vector &position, matrix3x4_t &mat ); +void AngleIMatrix (const QAngle &angles, matrix3x4_t &mat ); +void AngleIMatrix (const QAngle &angles, const Vector &position, matrix3x4_t &mat ); +void AngleIMatrix (const RadianEuler &angles, matrix3x4_t &mat ); +void VectorAngles( const Vector &forward, QAngle &angles ); +void VectorAngles( const Vector &forward, const Vector &pseudoup, QAngle &angles ); +void VectorMatrix( const Vector &forward, matrix3x4_t &mat ); +void VectorVectors( const Vector &forward, Vector &right, Vector &up ); +void SetIdentityMatrix( matrix3x4_t &mat ); +void SetScaleMatrix( float x, float y, float z, matrix3x4_t &dst ); +void MatrixBuildRotationAboutAxis( const Vector &vAxisOfRot, float angleDegrees, matrix3x4_t &dst ); + +inline void SetScaleMatrix( float flScale, matrix3x4_t &dst ) +{ + SetScaleMatrix( flScale, flScale, flScale, dst ); +} + +inline void SetScaleMatrix( const Vector& scale, matrix3x4_t &dst ) +{ + SetScaleMatrix( scale.x, scale.y, scale.z, dst ); +} + +// Computes the inverse transpose +void MatrixTranspose( matrix3x4_t& mat ); +void MatrixTranspose( const matrix3x4_t& src, matrix3x4_t& dst ); +void MatrixInverseTranspose( const matrix3x4_t& src, matrix3x4_t& dst ); + +inline void PositionMatrix( const Vector &position, matrix3x4_t &mat ) +{ + MatrixSetColumn( position, 3, mat ); +} + +inline void MatrixPosition( const matrix3x4_t &matrix, Vector &position ) +{ + position[0] = matrix[0][3]; + position[1] = matrix[1][3]; + position[2] = matrix[2][3]; +} + +inline void VectorRotate( const Vector& in1, const matrix3x4_t &in2, Vector &out) +{ + VectorRotate( &in1.x, in2, &out.x ); +} + +inline void VectorIRotate( const Vector& in1, const matrix3x4_t &in2, Vector &out) +{ + VectorIRotate( &in1.x, in2, &out.x ); +} + +inline void MatrixAngles( const matrix3x4_t &matrix, QAngle &angles ) +{ + MatrixAngles( matrix, &angles.x ); +} + +inline void MatrixAngles( const matrix3x4_t &matrix, QAngle &angles, Vector &position ) +{ + MatrixAngles( matrix, angles ); + MatrixPosition( matrix, position ); +} + +inline void MatrixAngles( const matrix3x4_t &matrix, RadianEuler &angles ) +{ + MatrixAngles( matrix, &angles.x ); + + angles.Init( DEG2RAD( angles.z ), DEG2RAD( angles.x ), DEG2RAD( angles.y ) ); +} + +void MatrixAngles( const matrix3x4_t &mat, RadianEuler &angles, Vector &position ); + +void MatrixAngles( const matrix3x4_t &mat, Quaternion &q, Vector &position ); + +inline int VectorCompare (const Vector& v1, const Vector& v2) +{ + return v1 == v2; +} + +inline void VectorTransform (const Vector& in1, const matrix3x4_t &in2, Vector &out) +{ + VectorTransform( &in1.x, in2, &out.x ); +} + +inline void VectorITransform (const Vector& in1, const matrix3x4_t &in2, Vector &out) +{ + VectorITransform( &in1.x, in2, &out.x ); +} + +/* +inline void DecomposeRotation( const matrix3x4_t &mat, Vector &out ) +{ + DecomposeRotation( mat, &out.x ); +} +*/ + +inline int BoxOnPlaneSide (const Vector& emins, const Vector& emaxs, const cplane_t *plane ) +{ + return BoxOnPlaneSide( &emins.x, &emaxs.x, plane ); +} + +inline void VectorFill(Vector& a, float b) +{ + a[0]=a[1]=a[2]=b; +} + +inline void VectorNegate(Vector& a) +{ + a[0] = -a[0]; + a[1] = -a[1]; + a[2] = -a[2]; +} + +inline vec_t VectorAvg(Vector& a) +{ + return ( a[0] + a[1] + a[2] ) / 3; +} + +//----------------------------------------------------------------------------- +// Box/plane test (slow version) +//----------------------------------------------------------------------------- +inline int FASTCALL BoxOnPlaneSide2 (const Vector& emins, const Vector& emaxs, const cplane_t *p, float tolerance = 0.f ) +{ + Vector corners[2]; + + if (p->normal[0] < 0) + { + corners[0][0] = emins[0]; + corners[1][0] = emaxs[0]; + } + else + { + corners[1][0] = emins[0]; + corners[0][0] = emaxs[0]; + } + + if (p->normal[1] < 0) + { + corners[0][1] = emins[1]; + corners[1][1] = emaxs[1]; + } + else + { + corners[1][1] = emins[1]; + corners[0][1] = emaxs[1]; + } + + if (p->normal[2] < 0) + { + corners[0][2] = emins[2]; + corners[1][2] = emaxs[2]; + } + else + { + corners[1][2] = emins[2]; + corners[0][2] = emaxs[2]; + } + + int sides = 0; + + float dist1 = DotProduct (p->normal, corners[0]) - p->dist; + if (dist1 >= tolerance) + sides = 1; + + float dist2 = DotProduct (p->normal, corners[1]) - p->dist; + if (dist2 < -tolerance) + sides |= 2; + + return sides; +} + +//----------------------------------------------------------------------------- +// Helpers for bounding box construction +//----------------------------------------------------------------------------- + +void ClearBounds (Vector& mins, Vector& maxs); +void AddPointToBounds (const Vector& v, Vector& mins, Vector& maxs); + +//----------------------------------------------------------------------------- +// Ensures that the min and max bounds values are valid. +// (ClearBounds() sets min > max, which is clearly invalid.) +//----------------------------------------------------------------------------- +bool AreBoundsValid( const Vector &vMin, const Vector &vMax ); + +//----------------------------------------------------------------------------- +// Returns true if the provided point is in the AABB defined by vMin +// at the lower corner and vMax at the upper corner. +//----------------------------------------------------------------------------- +bool IsPointInBounds( const Vector &vPoint, const Vector &vMin, const Vector &vMax ); + +// +// COLORSPACE/GAMMA CONVERSION STUFF +// +void BuildGammaTable( float gamma, float texGamma, float brightness, int overbright ); + +// convert texture to linear 0..1 value +inline float TexLightToLinear( int c, int exponent ) +{ + extern float power2_n[256]; + Assert( exponent >= -128 && exponent <= 127 ); + return ( float )c * power2_n[exponent+128]; +} + + +// convert texture to linear 0..1 value +int LinearToTexture( float f ); +// converts 0..1 linear value to screen gamma (0..255) +int LinearToScreenGamma( float f ); +float TextureToLinear( int c ); + +// compressed color format +struct ColorRGBExp32 +{ + byte r, g, b; + signed char exponent; +}; + +void ColorRGBExp32ToVector( const ColorRGBExp32& in, Vector& out ); +void VectorToColorRGBExp32( const Vector& v, ColorRGBExp32 &c ); + +// solve for "x" where "a x^2 + b x + c = 0", return true if solution exists +bool SolveQuadratic( float a, float b, float c, float &root1, float &root2 ); + +// solves for "a, b, c" where "a x^2 + b x + c = y", return true if solution exists +bool SolveInverseQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c ); + +// solves for a,b,c specified as above, except that it always creates a monotonically increasing or +// decreasing curve if the data is monotonically increasing or decreasing. In order to enforce the +// monoticity condition, it is possible that the resulting quadratic will only approximate the data +// instead of interpolating it. This code is not especially fast. +bool SolveInverseQuadraticMonotonic( float x1, float y1, float x2, float y2, + float x3, float y3, float &a, float &b, float &c ); + + + + +// solves for "a, b, c" where "1/(a x^2 + b x + c ) = y", return true if solution exists +bool SolveInverseReciprocalQuadratic( float x1, float y1, float x2, float y2, float x3, float y3, float &a, float &b, float &c ); + +// rotate a vector around the Z axis (YAW) +void VectorYawRotate( const Vector& in, float flYaw, Vector &out); + + +// Bias takes an X value between 0 and 1 and returns another value between 0 and 1 +// The curve is biased towards 0 or 1 based on biasAmt, which is between 0 and 1. +// Lower values of biasAmt bias the curve towards 0 and higher values bias it towards 1. +// +// For example, with biasAmt = 0.2, the curve looks like this: +// +// 1 +// | * +// | * +// | * +// | ** +// | ** +// | **** +// |********* +// |___________________ +// 0 1 +// +// +// With biasAmt = 0.8, the curve looks like this: +// +// 1 +// | ************** +// | ** +// | * +// | * +// |* +// |* +// |* +// |___________________ +// 0 1 +// +// With a biasAmt of 0.5, Bias returns X. +float Bias( float x, float biasAmt ); + + +// Gain is similar to Bias, but biasAmt biases towards or away from 0.5. +// Lower bias values bias towards 0.5 and higher bias values bias away from it. +// +// For example, with biasAmt = 0.2, the curve looks like this: +// +// 1 +// | * +// | * +// | ** +// | *************** +// | ** +// | * +// |* +// |___________________ +// 0 1 +// +// +// With biasAmt = 0.8, the curve looks like this: +// +// 1 +// | ***** +// | *** +// | * +// | * +// | * +// | *** +// |***** +// |___________________ +// 0 1 +float Gain( float x, float biasAmt ); + + +// SmoothCurve maps a 0-1 value into another 0-1 value based on a cosine wave +// where the derivatives of the function at 0 and 1 (and 0.5) are 0. This is useful for +// any fadein/fadeout effect where it should start and end smoothly. +// +// The curve looks like this: +// +// 1 +// | ** +// | * * +// | * * +// | * * +// | * * +// | ** ** +// |*** *** +// |___________________ +// 0 1 +// +float SmoothCurve( float x ); + + +// This works like SmoothCurve, with two changes: +// +// 1. Instead of the curve peaking at 0.5, it will peak at flPeakPos. +// (So if you specify flPeakPos=0.2, then the peak will slide to the left). +// +// 2. flPeakSharpness is a 0-1 value controlling the sharpness of the peak. +// Low values blunt the peak and high values sharpen the peak. +float SmoothCurve_Tweak( float x, float flPeakPos=0.5, float flPeakSharpness=0.5 ); + + +//float ExponentialDecay( float halflife, float dt ); +//float ExponentialDecay( float decayTo, float decayTime, float dt ); + +// halflife is time for value to reach 50% +inline float ExponentialDecay( float halflife, float dt ) +{ + // log(0.5) == -0.69314718055994530941723212145818 + return expf( -0.69314718f / halflife * dt); +} + +// decayTo is factor the value should decay to in decayTime +inline float ExponentialDecay( float decayTo, float decayTime, float dt ) +{ + return expf( logf( decayTo ) / decayTime * dt); +} + +// Get the integrated distanced traveled +// decayTo is factor the value should decay to in decayTime +// dt is the time relative to the last velocity update +inline float ExponentialDecayIntegral( float decayTo, float decayTime, float dt ) +{ + return (powf( decayTo, dt / decayTime) * decayTime - decayTime) / logf( decayTo ); +} + +// hermite basis function for smooth interpolation +// Similar to Gain() above, but very cheap to call +// value should be between 0 & 1 inclusive +inline float SimpleSpline( float value ) +{ + float valueSquared = value * value; + + // Nice little ease-in, ease-out spline-like curve + return (3 * valueSquared - 2 * valueSquared * value); +} + +// remaps a value in [startInterval, startInterval+rangeInterval] from linear to +// spline using SimpleSpline +inline float SimpleSplineRemapVal( float val, float A, float B, float C, float D) +{ + if ( A == B ) + return val >= B ? D : C; + float cVal = (val - A) / (B - A); + return C + (D - C) * SimpleSpline( cVal ); +} + +// remaps a value in [startInterval, startInterval+rangeInterval] from linear to +// spline using SimpleSpline +inline float SimpleSplineRemapValClamped( float val, float A, float B, float C, float D ) +{ + if ( A == B ) + return val >= B ? D : C; + float cVal = (val - A) / (B - A); + cVal = clamp( cVal, 0.0f, 1.0f ); + return C + (D - C) * SimpleSpline( cVal ); +} + +FORCEINLINE int RoundFloatToInt(float f) +{ +#if defined( _X360 ) +#ifdef Assert + Assert( IsFPUControlWordSet() ); +#endif + union + { + double flResult; + int pResult[2]; + }; + flResult = __fctiw( f ); + return pResult[1]; +#else // !X360 + int nResult; +#if defined( COMPILER_MSVC32 ) + __asm + { + fld f + fistp nResult + } +#elif GNUC + __asm __volatile__ ( + "fistpl %0;": "=m" (nResult): "t" (f) : "st" + ); +#else + nResult = static_cast(f); +#endif + return nResult; +#endif +} + +FORCEINLINE unsigned char RoundFloatToByte(float f) +{ +#if defined( _X360 ) +#ifdef Assert + Assert( IsFPUControlWordSet() ); +#endif + union + { + double flResult; + int pIntResult[2]; + unsigned char pResult[8]; + }; + flResult = __fctiw( f ); +#ifdef Assert + Assert( pIntResult[1] >= 0 && pIntResult[1] <= 255 ); +#endif + return pResult[7]; + +#else // !X360 + + int nResult; + +#if defined( COMPILER_MSVC32 ) + __asm + { + fld f + fistp nResult + } +#elif GNUC + __asm __volatile__ ( + "fistpl %0;": "=m" (nResult): "t" (f) : "st" + ); +#else + nResult = static_cast (f) & 0xff; +#endif + +#ifdef Assert + Assert( nResult >= 0 && nResult <= 255 ); +#endif + return nResult; + +#endif +} + +FORCEINLINE unsigned long RoundFloatToUnsignedLong(float f) +{ +#if defined( _X360 ) +#ifdef Assert + Assert( IsFPUControlWordSet() ); +#endif + union + { + double flResult; + int pIntResult[2]; + unsigned long pResult[2]; + }; + flResult = __fctiw( f ); + Assert( pIntResult[1] >= 0 ); + return pResult[1]; +#else // !X360 + +#if defined( COMPILER_MSVC32 ) + unsigned char nResult[8]; + __asm + { + fld f + fistp qword ptr nResult + } + return *((unsigned long*)nResult); +#elif defined( COMPILER_GCC ) + unsigned char nResult[8]; + __asm __volatile__ ( + "fistpl %0;": "=m" (nResult): "t" (f) : "st" + ); + return *((unsigned long*)nResult); +#else + return static_cast(f); +#endif + +#endif +} + +FORCEINLINE bool IsIntegralValue( float flValue, float flTolerance = 0.001f ) +{ + return fabs( RoundFloatToInt( flValue ) - flValue ) < flTolerance; +} + +// Fast, accurate ftol: +FORCEINLINE int Float2Int( float a ) +{ +#if defined( _X360 ) + union + { + double flResult; + int pResult[2]; + }; + flResult = __fctiwz( a ); + return pResult[1]; +#else // !X360 + + int RetVal; + +#if defined( COMPILER_MSVC32 ) + int CtrlwdHolder; + int CtrlwdSetter; + __asm + { + fld a // push 'a' onto the FP stack + fnstcw CtrlwdHolder // store FPU control word + movzx eax, CtrlwdHolder // move and zero extend word into eax + and eax, 0xFFFFF3FF // set all bits except rounding bits to 1 + or eax, 0x00000C00 // set rounding mode bits to round towards zero + mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid! + fldcw CtrlwdSetter // Entering plaid! + fistp RetVal // Store and converted (to int) result + fldcw CtrlwdHolder // Restore control word + } +#else + RetVal = static_cast( a ); +#endif + + return RetVal; +#endif +} + +// Over 15x faster than: (int)floor(value) +inline int Floor2Int( float a ) +{ + int RetVal; + +#if defined( _X360 ) + RetVal = (int)floor( a ); +#elif defined( COMPILER_MSVC32 ) + int CtrlwdHolder; + int CtrlwdSetter; + __asm + { + fld a // push 'a' onto the FP stack + fnstcw CtrlwdHolder // store FPU control word + movzx eax, CtrlwdHolder // move and zero extend word into eax + and eax, 0xFFFFF3FF // set all bits except rounding bits to 1 + or eax, 0x00000400 // set rounding mode bits to round down + mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid! + fldcw CtrlwdSetter // Entering plaid! + fistp RetVal // Store floored and converted (to int) result + fldcw CtrlwdHolder // Restore control word + } +#else + RetVal = static_cast( floor(a) ); +#endif + + return RetVal; +} + +//----------------------------------------------------------------------------- +// Fast color conversion from float to unsigned char +//----------------------------------------------------------------------------- +FORCEINLINE unsigned char FastFToC( float c ) +{ + volatile float dc; + + // ieee trick + dc = c * 255.0f + (float)(1 << 23); + + // return the lsb +#if defined( _X360 ) + return ((unsigned char*)&dc)[3]; +#else + return *(unsigned char*)&dc; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Bound input float to .001 (millisecond) boundary +// Input : in - +// Output : inline float +//----------------------------------------------------------------------------- +inline float ClampToMsec( float in ) +{ + int msec = Floor2Int( in * 1000.0f + 0.5f ); + return msec / 1000.0f; +} + +// Over 15x faster than: (int)ceil(value) +inline int Ceil2Int( float a ) +{ + int RetVal; + +#if defined( _X360 ) + RetVal = (int)ceil( a ); +#elif defined( COMPILER_MSVC32 ) + int CtrlwdHolder; + int CtrlwdSetter; + __asm + { + fld a // push 'a' onto the FP stack + fnstcw CtrlwdHolder // store FPU control word + movzx eax, CtrlwdHolder // move and zero extend word into eax + and eax, 0xFFFFF3FF // set all bits except rounding bits to 1 + or eax, 0x00000800 // set rounding mode bits to round down + mov CtrlwdSetter, eax // Prepare to set the rounding mode -- prepare to enter plaid! + fldcw CtrlwdSetter // Entering plaid! + fistp RetVal // Store floored and converted (to int) result + fldcw CtrlwdHolder // Restore control word + } +#else + RetVal = static_cast( ceil(a) ); +#endif + + return RetVal; +} + + +// Regular signed area of triangle +#define TriArea2D( A, B, C ) \ + ( 0.5f * ( ( B.x - A.x ) * ( C.y - A.y ) - ( B.y - A.y ) * ( C.x - A.x ) ) ) + +// This version doesn't premultiply by 0.5f, so it's the area of the rectangle instead +#define TriArea2DTimesTwo( A, B, C ) \ + ( ( ( B.x - A.x ) * ( C.y - A.y ) - ( B.y - A.y ) * ( C.x - A.x ) ) ) + + +// Get the barycentric coordinates of "pt" in triangle [A,B,C]. +inline void GetBarycentricCoords2D( + Vector2D const &A, + Vector2D const &B, + Vector2D const &C, + Vector2D const &pt, + float bcCoords[3] ) +{ + // Note, because to top and bottom are both x2, the issue washes out in the composite + float invTriArea = 1.0f / TriArea2DTimesTwo( A, B, C ); + + // NOTE: We assume here that the lightmap coordinate vertices go counterclockwise. + // If not, TriArea2D() is negated so this works out right. + bcCoords[0] = TriArea2DTimesTwo( B, C, pt ) * invTriArea; + bcCoords[1] = TriArea2DTimesTwo( C, A, pt ) * invTriArea; + bcCoords[2] = TriArea2DTimesTwo( A, B, pt ) * invTriArea; +} + + +// Return true of the sphere might touch the box (the sphere is actually treated +// like a box itself, so this may return true if the sphere's bounding box touches +// a corner of the box but the sphere itself doesn't). +inline bool QuickBoxSphereTest( + const Vector& vOrigin, + float flRadius, + const Vector& bbMin, + const Vector& bbMax ) +{ + return vOrigin.x - flRadius < bbMax.x && vOrigin.x + flRadius > bbMin.x && + vOrigin.y - flRadius < bbMax.y && vOrigin.y + flRadius > bbMin.y && + vOrigin.z - flRadius < bbMax.z && vOrigin.z + flRadius > bbMin.z; +} + + +// Return true of the boxes intersect (but not if they just touch). +inline bool QuickBoxIntersectTest( + const Vector& vBox1Min, + const Vector& vBox1Max, + const Vector& vBox2Min, + const Vector& vBox2Max ) +{ + return + vBox1Min.x < vBox2Max.x && vBox1Max.x > vBox2Min.x && + vBox1Min.y < vBox2Max.y && vBox1Max.y > vBox2Min.y && + vBox1Min.z < vBox2Max.z && vBox1Max.z > vBox2Min.z; +} + + +extern float GammaToLinearFullRange( float gamma ); +extern float LinearToGammaFullRange( float linear ); +extern float GammaToLinear( float gamma ); +extern float LinearToGamma( float linear ); + +extern float SrgbGammaToLinear( float flSrgbGammaValue ); +extern float SrgbLinearToGamma( float flLinearValue ); +extern float X360GammaToLinear( float fl360GammaValue ); +extern float X360LinearToGamma( float flLinearValue ); +extern float SrgbGammaTo360Gamma( float flSrgbGammaValue ); + +// linear (0..4) to screen corrected vertex space (0..1?) +FORCEINLINE float LinearToVertexLight( float f ) +{ + extern float lineartovertex[4096]; + + // Gotta clamp before the multiply; could overflow... + // assume 0..4 range + int i = RoundFloatToInt( f * 1024.f ); + + // Presumably the comman case will be not to clamp, so check that first: + if( (unsigned)i > 4095 ) + { + if ( i < 0 ) + i = 0; // Compare to zero instead of 4095 to save 4 bytes in the instruction stream + else + i = 4095; + } + + return lineartovertex[i]; +} + + +FORCEINLINE unsigned char LinearToLightmap( float f ) +{ + extern unsigned char lineartolightmap[4096]; + + // Gotta clamp before the multiply; could overflow... + int i = RoundFloatToInt( f * 1024.f ); // assume 0..4 range + + // Presumably the comman case will be not to clamp, so check that first: + if ( (unsigned)i > 4095 ) + { + if ( i < 0 ) + i = 0; // Compare to zero instead of 4095 to save 4 bytes in the instruction stream + else + i = 4095; + } + + return lineartolightmap[i]; +} + +FORCEINLINE void ColorClamp( Vector& color ) +{ + float maxc = MAX( color.x, MAX( color.y, color.z ) ); + if ( maxc > 1.0f ) + { + float ooMax = 1.0f / maxc; + color.x *= ooMax; + color.y *= ooMax; + color.z *= ooMax; + } + + if ( color[0] < 0.f ) color[0] = 0.f; + if ( color[1] < 0.f ) color[1] = 0.f; + if ( color[2] < 0.f ) color[2] = 0.f; +} + +inline void ColorClampTruncate( Vector& color ) +{ + if (color[0] > 1.0f) color[0] = 1.0f; else if (color[0] < 0.0f) color[0] = 0.0f; + if (color[1] > 1.0f) color[1] = 1.0f; else if (color[1] < 0.0f) color[1] = 0.0f; + if (color[2] > 1.0f) color[2] = 1.0f; else if (color[2] < 0.0f) color[2] = 0.0f; +} + +// Interpolate a Catmull-Rom spline. +// t is a [0,1] value and interpolates a curve between p2 and p3. +void Catmull_Rom_Spline( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector &output ); + +// Interpolate a Catmull-Rom spline. +// Returns the tangent of the point at t of the spline +void Catmull_Rom_Spline_Tangent( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector &output ); + +// area under the curve [0..t] +void Catmull_Rom_Spline_Integral( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// area under the curve [0..1] +void Catmull_Rom_Spline_Integral( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + Vector& output ); + +// Interpolate a Catmull-Rom spline. +// Normalize p2->p1 and p3->p4 to be the same length as p2->p3 +void Catmull_Rom_Spline_Normalize( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector &output ); + +// area under the curve [0..t] +// Normalize p2->p1 and p3->p4 to be the same length as p2->p3 +void Catmull_Rom_Spline_Integral_Normalize( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// Interpolate a Catmull-Rom spline. +// Normalize p2.x->p1.x and p3.x->p4.x to be the same length as p2.x->p3.x +void Catmull_Rom_Spline_NormalizeX( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector &output ); + +// area under the curve [0..t] +void Catmull_Rom_Spline_NormalizeX( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// Interpolate a Hermite spline. +// t is a [0,1] value and interpolates a curve between p1 and p2 with the deltas d1 and d2. +void Hermite_Spline( + const Vector &p1, + const Vector &p2, + const Vector &d1, + const Vector &d2, + float t, + Vector& output ); + +float Hermite_Spline( + float p1, + float p2, + float d1, + float d2, + float t ); + +// t is a [0,1] value and interpolates a curve between p1 and p2 with the slopes p0->p1 and p1->p2 +void Hermite_Spline( + const Vector &p0, + const Vector &p1, + const Vector &p2, + float t, + Vector& output ); + +float Hermite_Spline( + float p0, + float p1, + float p2, + float t ); + + +void Hermite_SplineBasis( float t, float basis[] ); + +void Hermite_Spline( + const Quaternion &q0, + const Quaternion &q1, + const Quaternion &q2, + float t, + Quaternion &output ); + + +// See http://en.wikipedia.org/wiki/Kochanek-Bartels_curves +// +// Tension: -1 = Round -> 1 = Tight +// Bias: -1 = Pre-shoot (bias left) -> 1 = Post-shoot (bias right) +// Continuity: -1 = Box corners -> 1 = Inverted corners +// +// If T=B=C=0 it's the same matrix as Catmull-Rom. +// If T=1 & B=C=0 it's the same as Cubic. +// If T=B=0 & C=-1 it's just linear interpolation +// +// See http://news.povray.org/povray.binaries.tutorials/attachment/%3CXns91B880592482seed7@povray.org%3E/Splines.bas.txt +// for example code and descriptions of various spline types... +// +void Kochanek_Bartels_Spline( + float tension, + float bias, + float continuity, + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +void Kochanek_Bartels_Spline_NormalizeX( + float tension, + float bias, + float continuity, + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// See link at Kochanek_Bartels_Spline for info on the basis matrix used +void Cubic_Spline( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +void Cubic_Spline_NormalizeX( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// See link at Kochanek_Bartels_Spline for info on the basis matrix used +void BSpline( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +void BSpline_NormalizeX( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// See link at Kochanek_Bartels_Spline for info on the basis matrix used +void Parabolic_Spline( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +void Parabolic_Spline_NormalizeX( + const Vector &p1, + const Vector &p2, + const Vector &p3, + const Vector &p4, + float t, + Vector& output ); + +// Evaluate the cubic Bernstein basis for the input parametric coordinate. +// Output is the coefficient for that basis polynomial. +float CubicBasis0( float t ); +float CubicBasis1( float t ); +float CubicBasis2( float t ); +float CubicBasis3( float t ); + +// quintic interpolating polynomial from Perlin. +// 0->0, 1->1, smooth-in between with smooth tangents +FORCEINLINE float QuinticInterpolatingPolynomial(float t) +{ + // 6t^5-15t^4+10t^3 + return t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 ); +} + +// given a table of sorted tabulated positions, return the two indices and blendfactor to linear +// interpolate. Does a search. Can be used to find the blend value to interpolate between +// keyframes. +void GetInterpolationData( float const *pKnotPositions, + float const *pKnotValues, + int nNumValuesinList, + int nInterpolationRange, + float flPositionToInterpolateAt, + bool bWrap, + float *pValueA, + float *pValueB, + float *pInterpolationValue); +float RangeCompressor( float flValue, float flMin, float flMax, float flBase ); + +// Get the minimum distance from vOrigin to the bounding box defined by [mins,maxs] +// using voronoi regions. +// 0 is returned if the origin is inside the box. +float CalcSqrDistanceToAABB( const Vector &mins, const Vector &maxs, const Vector &point ); +void CalcClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point, Vector &closestOut ); +void CalcSqrDistAndClosestPointOnAABB( const Vector &mins, const Vector &maxs, const Vector &point, Vector &closestOut, float &distSqrOut ); + +inline float CalcDistanceToAABB( const Vector &mins, const Vector &maxs, const Vector &point ) +{ + float flDistSqr = CalcSqrDistanceToAABB( mins, maxs, point ); + return sqrt(flDistSqr); +} + +// Get the closest point from P to the (infinite) line through vLineA and vLineB and +// calculate the shortest distance from P to the line. +// If you pass in a value for t, it will tell you the t for (A + (B-A)t) to get the closest point. +// If the closest point lies on the segment between A and B, then 0 <= t <= 1. +void CalcClosestPointOnLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *t=0 ); +float CalcDistanceToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 ); +float CalcDistanceSqrToLine( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 ); + +// The same three functions as above, except now the line is closed between A and B. +void CalcClosestPointOnLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, Vector &vClosest, float *t=0 ); +float CalcDistanceToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 ); +float CalcDistanceSqrToLineSegment( const Vector &P, const Vector &vLineA, const Vector &vLineB, float *t=0 ); + +// A function to compute the closes line segment connnection two lines (or false if the lines are parallel, etc.) +bool CalcLineToLineIntersectionSegment( + const Vector& p1,const Vector& p2,const Vector& p3,const Vector& p4,Vector *s1,Vector *s2, + float *t1, float *t2 ); + +// The above functions in 2D +void CalcClosestPointOnLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, Vector2D &vClosest, float *t=0 ); +float CalcDistanceToLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 ); +float CalcDistanceSqrToLine2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 ); +void CalcClosestPointOnLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, Vector2D &vClosest, float *t=0 ); +float CalcDistanceToLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 ); +float CalcDistanceSqrToLineSegment2D( Vector2D const &P, Vector2D const &vLineA, Vector2D const &vLineB, float *t=0 ); + +// Init the mathlib +void MathLib_Init( float gamma = 2.2f, float texGamma = 2.2f, float brightness = 0.0f, int overbright = 2.0f, bool bAllow3DNow = true, bool bAllowSSE = true, bool bAllowSSE2 = true, bool bAllowMMX = true ); +bool MathLib_MMXEnabled( void ); +bool MathLib_SSEEnabled( void ); +bool MathLib_SSE2Enabled( void ); + +inline float Approach( float target, float value, float speed ); +float ApproachAngle( float target, float value, float speed ); +float AngleDiff( float destAngle, float srcAngle ); +float AngleDistance( float next, float cur ); +float AngleNormalize( float angle ); + +// ensure that 0 <= angle <= 360 +float AngleNormalizePositive( float angle ); + +bool AnglesAreEqual( float a, float b, float tolerance = 0.0f ); + + +void RotationDeltaAxisAngle( const QAngle &srcAngles, const QAngle &destAngles, Vector &deltaAxis, float &deltaAngle ); +void RotationDelta( const QAngle &srcAngles, const QAngle &destAngles, QAngle *out ); + +//----------------------------------------------------------------------------- +// Clips a line segment such that only the portion in the positive half-space +// of the plane remains. If the segment is entirely clipped, the vectors +// are set to vec3_invalid (all components are FLT_MAX). +// +// flBias is added to the dot product with the normal. A positive bias +// results in a more inclusive positive half-space, while a negative bias +// results in a more exclusive positive half-space. +//----------------------------------------------------------------------------- +void ClipLineSegmentToPlane( const Vector &vNormal, const Vector &vPlanePoint, Vector *p1, Vector *p2, float flBias = 0.0f ); + +void ComputeTrianglePlane( const Vector& v1, const Vector& v2, const Vector& v3, Vector& normal, float& intercept ); +int PolyFromPlane( Vector *outVerts, const Vector& normal, float dist, float fHalfScale = 9000.0f ); +int ClipPolyToPlane( Vector *inVerts, int vertCount, Vector *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon = 0.1f ); +int ClipPolyToPlane_Precise( double *inVerts, int vertCount, double *outVerts, const double *normal, double dist, double fOnPlaneEpsilon = 0.1 ); +float TetrahedronVolume( const Vector &p0, const Vector &p1, const Vector &p2, const Vector &p3 ); +float TriangleArea( const Vector &p0, const Vector &p1, const Vector &p2 ); + +//----------------------------------------------------------------------------- +// Computes a reasonable tangent space for a triangle +//----------------------------------------------------------------------------- +void CalcTriangleTangentSpace( const Vector &p0, const Vector &p1, const Vector &p2, + const Vector2D &t0, const Vector2D &t1, const Vector2D& t2, + Vector &sVect, Vector &tVect ); + +//----------------------------------------------------------------------------- +// Transforms a AABB into another space; which will inherently grow the box. +//----------------------------------------------------------------------------- +void TransformAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut ); + +//----------------------------------------------------------------------------- +// Uses the inverse transform of in1 +//----------------------------------------------------------------------------- +void ITransformAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut ); + +//----------------------------------------------------------------------------- +// Rotates a AABB into another space; which will inherently grow the box. +// (same as TransformAABB, but doesn't take the translation into account) +//----------------------------------------------------------------------------- +void RotateAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut ); + +//----------------------------------------------------------------------------- +// Uses the inverse transform of in1 +//----------------------------------------------------------------------------- +void IRotateAABB( const matrix3x4_t &in1, const Vector &vecMinsIn, const Vector &vecMaxsIn, Vector &vecMinsOut, Vector &vecMaxsOut ); + +//----------------------------------------------------------------------------- +// Transform a plane +//----------------------------------------------------------------------------- +inline void MatrixTransformPlane( const matrix3x4_t &src, const cplane_t &inPlane, cplane_t &outPlane ) +{ + // What we want to do is the following: + // 1) transform the normal into the new space. + // 2) Determine a point on the old plane given by plane dist * plane normal + // 3) Transform that point into the new space + // 4) Plane dist = DotProduct( new normal, new point ) + + // An optimized version, which works if the plane is orthogonal. + // 1) Transform the normal into the new space + // 2) Realize that transforming the old plane point into the new space + // is given by [ d * n'x + Tx, d * n'y + Ty, d * n'z + Tz ] + // where d = old plane dist, n' = transformed normal, Tn = translational component of transform + // 3) Compute the new plane dist using the dot product of the normal result of #2 + + // For a correct result, this should be an inverse-transpose matrix + // but that only matters if there are nonuniform scale or skew factors in this matrix. + VectorRotate( inPlane.normal, src, outPlane.normal ); + outPlane.dist = inPlane.dist * DotProduct( outPlane.normal, outPlane.normal ); + outPlane.dist += outPlane.normal.x * src[0][3] + outPlane.normal.y * src[1][3] + outPlane.normal.z * src[2][3]; +} + +inline void MatrixITransformPlane( const matrix3x4_t &src, const cplane_t &inPlane, cplane_t &outPlane ) +{ + // The trick here is that Tn = translational component of transform, + // but for an inverse transform, Tn = - R^-1 * T + Vector vecTranslation; + MatrixGetColumn( src, 3, vecTranslation ); + + Vector vecInvTranslation; + VectorIRotate( vecTranslation, src, vecInvTranslation ); + + VectorIRotate( inPlane.normal, src, outPlane.normal ); + outPlane.dist = inPlane.dist * DotProduct( outPlane.normal, outPlane.normal ); + outPlane.dist -= outPlane.normal.x * vecInvTranslation[0] + outPlane.normal.y * vecInvTranslation[1] + outPlane.normal.z * vecInvTranslation[2]; +} + +int CeilPow2( int in ); +int FloorPow2( int in ); + +FORCEINLINE float * UnpackNormal_HEND3N( const unsigned int *pPackedNormal, float *pNormal ) +{ + int temp[3]; + temp[0] = ((*pPackedNormal >> 0L) & 0x7ff); + if ( temp[0] & 0x400 ) + { + temp[0] = 2048 - temp[0]; + } + temp[1] = ((*pPackedNormal >> 11L) & 0x7ff); + if ( temp[1] & 0x400 ) + { + temp[1] = 2048 - temp[1]; + } + temp[2] = ((*pPackedNormal >> 22L) & 0x3ff); + if ( temp[2] & 0x200 ) + { + temp[2] = 1024 - temp[2]; + } + pNormal[0] = (float)temp[0] * 1.0f/1023.0f; + pNormal[1] = (float)temp[1] * 1.0f/1023.0f; + pNormal[2] = (float)temp[2] * 1.0f/511.0f; + return pNormal; +} + +FORCEINLINE unsigned int * PackNormal_HEND3N( const float *pNormal, unsigned int *pPackedNormal ) +{ + int temp[3]; + + temp[0] = Float2Int( pNormal[0] * 1023.0f ); + temp[1] = Float2Int( pNormal[1] * 1023.0f ); + temp[2] = Float2Int( pNormal[2] * 511.0f ); + + // the normal is out of bounds, determine the source and fix + // clamping would be even more of a slowdown here + Assert( temp[0] >= -1023 && temp[0] <= 1023 ); + Assert( temp[1] >= -1023 && temp[1] <= 1023 ); + Assert( temp[2] >= -511 && temp[2] <= 511 ); + + *pPackedNormal = ( ( temp[2] & 0x3ff ) << 22L ) | + ( ( temp[1] & 0x7ff ) << 11L ) | + ( ( temp[0] & 0x7ff ) << 0L ); + return pPackedNormal; +} + +FORCEINLINE unsigned int * PackNormal_HEND3N( float nx, float ny, float nz, unsigned int *pPackedNormal ) +{ + int temp[3]; + + temp[0] = Float2Int( nx * 1023.0f ); + temp[1] = Float2Int( ny * 1023.0f ); + temp[2] = Float2Int( nz * 511.0f ); + + // the normal is out of bounds, determine the source and fix + // clamping would be even more of a slowdown here + Assert( temp[0] >= -1023 && temp[0] <= 1023 ); + Assert( temp[1] >= -1023 && temp[1] <= 1023 ); + Assert( temp[2] >= -511 && temp[2] <= 511 ); + + *pPackedNormal = ( ( temp[2] & 0x3ff ) << 22L ) | + ( ( temp[1] & 0x7ff ) << 11L ) | + ( ( temp[0] & 0x7ff ) << 0L ); + return pPackedNormal; +} + +FORCEINLINE float * UnpackNormal_SHORT2( const unsigned int *pPackedNormal, float *pNormal, bool bIsTangent = FALSE ) +{ + // Unpacks from Jason's 2-short format (fills in a 4th binormal-sign (+1/-1) value, if this is a tangent vector) + + // FIXME: short math is slow on 360 - use ints here instead (bit-twiddle to deal w/ the sign bits) + short iX = (*pPackedNormal & 0x0000FFFF); + short iY = (*pPackedNormal & 0xFFFF0000) >> 16; + + float zSign = +1; + if ( iX < 0 ) + { + zSign = -1; + iX = -iX; + } + float tSign = +1; + if ( iY < 0 ) + { + tSign = -1; + iY = -iY; + } + + pNormal[0] = ( iX - 16384.0f ) / 16384.0f; + pNormal[1] = ( iY - 16384.0f ) / 16384.0f; + float mag = ( pNormal[0]*pNormal[0] + pNormal[1]*pNormal[1] ); + if ( mag > 1.0f ) + { + mag = 1.0f; + } + pNormal[2] = zSign*sqrtf( 1.0f - mag ); + if ( bIsTangent ) + { + pNormal[3] = tSign; + } + + return pNormal; +} + +FORCEINLINE unsigned int * PackNormal_SHORT2( float nx, float ny, float nz, unsigned int *pPackedNormal, float binormalSign = +1.0f ) +{ + // Pack a vector (ASSUMED TO BE NORMALIZED) into Jason's 4-byte (SHORT2) format. + // This simply reconstructs Z from X & Y. It uses the sign bits of the X & Y coords + // to reconstruct the sign of Z and, if this is a tangent vector, the sign of the + // binormal (this is needed because tangent/binormal vectors are supposed to follow + // UV gradients, but shaders reconstruct the binormal from the tangent and normal + // assuming that they form a right-handed basis). + + nx += 1; // [-1,+1] -> [0,2] + ny += 1; + nx *= 16384.0f; // [ 0, 2] -> [0,32768] + ny *= 16384.0f; + + // '0' and '32768' values are invalid encodings + nx = MAX( nx, 1.0f ); // Make sure there are no zero values + ny = MAX( ny, 1.0f ); + nx = MIN( nx, 32767.0f ); // Make sure there are no 32768 values + ny = MIN( ny, 32767.0f ); + + if ( nz < 0.0f ) + nx = -nx; // Set the sign bit for z + + ny *= binormalSign; // Set the sign bit for the binormal (use when encoding a tangent vector) + + // FIXME: short math is slow on 360 - use ints here instead (bit-twiddle to deal w/ the sign bits), also use Float2Int() + short sX = (short)nx; // signed short [1,32767] + short sY = (short)ny; + + *pPackedNormal = ( sX & 0x0000FFFF ) | ( sY << 16 ); // NOTE: The mask is necessary (if sX is negative and cast to an int...) + + return pPackedNormal; +} + +FORCEINLINE unsigned int * PackNormal_SHORT2( const float *pNormal, unsigned int *pPackedNormal, float binormalSign = +1.0f ) +{ + return PackNormal_SHORT2( pNormal[0], pNormal[1], pNormal[2], pPackedNormal, binormalSign ); +} + +// Unpacks a UBYTE4 normal (for a tangent, the result's fourth component receives the binormal 'sign') +FORCEINLINE float * UnpackNormal_UBYTE4( const unsigned int *pPackedNormal, float *pNormal, bool bIsTangent = FALSE ) +{ + unsigned char cX, cY; + if ( bIsTangent ) + { + cX = *pPackedNormal >> 16; // Unpack Z + cY = *pPackedNormal >> 24; // Unpack W + } + else + { + cX = *pPackedNormal >> 0; // Unpack X + cY = *pPackedNormal >> 8; // Unpack Y + } + + float x = cX - 128.0f; + float y = cY - 128.0f; + float z; + + float zSignBit = x < 0 ? 1.0f : 0.0f; // z and t negative bits (like slt asm instruction) + float tSignBit = y < 0 ? 1.0f : 0.0f; + float zSign = -( 2*zSignBit - 1 ); // z and t signs + float tSign = -( 2*tSignBit - 1 ); + + x = x*zSign - zSignBit; // 0..127 + y = y*tSign - tSignBit; + x = x - 64; // -64..63 + y = y - 64; + + float xSignBit = x < 0 ? 1.0f : 0.0f; // x and y negative bits (like slt asm instruction) + float ySignBit = y < 0 ? 1.0f : 0.0f; + float xSign = -( 2*xSignBit - 1 ); // x and y signs + float ySign = -( 2*ySignBit - 1 ); + + x = ( x*xSign - xSignBit ) / 63.0f; // 0..1 range + y = ( y*ySign - ySignBit ) / 63.0f; + z = 1.0f - x - y; + + float oolen = 1.0f / sqrt( x*x + y*y + z*z ); // Normalize and + x *= oolen * xSign; // Recover signs + y *= oolen * ySign; + z *= oolen * zSign; + + pNormal[0] = x; + pNormal[1] = y; + pNormal[2] = z; + if ( bIsTangent ) + { + pNormal[3] = tSign; + } + + return pNormal; +} + +////////////////////////////////////////////////////////////////////////////// +// See: http://www.oroboro.com/rafael/docserv.php/index/programming/article/unitv2 +// +// UBYTE4 encoding, using per-octant projection onto x+y+z=1 +// Assume input vector is already unit length +// +// binormalSign specifies 'sign' of binormal, stored in t sign bit of tangent +// (lets the shader know whether norm/tan/bin form a right-handed basis) +// +// bIsTangent is used to specify which WORD of the output to store the data +// The expected usage is to call once with the normal and once with +// the tangent and binormal sign flag, bitwise OR'ing the returned DWORDs +FORCEINLINE unsigned int * PackNormal_UBYTE4( float nx, float ny, float nz, unsigned int *pPackedNormal, bool bIsTangent = false, float binormalSign = +1.0f ) +{ + float xSign = nx < 0.0f ? -1.0f : 1.0f; // -1 or 1 sign + float ySign = ny < 0.0f ? -1.0f : 1.0f; + float zSign = nz < 0.0f ? -1.0f : 1.0f; + float tSign = binormalSign; + Assert( ( binormalSign == +1.0f ) || ( binormalSign == -1.0f ) ); + + float xSignBit = 0.5f*( 1 - xSign ); // [-1,+1] -> [1,0] + float ySignBit = 0.5f*( 1 - ySign ); // 1 is negative bit (like slt instruction) + float zSignBit = 0.5f*( 1 - zSign ); + float tSignBit = 0.5f*( 1 - binormalSign ); + + float absX = xSign*nx; // 0..1 range (abs) + float absY = ySign*ny; + float absZ = zSign*nz; + + float xbits = absX / ( absX + absY + absZ ); // Project onto x+y+z=1 plane + float ybits = absY / ( absX + absY + absZ ); + + xbits *= 63; // 0..63 + ybits *= 63; + + xbits = xbits * xSign - xSignBit; // -64..63 range + ybits = ybits * ySign - ySignBit; + xbits += 64.0f; // 0..127 range + ybits += 64.0f; + + xbits = xbits * zSign - zSignBit; // Negate based on z and t + ybits = ybits * tSign - tSignBit; // -128..127 range + + xbits += 128.0f; // 0..255 range + ybits += 128.0f; + + unsigned char cX = (unsigned char) xbits; + unsigned char cY = (unsigned char) ybits; + + if ( !bIsTangent ) + *pPackedNormal = (cX << 0) | (cY << 8); // xy for normal + else + *pPackedNormal = (cX << 16) | (cY << 24); // zw for tangent + + return pPackedNormal; +} + +FORCEINLINE unsigned int * PackNormal_UBYTE4( const float *pNormal, unsigned int *pPackedNormal, bool bIsTangent = false, float binormalSign = +1.0f ) +{ + return PackNormal_UBYTE4( pNormal[0], pNormal[1], pNormal[2], pPackedNormal, bIsTangent, binormalSign ); +} + +FORCEINLINE void RGB2YUV( int &nR, int &nG, int &nB, float &fY, float &fU, float &fV, bool bApplySaturationCurve ) +{ + // YUV conversion: + // |Y| | 0.299f 0.587f 0.114f | |R| + // |U| = | -0.14713f -0.28886f 0.436f | x |G| + // |V| | 0.615f -0.51499f -0.10001f | |B| + // + // The coefficients in the first row sum to one, whereas the 2nd and 3rd rows each sum to zero (UV (0,0) means greyscale). + // Ranges are Y [0,1], U [-0.436,+0.436] and V [-0.615,+0.615]. + // We scale and offset to [0,1] and allow the caller to round as they please. + + fY = ( 0.29900f*nR + 0.58700f*nG + 0.11400f*nB ) / 255; + fU = ( -0.14713f*nR + -0.28886f*nG + 0.43600f*nB )*( 0.5f / 0.436f ) / 255 + 0.5f; + fV = ( 0.61500f*nR + -0.51499f*nG + -0.10001f*nB )*( 0.5f / 0.615f ) / 255 + 0.5f; + + if ( bApplySaturationCurve ) + { + // Apply a curve to saturation, and snap-to-grey for low saturations + const float SNAP_TO_GREY = 0;//0.0125f; Disabled, saturation curve seems sufficient + float dX, dY, sat, scale; + dX = 2*( fU - 0.5f ); + dY = 2*( fV - 0.5f ); + sat = sqrtf( dX*dX + dY*dY ); + sat = clamp( ( sat*( 1 + SNAP_TO_GREY ) - SNAP_TO_GREY ), 0, 1 ); + scale = ( sat == 0 ) ? 0 : MIN( ( sqrtf( sat ) / sat ), 4.0f ); + fU = 0.5f + scale*( fU - 0.5f ); + fV = 0.5f + scale*( fV - 0.5f ); + } +} + +#ifdef _X360 +// Used for direct CPU access to VB data on 360 (used by shaderapi, studiorender and engine) +struct VBCPU_AccessInfo_t +{ + // Points to the GPU data pointer in the CVertexBuffer struct (VB data can be relocated during level transitions) + const byte **ppBaseAddress; + // pBaseAddress should be computed from ppBaseAddress immediately before use + const byte *pBaseAddress; + int nStride; + int nPositionOffset; + int nTexCoord0_Offset; + int nNormalOffset; + int nBoneIndexOffset; + int nBoneWeightOffset; + int nCompressionType; + // TODO: if needed, add colour and tangents +}; +#endif + +//----------------------------------------------------------------------------- +// Convert RGB to HSV +//----------------------------------------------------------------------------- +void RGBtoHSV( const Vector &rgb, Vector &hsv ); + + +//----------------------------------------------------------------------------- +// Convert HSV to RGB +//----------------------------------------------------------------------------- +void HSVtoRGB( const Vector &hsv, Vector &rgb ); + + +//----------------------------------------------------------------------------- +// Fast version of pow and log +//----------------------------------------------------------------------------- + +float FastLog2(float i); // log2( i ) +float FastPow2(float i); // 2^i +float FastPow(float a, float b); // a^b +float FastPow10( float i ); // 10^i + +//----------------------------------------------------------------------------- +// For testing float equality +//----------------------------------------------------------------------------- + +inline bool CloseEnough( float a, float b, float epsilon = EQUAL_EPSILON ) +{ + return fabs( a - b ) <= epsilon; +} + +inline bool CloseEnough( const Vector &a, const Vector &b, float epsilon = EQUAL_EPSILON ) +{ + return fabs( a.x - b.x ) <= epsilon && + fabs( a.y - b.y ) <= epsilon && + fabs( a.z - b.z ) <= epsilon; +} + +// Fast compare +// maxUlps is the maximum error in terms of Units in the Last Place. This +// specifies how big an error we are willing to accept in terms of the value +// of the least significant digit of the floating point number’s +// representation. maxUlps can also be interpreted in terms of how many +// representable floats we are willing to accept between A and B. +// This function will allow maxUlps-1 floats between A and B. +bool AlmostEqual(float a, float b, int maxUlps = 10); + +inline bool AlmostEqual( const Vector &a, const Vector &b, int maxUlps = 10) +{ + return AlmostEqual( a.x, b.x, maxUlps ) && + AlmostEqual( a.y, b.y, maxUlps ) && + AlmostEqual( a.z, b.z, maxUlps ); +} + +inline float Approach( float target, float value, float speed ) +{ + float delta = target - value; + +#if defined(_X360) || defined( PS3 ) // use conditional move for speed on 360 + + return fsel( delta-speed, // delta >= speed ? + value + speed, // if delta == speed, then value + speed == value + delta == target + fsel( (-speed) - delta, // delta <= -speed + value - speed, + target ) + ); // delta < speed && delta > -speed + +#else + + if ( delta > speed ) + value += speed; + else if ( delta < -speed ) + value -= speed; + else + value = target; + + return value; + +#endif +} + +#endif // MATH_BASE_H + diff --git a/public/mathlib/noise.h b/public/mathlib/noise.h new file mode 100644 index 0000000..0300363 --- /dev/null +++ b/public/mathlib/noise.h @@ -0,0 +1,35 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=====================================================================================// + +#ifndef NOISE_H +#define NOISE_H + +#include +#include "basetypes.h" +#include "mathlib/vector.h" +#include "tier0/dbg.h" + + +// The following code is the c-ification of Ken Perlin's new noise algorithm +// "JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN" +// as available here: http://mrl.nyu.edu/~perlin/noise/ +// it generates a single octave of noise in the -1..1 range +// this should at some point probably replace SparseConvolutionNoise - jd +float ImprovedPerlinNoise( Vector const &pnt ); + +// get the noise value at a point. Output range is 0..1. +float SparseConvolutionNoise( Vector const &pnt ); + +// get the noise value at a point, passing a custom noise shaping function. The noise shaping +// function should map the domain 0..1 to 0..1. +float SparseConvolutionNoise(Vector const &pnt, float (*pNoiseShapeFunction)(float) ); + +// returns a 1/f noise. more octaves take longer +float FractalNoise( Vector const &pnt, int n_octaves ); + +// returns a abs(f)*1/f noise i.e. turbulence +float Turbulence( Vector const &pnt, int n_octaves ); +#endif // NOISE_H diff --git a/public/mathlib/polyhedron.h b/public/mathlib/polyhedron.h new file mode 100644 index 0000000..cf01997 --- /dev/null +++ b/public/mathlib/polyhedron.h @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef POLYHEDRON_H_ +#define POLYHEDRON_H_ + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/mathlib.h" + + + +struct Polyhedron_IndexedLine_t +{ + unsigned short iPointIndices[2]; +}; + +struct Polyhedron_IndexedLineReference_t +{ + unsigned short iLineIndex; + unsigned char iEndPointIndex; //since two polygons reference any one line, one needs to traverse the line backwards, this flags that behavior +}; + +struct Polyhedron_IndexedPolygon_t +{ + unsigned short iFirstIndex; + unsigned short iIndexCount; + Vector polyNormal; +}; + +class CPolyhedron //made into a class because it's going virtual to support distinctions between temp and permanent versions +{ +public: + Vector *pVertices; + Polyhedron_IndexedLine_t *pLines; + Polyhedron_IndexedLineReference_t *pIndices; + Polyhedron_IndexedPolygon_t *pPolygons; + + unsigned short iVertexCount; + unsigned short iLineCount; + unsigned short iIndexCount; + unsigned short iPolygonCount; + + virtual ~CPolyhedron( void ) {}; + virtual void Release( void ) = 0; + Vector Center( void ); +}; + +class CPolyhedron_AllocByNew : public CPolyhedron +{ +public: + virtual void Release( void ); + static CPolyhedron_AllocByNew *Allocate( unsigned short iVertices, unsigned short iLines, unsigned short iIndices, unsigned short iPolygons ); //creates the polyhedron along with enough memory to hold all it's data in a single allocation + +private: + CPolyhedron_AllocByNew( void ) { }; //CPolyhedron_AllocByNew::Allocate() is the only way to create one of these. +}; + +CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory = false ); //be sure to polyhedron->Release() +CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory = false ); //this does NOT modify/delete the existing polyhedron + +CPolyhedron *GetTempPolyhedron( unsigned short iVertices, unsigned short iLines, unsigned short iIndices, unsigned short iPolygons ); //grab the temporary polyhedron. Avoids new/delete for quick work. Can only be in use by one chunk of code at a time + + +#endif //#ifndef POLYHEDRON_H_ + diff --git a/public/mathlib/quantize.h b/public/mathlib/quantize.h new file mode 100644 index 0000000..af9618f --- /dev/null +++ b/public/mathlib/quantize.h @@ -0,0 +1,141 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef QUANTIZE_H +#define QUANTIZE_H + +#ifndef STRING_H +#include +#endif + +#define MAXDIMS 768 +#define MAXQUANT 16000 + + +#include + +struct Sample; + +struct QuantizedValue { + double MinError; // minimum possible error. used + // for neighbor searches. + struct QuantizedValue *Children[2]; // splits + int32 value; // only exists for leaf nodes + struct Sample *Samples; // every sample quantized into this + // entry + int32 NSamples; // how many were quantized to this. + int32 TotSamples; + double *ErrorMeasure; // variance measure for each dimension + double TotalError; // sum of errors + uint8 *Mean; // average value of each dimension + uint8 *Mins; // min box for children and this + uint8 *Maxs; // max box for children and this + int NQuant; // the number of samples which were + // quantzied to this node since the + // last time OptimizeQuantizer() + // was called. + int *Sums; // sum used by OptimizeQuantizer + int sortdim; // dimension currently sorted along. +}; + +struct Sample { + int32 ID; // identifier of this sample. can + // be used for any purpose. + int32 Count; // number of samples this sample + // represents + int32 QNum; // what value this sample ended up quantized + // to. + struct QuantizedValue *qptr; // ptr to what this was quantized to. + uint8 Value[1]; // array of values for multi-dimensional + // variables. +}; + +void FreeQuantization(struct QuantizedValue *t); + +struct QuantizedValue *Quantize(struct Sample *s, int nsamples, int ndims, + int nvalues, uint8 *weights, int value0=0); + +int CompressSamples(struct Sample *s, int nsamples, int ndims); + +struct QuantizedValue *FindMatch(uint8 const *sample, + int ndims,uint8 *weights, + struct QuantizedValue *QTable); +void PrintSamples(struct Sample const *s, int nsamples, int ndims); + +struct QuantizedValue *FindQNode(struct QuantizedValue const *q, int32 code); + +inline struct Sample *NthSample(struct Sample *s, int i, int nd) +{ + uint8 *r=(uint8 *) s; + r+=i*(sizeof(*s)+(nd-1)); + return (struct Sample *) r; +} + +inline struct Sample *AllocSamples(int ns, int nd) +{ + size_t size5=(sizeof(struct Sample)+(nd-1))*ns; + void *ret=new uint8[size5]; + memset(ret,0,size5); + for(int i=0;iCount=1; + return (struct Sample *) ret; +} + + +// MinimumError: what is the min error which will occur if quantizing +// a sample to the given qnode? This is just the error if the qnode +// is a leaf. +double MinimumError(struct QuantizedValue const *q, uint8 const *sample, + int ndims, uint8 const *weights); +double MaximumError(struct QuantizedValue const *q, uint8 const *sample, + int ndims, uint8 const *weights); + +void PrintQTree(struct QuantizedValue const *p,int idlevel=0); +void OptimizeQuantizer(struct QuantizedValue *q, int ndims); + +// RecalculateVelues: update the means in a sample tree, based upon +// the samples. can be used to reoptimize when samples are deleted, +// for instance. + +void RecalculateValues(struct QuantizedValue *q, int ndims); + +extern double SquaredError; // may be reset and examined. updated by + // FindMatch() + + + + +// the routines below can be used for uniform quantization via dart-throwing. +typedef void (*GENERATOR)(void *); // generate a random sample +typedef double (*COMPARER)(void const *a, void const *b); + +void *DartThrow(int NResults, int NTries, size_t itemsize, GENERATOR gen, + COMPARER cmp); +void *FindClosestDart(void *items,int NResults, size_t itemsize, + COMPARER cmp, void *lookfor, int *idx); + + + + +// color quantization of 24 bit images +#define QUANTFLAGS_NODITHER 1 // don't do Floyd-steinberg dither + +extern void ColorQuantize( +uint8 const *pImage, // 4 byte pixels ARGB +int nWidth, +int nHeight, +int nFlags, // QUANTFLAGS_xxx +int nColors, // # of colors to fill in in palette +uint8 *pOutPixels, // where to store resulting 8 bit pixels +uint8 *pOutPalette, // where to store resulting 768-byte palette +int nFirstColor); // first color to use in mapping + + + + + +#endif diff --git a/public/mathlib/simdvectormatrix.h b/public/mathlib/simdvectormatrix.h new file mode 100644 index 0000000..9f5267e --- /dev/null +++ b/public/mathlib/simdvectormatrix.h @@ -0,0 +1,142 @@ +//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. =======// +// +// Purpose: Provide a class (SSE/SIMD only) holding a 2d matrix of class FourVectors, +// for high speed processing in tools. +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef SIMDVECTORMATRIX_H +#define SIMDVECTORMATRIX_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/utlsoacontainer.h" +#include "mathlib/ssemath.h" + +class CSIMDVectorMatrix +{ +public: + int m_nWidth; // in actual vectors + int m_nHeight; + + int m_nPaddedWidth; // # of 4x wide elements + + FourVectors *m_pData; + +protected: + void Init( void ) + { + m_pData = NULL; + m_nWidth = 0; + m_nHeight = 0; + m_nPaddedWidth = 0; + } + + int NVectors( void ) const + { + return m_nHeight * m_nPaddedWidth; + } + +public: + // constructors and destructors + CSIMDVectorMatrix( void ) + { + Init(); + } + + ~CSIMDVectorMatrix( void ) + { + if ( m_pData ) + delete[] m_pData; + } + + // set up storage and fields for m x n matrix. destroys old data + void SetSize( int width, int height ) + { + if ( ( ! m_pData ) || ( width != m_nWidth ) || ( height != m_nHeight ) ) + { + if ( m_pData ) + delete[] m_pData; + + m_nWidth = width; + m_nHeight = height; + + m_nPaddedWidth = ( m_nWidth + 3) >> 2; + m_pData = NULL; + if ( width && height ) + m_pData = new FourVectors[ m_nPaddedWidth * m_nHeight ]; + } + } + + CSIMDVectorMatrix( int width, int height ) + { + Init(); + SetSize( width, height ); + } + + CSIMDVectorMatrix &operator=( CSIMDVectorMatrix const &src ) + { + SetSize( src.m_nWidth, src.m_nHeight ); + if ( m_pData ) + memcpy( m_pData, src.m_pData, m_nHeight*m_nPaddedWidth*sizeof(m_pData[0]) ); + return *this; + } + + CSIMDVectorMatrix &operator+=( CSIMDVectorMatrix const &src ); + + CSIMDVectorMatrix &operator*=( Vector const &src ); + + // create from an RGBA float bitmap. alpha ignored. + void CreateFromRGBA_FloatImageData(int srcwidth, int srcheight, float const *srcdata ); + + // create from 3 fields in a csoa + void CreateFromCSOAAttributes( CSOAContainer const *pSrc, + int nAttrIdx0, int nAttrIdx1, int nAttrIdx2 ); + + // Element access. If you are calling this a lot, you don't want to use this class, because + // you're not getting the sse advantage + Vector Element(int x, int y) const + { + Assert( m_pData ); + Assert( x < m_nWidth ); + Assert( y < m_nHeight ); + Vector ret; + FourVectors const *pData=m_pData+y*m_nPaddedWidth+(x >> 2); + + int xo=(x & 3); + ret.x=pData->X( xo ); + ret.y=pData->Y( xo ); + ret.z=pData->Z( xo ); + return ret; + } + + //addressing the individual fourvectors elements + FourVectors &CompoundElement(int x, int y) + { + Assert( m_pData ); + Assert( y < m_nHeight ); + Assert( x < m_nPaddedWidth ); + return m_pData[x + m_nPaddedWidth*y ]; + } + + // math operations on the whole image + void Clear( void ) + { + Assert( m_pData ); + memset( m_pData, 0, m_nHeight*m_nPaddedWidth*sizeof(m_pData[0]) ); + } + + void RaiseToPower( float power ); +}; + + + +#endif diff --git a/public/mathlib/spherical_geometry.h b/public/mathlib/spherical_geometry.h new file mode 100644 index 0000000..c053021 --- /dev/null +++ b/public/mathlib/spherical_geometry.h @@ -0,0 +1,73 @@ +//====== Copyright © 2007-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: Functions for spherical geometry. +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef SPHERICAL_GEOMETRY_H +#define SPHERICAL_GEOMETRY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// see http://mathworld.wolfram.com/SphericalTrigonometry.html + +// return the spherical distance, in radians, between 2 points on the unit sphere. +FORCEINLINE float UnitSphereLineSegmentLength( Vector const &a, Vector const &b ) +{ + // check unit length + Assert( fabs( VectorLength( a ) - 1.0 ) < 1.0e-3 ); + Assert( fabs( VectorLength( b ) - 1.0 ) < 1.0e-3 ); + return acos( DotProduct( a, b ) ); +} + + +// given 3 points on the unit sphere, return the spherical area (in radians) of the triangle they form. +// valid for "small" triangles. +FORCEINLINE float UnitSphereTriangleArea( Vector const &a, Vector const &b , Vector const &c ) +{ + float flLengthA = UnitSphereLineSegmentLength( b, c ); + float flLengthB = UnitSphereLineSegmentLength( c, a ); + float flLengthC = UnitSphereLineSegmentLength( a, b ); + + if ( ( flLengthA == 0. ) || ( flLengthB == 0. ) || ( flLengthC == 0. ) ) + return 0.; // zero area triangle + + // now, find the 3 incribed angles for the triangle + float flHalfSumLens = 0.5 * ( flLengthA + flLengthB + flLengthC ); + float flSinSums = sin( flHalfSumLens ); + float flSinSMinusA= sin( flHalfSumLens - flLengthA ); + float flSinSMinusB= sin( flHalfSumLens - flLengthB ); + float flSinSMinusC= sin( flHalfSumLens - flLengthC ); + + float flTanAOver2 = sqrt ( ( flSinSMinusB * flSinSMinusC ) / ( flSinSums * flSinSMinusA ) ); + float flTanBOver2 = sqrt ( ( flSinSMinusA * flSinSMinusC ) / ( flSinSums * flSinSMinusB ) ); + float flTanCOver2 = sqrt ( ( flSinSMinusA * flSinSMinusB ) / ( flSinSums * flSinSMinusC ) ); + + // Girards formula : area = sum of angles - pi. + return 2.0 * ( atan( flTanAOver2 ) + atan( flTanBOver2 ) + atan( flTanCOver2 ) ) - M_PI; +} + +// spherical harmonics-related functions. Best explanation at http://www.research.scea.com/gdc2003/spherical-harmonic-lighting.pdf + +// Evaluate associated legendre polynomial P( l, m ) at flX, using recurrence relation +float AssociatedLegendrePolynomial( int nL, int nM, float flX ); + +// Evaluate order N spherical harmonic with spherical coordinates +// nL = band, 0..N +// nM = -nL .. nL +// theta = 0..M_PI +// phi = 0.. 2 * M_PHI +float SphericalHarmonic( int nL, int nM, float flTheta, float flPhi ); + +// evaluate spherical harmonic with normalized vector direction +float SphericalHarmonic( int nL, int nM, Vector const &vecDirection ); + + +#endif // SPHERICAL_GEOMETRY_H diff --git a/public/mathlib/ssemath.h b/public/mathlib/ssemath.h new file mode 100644 index 0000000..7f35ee3 --- /dev/null +++ b/public/mathlib/ssemath.h @@ -0,0 +1,4123 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: - defines SIMD "structure of arrays" classes and functions. +// +//===========================================================================// +#ifndef SSEMATH_H +#define SSEMATH_H + +#if defined( _X360 ) +#include +#else +#include +#ifndef _LINUX +#include +#endif +#endif + +#include +#include +#include + +#if defined(GNUC) +#define USE_STDC_FOR_SIMD 0 +#else +#define USE_STDC_FOR_SIMD 0 +#endif + +#if (!defined(_X360) && (USE_STDC_FOR_SIMD == 0)) +#define _SSE1 1 +#endif + +// I thought about defining a class/union for the SIMD packed floats instead of using fltx4, +// but decided against it because (a) the nature of SIMD code which includes comparisons is to blur +// the relationship between packed floats and packed integer types and (b) not sure that the +// compiler would handle generating good code for the intrinsics. + +#if USE_STDC_FOR_SIMD + +#error "hello" +typedef union +{ + float m128_f32[4]; + uint32 m128_u32[4]; +} fltx4; + +typedef fltx4 i32x4; +typedef fltx4 u32x4; + +#elif ( defined( _X360 ) ) + +typedef union +{ + // This union allows float/int access (which generally shouldn't be done in inner loops) + __vector4 vmx; + float m128_f32[4]; + uint32 m128_u32[4]; +} fltx4_union; + +typedef __vector4 fltx4; +typedef __vector4 i32x4; // a VMX register; just a way of making it explicit that we're doing integer ops. +typedef __vector4 u32x4; // a VMX register; just a way of making it explicit that we're doing unsigned integer ops. + +#else + +typedef __m128 fltx4; +typedef __m128 i32x4; +typedef __m128 u32x4; + +#endif + +// The FLTX4 type is a fltx4 used as a parameter to a function. +// On the 360, the best way to do this is pass-by-copy on the registers. +// On the PC, the best way is to pass by const reference. +// The compiler will sometimes, but not always, replace a pass-by-const-ref +// with a pass-in-reg on the 360; to avoid this confusion, you can +// explicitly use a FLTX4 as the parameter type. +#ifdef _X360 +typedef __vector4 FLTX4; +#else +typedef const fltx4 & FLTX4; +#endif + +// A 16-byte aligned int32 datastructure +// (for use when writing out fltx4's as SIGNED +// ints). +struct ALIGN16 intx4 +{ + int32 m_i32[4]; + + inline int & operator[](int which) + { + return m_i32[which]; + } + + inline const int & operator[](int which) const + { + return m_i32[which]; + } + + inline int32 *Base() { + return m_i32; + } + + inline const int32 *Base() const + { + return m_i32; + } + + inline const bool operator==(const intx4 &other) const + { + return m_i32[0] == other.m_i32[0] && + m_i32[1] == other.m_i32[1] && + m_i32[2] == other.m_i32[2] && + m_i32[3] == other.m_i32[3] ; + } +} ALIGN16_POST; + + +#if defined( _DEBUG ) && defined( _X360 ) +FORCEINLINE void TestVPUFlags() +{ + // Check that the VPU is in the appropriate (Java-compliant) mode (see 3.2.1 in altivec_pem.pdf on xds.xbox.com) + __vector4 a; + __asm + { + mfvscr a; + } + unsigned int * flags = (unsigned int *)&a; + unsigned int controlWord = flags[3]; + Assert(controlWord == 0); +} +#else // _DEBUG +FORCEINLINE void TestVPUFlags() {} +#endif // _DEBUG + + +// useful constants in SIMD packed float format: +// (note: some of these aren't stored on the 360, +// but are manufactured directly in one or two +// instructions, saving a load and possible L2 +// miss.) +#ifndef _X360 +extern const fltx4 Four_Zeros; // 0 0 0 0 +extern const fltx4 Four_Ones; // 1 1 1 1 +extern const fltx4 Four_Twos; // 2 2 2 2 +extern const fltx4 Four_Threes; // 3 3 3 3 +extern const fltx4 Four_Fours; // guess. +extern const fltx4 Four_Point225s; // .225 .225 .225 .225 +extern const fltx4 Four_PointFives; // .5 .5 .5 .5 +extern const fltx4 Four_Thirds; // 1/3 +extern const fltx4 Four_TwoThirds; // 2/3 +extern const fltx4 Four_Epsilons; // FLT_EPSILON FLT_EPSILON FLT_EPSILON FLT_EPSILON +extern const fltx4 Four_2ToThe21s; // (1<<21).. +extern const fltx4 Four_2ToThe22s; // (1<<22).. +extern const fltx4 Four_2ToThe23s; // (1<<23).. +extern const fltx4 Four_2ToThe24s; // (1<<24).. +extern const fltx4 Four_Origin; // 0 0 0 1 (origin point, like vr0 on the PS2) +extern const fltx4 Four_NegativeOnes; // -1 -1 -1 -1 +#else +#define Four_Zeros XMVectorZero() // 0 0 0 0 +#define Four_Ones XMVectorSplatOne() // 1 1 1 1 +extern const fltx4 Four_Twos; // 2 2 2 2 +extern const fltx4 Four_Threes; // 3 3 3 3 +extern const fltx4 Four_Fours; // guess. +extern const fltx4 Four_Point225s; // .225 .225 .225 .225 +extern const fltx4 Four_PointFives; // .5 .5 .5 .5 +extern const fltx4 Four_Thirds; // 1/3 +extern const fltx4 Four_TwoThirds; // 2/3 +extern const fltx4 Four_Epsilons; // FLT_EPSILON FLT_EPSILON FLT_EPSILON FLT_EPSILON +extern const fltx4 Four_2ToThe21s; // (1<<21).. +extern const fltx4 Four_2ToThe22s; // (1<<22).. +extern const fltx4 Four_2ToThe23s; // (1<<23).. +extern const fltx4 Four_2ToThe24s; // (1<<24).. +extern const fltx4 Four_Origin; // 0 0 0 1 (origin point, like vr0 on the PS2) +extern const fltx4 Four_NegativeOnes; // -1 -1 -1 -1 +#endif +extern const fltx4 Four_FLT_MAX; // FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX +extern const fltx4 Four_Negative_FLT_MAX; // -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX +extern const fltx4 g_SIMD_0123; // 0 1 2 3 as float + +// external aligned integer constants +#ifndef ALIGN16_POST +#define ALIGN16_POST +#endif +extern const ALIGN16 int32 g_SIMD_clear_signmask[] ALIGN16_POST; // 0x7fffffff x 4 +extern const ALIGN16 int32 g_SIMD_signmask[] ALIGN16_POST; // 0x80000000 x 4 +extern const ALIGN16 int32 g_SIMD_lsbmask[] ALIGN16_POST; // 0xfffffffe x 4 +extern const ALIGN16 int32 g_SIMD_clear_wmask[] ALIGN16_POST; // -1 -1 -1 0 +extern const ALIGN16 int32 g_SIMD_ComponentMask[4][4] ALIGN16_POST; // [0xFFFFFFFF 0 0 0], [0 0xFFFFFFFF 0 0], [0 0 0xFFFFFFFF 0], [0 0 0 0xFFFFFFFF] +extern const ALIGN16 int32 g_SIMD_AllOnesMask[] ALIGN16_POST; // ~0,~0,~0,~0 +extern const ALIGN16 int32 g_SIMD_Low16BitsMask[] ALIGN16_POST; // 0xffff x 4 + +// this mask is used for skipping the tail of things. If you have N elements in an array, and wish +// to mask out the tail, g_SIMD_SkipTailMask[N & 3] what you want to use for the last iteration. +extern const int32 ALIGN16 g_SIMD_SkipTailMask[4][4] ALIGN16_POST; + +extern const int32 ALIGN16 g_SIMD_EveryOtherMask[]; // 0, ~0, 0, ~0 +// Define prefetch macros. +// The characteristics of cache and prefetch are completely +// different between the different platforms, so you DO NOT +// want to just define one macro that maps to every platform +// intrinsic under the hood -- you need to prefetch at different +// intervals between x86 and PPC, for example, and that is +// a higher level code change. +// On the other hand, I'm tired of typing #ifdef _X360 +// all over the place, so this is just a nop on Intel, PS3. +#ifdef _X360 +#define PREFETCH360(address, offset) __dcbt(offset,address) +#else +#define PREFETCH360(x,y) // nothing +#endif + +// Here's a handy function to align a pointer to the next +// sixteen byte boundary -- it'll round it up to the nearest +// multiple of 16. This is useful if you're subdividing +// big swaths of allocated memory, but in that case, remember +// to leave yourself the necessary slack in the allocation. +template +inline T *AlignPointer(void * ptr) +{ + unsigned temp = ptr; + temp = ALIGN_VALUE(temp, sizeof(T)); + return (T *)temp; +} + +// Define prefetch macros. +// The characteristics of cache and prefetch are completely +// different between the different platforms, so you DO NOT +// want to just define one macro that maps to every platform +// intrinsic under the hood -- you need to prefetch at different +// intervals between x86 and PPC, for example, and that is +// a higher level code change. +// On the other hand, I'm tired of typing #ifdef _X360 +// all over the place, so this is just a nop on Intel, PS3. +#ifdef _X360 +#define PREFETCH360(address, offset) __dcbt(offset,address) +#else +#define PREFETCH360(x,y) // nothing +#endif + +#if USE_STDC_FOR_SIMD + +//--------------------------------------------------------------------- +// Standard C (fallback/Linux) implementation (only there for compat - slow) +//--------------------------------------------------------------------- + +FORCEINLINE float SubFloat( const fltx4 & a, int idx ) +{ + return a.m128_f32[ idx ]; +} + +FORCEINLINE float & SubFloat( fltx4 & a, int idx ) +{ + return a.m128_f32[idx]; +} + +FORCEINLINE uint32 SubInt( const fltx4 & a, int idx ) +{ + return a.m128_u32[idx]; +} + +FORCEINLINE uint32 & SubInt( fltx4 & a, int idx ) +{ + return a.m128_u32[idx]; +} + +// Return one in the fastest way -- on the x360, faster even than loading. +FORCEINLINE fltx4 LoadZeroSIMD( void ) +{ + return Four_Zeros; +} + +// Return one in the fastest way -- on the x360, faster even than loading. +FORCEINLINE fltx4 LoadOneSIMD( void ) +{ + return Four_Ones; +} + +FORCEINLINE fltx4 SplatXSIMD( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 0 ); + SubFloat( retVal, 1 ) = SubFloat( a, 0 ); + SubFloat( retVal, 2 ) = SubFloat( a, 0 ); + SubFloat( retVal, 3 ) = SubFloat( a, 0 ); + return retVal; +} + +FORCEINLINE fltx4 SplatYSIMD( fltx4 a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 1 ); + SubFloat( retVal, 1 ) = SubFloat( a, 1 ); + SubFloat( retVal, 2 ) = SubFloat( a, 1 ); + SubFloat( retVal, 3 ) = SubFloat( a, 1 ); + return retVal; +} + +FORCEINLINE fltx4 SplatZSIMD( fltx4 a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 2 ); + SubFloat( retVal, 1 ) = SubFloat( a, 2 ); + SubFloat( retVal, 2 ) = SubFloat( a, 2 ); + SubFloat( retVal, 3 ) = SubFloat( a, 2 ); + return retVal; +} + +FORCEINLINE fltx4 SplatWSIMD( fltx4 a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 3 ); + SubFloat( retVal, 1 ) = SubFloat( a, 3 ); + SubFloat( retVal, 2 ) = SubFloat( a, 3 ); + SubFloat( retVal, 3 ) = SubFloat( a, 3 ); + return retVal; +} + +FORCEINLINE fltx4 SetXSIMD( const fltx4& a, const fltx4& x ) +{ + fltx4 result = a; + SubFloat( result, 0 ) = SubFloat( x, 0 ); + return result; +} + +FORCEINLINE fltx4 SetYSIMD( const fltx4& a, const fltx4& y ) +{ + fltx4 result = a; + SubFloat( result, 1 ) = SubFloat( y, 1 ); + return result; +} + +FORCEINLINE fltx4 SetZSIMD( const fltx4& a, const fltx4& z ) +{ + fltx4 result = a; + SubFloat( result, 2 ) = SubFloat( z, 2 ); + return result; +} + +FORCEINLINE fltx4 SetWSIMD( const fltx4& a, const fltx4& w ) +{ + fltx4 result = a; + SubFloat( result, 3 ) = SubFloat( w, 3 ); + return result; +} + +FORCEINLINE fltx4 SetComponentSIMD( const fltx4& a, int nComponent, float flValue ) +{ + fltx4 result = a; + SubFloat( result, nComponent ) = flValue; + return result; +} + + +// a b c d -> b c d a +FORCEINLINE fltx4 RotateLeft( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 1 ); + SubFloat( retVal, 1 ) = SubFloat( a, 2 ); + SubFloat( retVal, 2 ) = SubFloat( a, 3 ); + SubFloat( retVal, 3 ) = SubFloat( a, 0 ); + return retVal; +} + +// a b c d -> c d a b +FORCEINLINE fltx4 RotateLeft2( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = SubFloat( a, 2 ); + SubFloat( retVal, 1 ) = SubFloat( a, 3 ); + SubFloat( retVal, 2 ) = SubFloat( a, 0 ); + SubFloat( retVal, 3 ) = SubFloat( a, 1 ); + return retVal; +} + +#define BINOP(op) \ + fltx4 retVal; \ + SubFloat( retVal, 0 ) = ( SubFloat( a, 0 ) op SubFloat( b, 0 ) ); \ + SubFloat( retVal, 1 ) = ( SubFloat( a, 1 ) op SubFloat( b, 1 ) ); \ + SubFloat( retVal, 2 ) = ( SubFloat( a, 2 ) op SubFloat( b, 2 ) ); \ + SubFloat( retVal, 3 ) = ( SubFloat( a, 3 ) op SubFloat( b, 3 ) ); \ + return retVal; + +#define IBINOP(op) \ + fltx4 retVal; \ + SubInt( retVal, 0 ) = ( SubInt( a, 0 ) op SubInt ( b, 0 ) ); \ + SubInt( retVal, 1 ) = ( SubInt( a, 1 ) op SubInt ( b, 1 ) ); \ + SubInt( retVal, 2 ) = ( SubInt( a, 2 ) op SubInt ( b, 2 ) ); \ + SubInt( retVal, 3 ) = ( SubInt( a, 3 ) op SubInt ( b, 3 ) ); \ + return retVal; + +FORCEINLINE fltx4 AddSIMD( const fltx4 & a, const fltx4 & b ) +{ + BINOP(+); +} + +FORCEINLINE fltx4 SubSIMD( const fltx4 & a, const fltx4 & b ) // a-b +{ + BINOP(-); +}; + +FORCEINLINE fltx4 MulSIMD( const fltx4 & a, const fltx4 & b ) // a*b +{ + BINOP(*); +} + +FORCEINLINE fltx4 DivSIMD( const fltx4 & a, const fltx4 & b ) // a/b +{ + BINOP(/); +} + + +FORCEINLINE fltx4 MaddSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // a*b + c +{ + return AddSIMD( MulSIMD(a,b), c ); +} + +FORCEINLINE fltx4 MsubSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // c - a*b +{ + return SubSIMD( c, MulSIMD(a,b) ); +}; + + +FORCEINLINE fltx4 SinSIMD( const fltx4 &radians ) +{ + fltx4 result; + SubFloat( result, 0 ) = sin( SubFloat( radians, 0 ) ); + SubFloat( result, 1 ) = sin( SubFloat( radians, 1 ) ); + SubFloat( result, 2 ) = sin( SubFloat( radians, 2 ) ); + SubFloat( result, 3 ) = sin( SubFloat( radians, 3 ) ); + return result; +} + +FORCEINLINE void SinCos3SIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) +{ + SinCos( SubFloat( radians, 0 ), &SubFloat( sine, 0 ), &SubFloat( cosine, 0 ) ); + SinCos( SubFloat( radians, 1 ), &SubFloat( sine, 1 ), &SubFloat( cosine, 1 ) ); + SinCos( SubFloat( radians, 2 ), &SubFloat( sine, 2 ), &SubFloat( cosine, 2 ) ); +} + +FORCEINLINE void SinCosSIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) +{ + SinCos( SubFloat( radians, 0 ), &SubFloat( sine, 0 ), &SubFloat( cosine, 0 ) ); + SinCos( SubFloat( radians, 1 ), &SubFloat( sine, 1 ), &SubFloat( cosine, 1 ) ); + SinCos( SubFloat( radians, 2 ), &SubFloat( sine, 2 ), &SubFloat( cosine, 2 ) ); + SinCos( SubFloat( radians, 3 ), &SubFloat( sine, 3 ), &SubFloat( cosine, 3 ) ); +} + +FORCEINLINE fltx4 ArcSinSIMD( const fltx4 &sine ) +{ + fltx4 result; + SubFloat( result, 0 ) = asin( SubFloat( sine, 0 ) ); + SubFloat( result, 1 ) = asin( SubFloat( sine, 1 ) ); + SubFloat( result, 2 ) = asin( SubFloat( sine, 2 ) ); + SubFloat( result, 3 ) = asin( SubFloat( sine, 3 ) ); + return result; +} + +FORCEINLINE fltx4 ArcCosSIMD( const fltx4 &cs ) +{ + fltx4 result; + SubFloat( result, 0 ) = acos( SubFloat( cs, 0 ) ); + SubFloat( result, 1 ) = acos( SubFloat( cs, 1 ) ); + SubFloat( result, 2 ) = acos( SubFloat( cs, 2 ) ); + SubFloat( result, 3 ) = acos( SubFloat( cs, 3 ) ); + return result; +} + +// tan^1(a/b) .. ie, pass sin in as a and cos in as b +FORCEINLINE fltx4 ArcTan2SIMD( const fltx4 &a, const fltx4 &b ) +{ + fltx4 result; + SubFloat( result, 0 ) = atan2( SubFloat( a, 0 ), SubFloat( b, 0 ) ); + SubFloat( result, 1 ) = atan2( SubFloat( a, 1 ), SubFloat( b, 1 ) ); + SubFloat( result, 2 ) = atan2( SubFloat( a, 2 ), SubFloat( b, 2 ) ); + SubFloat( result, 3 ) = atan2( SubFloat( a, 3 ), SubFloat( b, 3 ) ); + return result; +} + +FORCEINLINE fltx4 MaxSIMD( const fltx4 & a, const fltx4 & b ) // max(a,b) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = max( SubFloat( a, 0 ), SubFloat( b, 0 ) ); + SubFloat( retVal, 1 ) = max( SubFloat( a, 1 ), SubFloat( b, 1 ) ); + SubFloat( retVal, 2 ) = max( SubFloat( a, 2 ), SubFloat( b, 2 ) ); + SubFloat( retVal, 3 ) = max( SubFloat( a, 3 ), SubFloat( b, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 MinSIMD( const fltx4 & a, const fltx4 & b ) // min(a,b) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = min( SubFloat( a, 0 ), SubFloat( b, 0 ) ); + SubFloat( retVal, 1 ) = min( SubFloat( a, 1 ), SubFloat( b, 1 ) ); + SubFloat( retVal, 2 ) = min( SubFloat( a, 2 ), SubFloat( b, 2 ) ); + SubFloat( retVal, 3 ) = min( SubFloat( a, 3 ), SubFloat( b, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 AndSIMD( const fltx4 & a, const fltx4 & b ) // a & b +{ + IBINOP(&); +} + +FORCEINLINE fltx4 AndNotSIMD( const fltx4 & a, const fltx4 & b ) // ~a & b +{ + fltx4 retVal; + SubInt( retVal, 0 ) = ~SubInt( a, 0 ) & SubInt( b, 0 ); + SubInt( retVal, 1 ) = ~SubInt( a, 1 ) & SubInt( b, 1 ); + SubInt( retVal, 2 ) = ~SubInt( a, 2 ) & SubInt( b, 2 ); + SubInt( retVal, 3 ) = ~SubInt( a, 3 ) & SubInt( b, 3 ); + return retVal; +} + +FORCEINLINE fltx4 XorSIMD( const fltx4 & a, const fltx4 & b ) // a ^ b +{ + IBINOP(^); +} + +FORCEINLINE fltx4 OrSIMD( const fltx4 & a, const fltx4 & b ) // a | b +{ + IBINOP(|); +} + +FORCEINLINE fltx4 NegSIMD(const fltx4 &a) // negate: -a +{ + fltx4 retval; + SubFloat( retval, 0 ) = -SubFloat( a, 0 ); + SubFloat( retval, 1 ) = -SubFloat( a, 1 ); + SubFloat( retval, 2 ) = -SubFloat( a, 2 ); + SubFloat( retval, 3 ) = -SubFloat( a, 3 ); + + return retval; +} + +FORCEINLINE bool IsAllZeros( const fltx4 & a ) // all floats of a zero? +{ + return ( SubFloat( a, 0 ) == 0.0 ) && + ( SubFloat( a, 1 ) == 0.0 ) && + ( SubFloat( a, 2 ) == 0.0 ) && + ( SubFloat( a, 3 ) == 0.0 ) ; +} + + +// for branching when a.xyzw > b.xyzw +FORCEINLINE bool IsAllGreaterThan( const fltx4 &a, const fltx4 &b ) +{ + return SubFloat(a,0) > SubFloat(b,0) && + SubFloat(a,1) > SubFloat(b,1) && + SubFloat(a,2) > SubFloat(b,2) && + SubFloat(a,3) > SubFloat(b,3); +} + +// for branching when a.xyzw >= b.xyzw +FORCEINLINE bool IsAllGreaterThanOrEq( const fltx4 &a, const fltx4 &b ) +{ + return SubFloat(a,0) >= SubFloat(b,0) && + SubFloat(a,1) >= SubFloat(b,1) && + SubFloat(a,2) >= SubFloat(b,2) && + SubFloat(a,3) >= SubFloat(b,3); +} + +// For branching if all a.xyzw == b.xyzw +FORCEINLINE bool IsAllEqual( const fltx4 & a, const fltx4 & b ) +{ + return SubFloat(a,0) == SubFloat(b,0) && + SubFloat(a,1) == SubFloat(b,1) && + SubFloat(a,2) == SubFloat(b,2) && + SubFloat(a,3) == SubFloat(b,3); +} + +// For branching if a.x == b.x || a.y == b.y || a.z == b.z || a.w == b.w +FORCEINLINE bool IsAnyEqual( const fltx4 & a, const fltx4 & b ) +{ + return SubFloat(a,0) == SubFloat(b,0) || + SubFloat(a,1) == SubFloat(b,1) || + SubFloat(a,2) == SubFloat(b,2) || + SubFloat(a,3) == SubFloat(b,3); +} + +FORCEINLINE int TestSignSIMD( const fltx4 & a ) // mask of which floats have the high bit set +{ + int nRet = 0; + + nRet |= ( SubInt( a, 0 ) & 0x80000000 ) >> 31; // sign(x) -> bit 0 + nRet |= ( SubInt( a, 1 ) & 0x80000000 ) >> 30; // sign(y) -> bit 1 + nRet |= ( SubInt( a, 2 ) & 0x80000000 ) >> 29; // sign(z) -> bit 2 + nRet |= ( SubInt( a, 3 ) & 0x80000000 ) >> 28; // sign(w) -> bit 3 + + return nRet; +} + +FORCEINLINE bool IsAnyNegative( const fltx4 & a ) // (a.x < 0) || (a.y < 0) || (a.z < 0) || (a.w < 0) +{ + return (0 != TestSignSIMD( a )); +} + +FORCEINLINE fltx4 CmpEqSIMD( const fltx4 & a, const fltx4 & b ) // (a==b) ? ~0:0 +{ + fltx4 retVal; + SubInt( retVal, 0 ) = ( SubFloat( a, 0 ) == SubFloat( b, 0 )) ? ~0 : 0; + SubInt( retVal, 1 ) = ( SubFloat( a, 1 ) == SubFloat( b, 1 )) ? ~0 : 0; + SubInt( retVal, 2 ) = ( SubFloat( a, 2 ) == SubFloat( b, 2 )) ? ~0 : 0; + SubInt( retVal, 3 ) = ( SubFloat( a, 3 ) == SubFloat( b, 3 )) ? ~0 : 0; + return retVal; +} + +FORCEINLINE fltx4 CmpGtSIMD( const fltx4 & a, const fltx4 & b ) // (a>b) ? ~0:0 +{ + fltx4 retVal; + SubInt( retVal, 0 ) = ( SubFloat( a, 0 ) > SubFloat( b, 0 )) ? ~0 : 0; + SubInt( retVal, 1 ) = ( SubFloat( a, 1 ) > SubFloat( b, 1 )) ? ~0 : 0; + SubInt( retVal, 2 ) = ( SubFloat( a, 2 ) > SubFloat( b, 2 )) ? ~0 : 0; + SubInt( retVal, 3 ) = ( SubFloat( a, 3 ) > SubFloat( b, 3 )) ? ~0 : 0; + return retVal; +} + +FORCEINLINE fltx4 CmpGeSIMD( const fltx4 & a, const fltx4 & b ) // (a>=b) ? ~0:0 +{ + fltx4 retVal; + SubInt( retVal, 0 ) = ( SubFloat( a, 0 ) >= SubFloat( b, 0 )) ? ~0 : 0; + SubInt( retVal, 1 ) = ( SubFloat( a, 1 ) >= SubFloat( b, 1 )) ? ~0 : 0; + SubInt( retVal, 2 ) = ( SubFloat( a, 2 ) >= SubFloat( b, 2 )) ? ~0 : 0; + SubInt( retVal, 3 ) = ( SubFloat( a, 3 ) >= SubFloat( b, 3 )) ? ~0 : 0; + return retVal; +} + +FORCEINLINE fltx4 CmpLtSIMD( const fltx4 & a, const fltx4 & b ) // (a= -b) ? ~0 : 0 +{ + fltx4 retVal; + SubInt( retVal, 0 ) = ( SubFloat( a, 0 ) <= SubFloat( b, 0 ) && SubFloat( a, 0 ) >= -SubFloat( b, 0 ) ) ? ~0 : 0; + SubInt( retVal, 1 ) = ( SubFloat( a, 1 ) <= SubFloat( b, 1 ) && SubFloat( a, 1 ) >= -SubFloat( b, 1 ) ) ? ~0 : 0; + SubInt( retVal, 2 ) = ( SubFloat( a, 2 ) <= SubFloat( b, 2 ) && SubFloat( a, 2 ) >= -SubFloat( b, 2 ) ) ? ~0 : 0; + SubInt( retVal, 3 ) = ( SubFloat( a, 3 ) <= SubFloat( b, 3 ) && SubFloat( a, 3 ) >= -SubFloat( b, 3 ) ) ? ~0 : 0; + return retVal; +} + + +FORCEINLINE fltx4 MaskedAssign( const fltx4 & ReplacementMask, const fltx4 & NewValue, const fltx4 & OldValue ) +{ + return OrSIMD( + AndSIMD( ReplacementMask, NewValue ), + AndNotSIMD( ReplacementMask, OldValue ) ); +} + +FORCEINLINE fltx4 ReplicateX4( float flValue ) // a,a,a,a +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = flValue; + SubFloat( retVal, 1 ) = flValue; + SubFloat( retVal, 2 ) = flValue; + SubFloat( retVal, 3 ) = flValue; + return retVal; +} + +/// replicate a single 32 bit integer value to all 4 components of an m128 +FORCEINLINE fltx4 ReplicateIX4( int nValue ) +{ + fltx4 retVal; + SubInt( retVal, 0 ) = nValue; + SubInt( retVal, 1 ) = nValue; + SubInt( retVal, 2 ) = nValue; + SubInt( retVal, 3 ) = nValue; + return retVal; + +} + +// Round towards positive infinity +FORCEINLINE fltx4 CeilSIMD( const fltx4 &a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = ceil( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = ceil( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = ceil( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = ceil( SubFloat( a, 3 ) ); + return retVal; + +} + +// Round towards negative infinity +FORCEINLINE fltx4 FloorSIMD( const fltx4 &a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = floor( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = floor( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = floor( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = floor( SubFloat( a, 3 ) ); + return retVal; + +} + +FORCEINLINE fltx4 SqrtEstSIMD( const fltx4 & a ) // sqrt(a), more or less +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = sqrt( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = sqrt( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = sqrt( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = sqrt( SubFloat( a, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 SqrtSIMD( const fltx4 & a ) // sqrt(a) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = sqrt( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = sqrt( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = sqrt( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = sqrt( SubFloat( a, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSIMD( const fltx4 & a ) // 1/sqrt(a), more or less +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / sqrt( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = 1.0 / sqrt( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = 1.0 / sqrt( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = 1.0 / sqrt( SubFloat( a, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSaturateSIMD( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / sqrt( SubFloat( a, 0 ) != 0.0f ? SubFloat( a, 0 ) : FLT_EPSILON ); + SubFloat( retVal, 1 ) = 1.0 / sqrt( SubFloat( a, 1 ) != 0.0f ? SubFloat( a, 1 ) : FLT_EPSILON ); + SubFloat( retVal, 2 ) = 1.0 / sqrt( SubFloat( a, 2 ) != 0.0f ? SubFloat( a, 2 ) : FLT_EPSILON ); + SubFloat( retVal, 3 ) = 1.0 / sqrt( SubFloat( a, 3 ) != 0.0f ? SubFloat( a, 3 ) : FLT_EPSILON ); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalSqrtSIMD( const fltx4 & a ) // 1/sqrt(a) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / sqrt( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = 1.0 / sqrt( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = 1.0 / sqrt( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = 1.0 / sqrt( SubFloat( a, 3 ) ); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalEstSIMD( const fltx4 & a ) // 1/a, more or less +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / SubFloat( a, 0 ); + SubFloat( retVal, 1 ) = 1.0 / SubFloat( a, 1 ); + SubFloat( retVal, 2 ) = 1.0 / SubFloat( a, 2 ); + SubFloat( retVal, 3 ) = 1.0 / SubFloat( a, 3 ); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalSIMD( const fltx4 & a ) // 1/a +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / SubFloat( a, 0 ); + SubFloat( retVal, 1 ) = 1.0 / SubFloat( a, 1 ); + SubFloat( retVal, 2 ) = 1.0 / SubFloat( a, 2 ); + SubFloat( retVal, 3 ) = 1.0 / SubFloat( a, 3 ); + return retVal; +} + +/// 1/x for all 4 values. +/// 1/0 will result in a big but NOT infinite result +FORCEINLINE fltx4 ReciprocalEstSaturateSIMD( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / (SubFloat( a, 0 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 0 )); + SubFloat( retVal, 1 ) = 1.0 / (SubFloat( a, 1 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 1 )); + SubFloat( retVal, 2 ) = 1.0 / (SubFloat( a, 2 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 2 )); + SubFloat( retVal, 3 ) = 1.0 / (SubFloat( a, 3 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 3 )); + return retVal; +} + +FORCEINLINE fltx4 ReciprocalSaturateSIMD( const fltx4 & a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = 1.0 / (SubFloat( a, 0 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 0 )); + SubFloat( retVal, 1 ) = 1.0 / (SubFloat( a, 1 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 1 )); + SubFloat( retVal, 2 ) = 1.0 / (SubFloat( a, 2 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 2 )); + SubFloat( retVal, 3 ) = 1.0 / (SubFloat( a, 3 ) == 0.0f ? FLT_EPSILON : SubFloat( a, 3 )); + return retVal; +} + +// 2^x for all values (the antilog) +FORCEINLINE fltx4 ExpSIMD( const fltx4 &toPower ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = powf( 2, SubFloat(toPower, 0) ); + SubFloat( retVal, 1 ) = powf( 2, SubFloat(toPower, 1) ); + SubFloat( retVal, 2 ) = powf( 2, SubFloat(toPower, 2) ); + SubFloat( retVal, 3 ) = powf( 2, SubFloat(toPower, 3) ); + + return retVal; +} + +FORCEINLINE fltx4 Dot3SIMD( const fltx4 &a, const fltx4 &b ) +{ + float flDot = SubFloat( a, 0 ) * SubFloat( b, 0 ) + + SubFloat( a, 1 ) * SubFloat( b, 1 ) + + SubFloat( a, 2 ) * SubFloat( b, 2 ); + return ReplicateX4( flDot ); +} + +FORCEINLINE fltx4 Dot4SIMD( const fltx4 &a, const fltx4 &b ) +{ + float flDot = SubFloat( a, 0 ) * SubFloat( b, 0 ) + + SubFloat( a, 1 ) * SubFloat( b, 1 ) + + SubFloat( a, 2 ) * SubFloat( b, 2 ) + + SubFloat( a, 3 ) * SubFloat( b, 3 ); + return ReplicateX4( flDot ); +} + +// Clamps the components of a vector to a specified minimum and maximum range. +FORCEINLINE fltx4 ClampVectorSIMD( FLTX4 in, FLTX4 min, FLTX4 max) +{ + return MaxSIMD( min, MinSIMD( max, in ) ); +} + +// Squelch the w component of a vector to +0.0. +// Most efficient when you say a = SetWToZeroSIMD(a) (avoids a copy) +FORCEINLINE fltx4 SetWToZeroSIMD( const fltx4 & a ) +{ + fltx4 retval; + retval = a; + SubFloat( retval, 0 ) = 0; + return retval; +} + +FORCEINLINE fltx4 LoadUnalignedSIMD( const void *pSIMD ) +{ + return *( reinterpret_cast< const fltx4 *> ( pSIMD ) ); +} + +FORCEINLINE fltx4 LoadUnaligned3SIMD( const void *pSIMD ) +{ + return *( reinterpret_cast< const fltx4 *> ( pSIMD ) ); +} + +// load a single unaligned float into the x component of a SIMD word +FORCEINLINE fltx4 LoadUnalignedFloatSIMD( const float *pFlt ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = *pFlt; + return retval; +} + +FORCEINLINE fltx4 LoadAlignedSIMD( const void *pSIMD ) +{ + return *( reinterpret_cast< const fltx4 *> ( pSIMD ) ); +} + +// for the transitional class -- load a 3-by VectorAligned and squash its w component +FORCEINLINE fltx4 LoadAlignedSIMD( const VectorAligned & pSIMD ) +{ + fltx4 retval = LoadAlignedSIMD(pSIMD.Base()); + // squelch w + SubInt( retval, 3 ) = 0; + return retval; +} + +FORCEINLINE void StoreAlignedSIMD( float *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< fltx4 *> ( pSIMD ) ) = a; +} + +FORCEINLINE void StoreUnalignedSIMD( float *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< fltx4 *> ( pSIMD ) ) = a; +} + +FORCEINLINE void StoreUnaligned3SIMD( float *pSIMD, const fltx4 & a ) +{ + *pSIMD = SubFloat(a, 0); + *(pSIMD+1) = SubFloat(a, 1); + *(pSIMD+2) = SubFloat(a, 2); +} + +// strongly typed -- syntactic castor oil used for typechecking as we transition to SIMD +FORCEINLINE void StoreAligned3SIMD( VectorAligned * RESTRICT pSIMD, const fltx4 & a ) +{ + StoreAlignedSIMD(pSIMD->Base(),a); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination[0], pDestination[1], pDestination[2], pDestination[3] +// The Vectors are assumed to be unaligned. +FORCEINLINE void StoreFourUnalignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + StoreUnaligned3SIMD( pDestination->Base(), a ); + StoreUnaligned3SIMD( (pDestination+1)->Base(), b ); + StoreUnaligned3SIMD( (pDestination+2)->Base(), c ); + StoreUnaligned3SIMD( (pDestination+3)->Base(), d ); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination , pDestination + 1, pDestination + 2, pDestination + 3 +// The Vectors are assumed to start on an ALIGNED address, that is, +// pDestination is 16-byte aligned (thhough obviously pDestination+1 is not). +FORCEINLINE void StoreFourAlignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + StoreUnaligned3SIMD( pDestination->Base(), a ); + StoreUnaligned3SIMD( (pDestination+1)->Base(), b ); + StoreUnaligned3SIMD( (pDestination+2)->Base(), c ); + StoreUnaligned3SIMD( (pDestination+3)->Base(), d ); +} + + +FORCEINLINE void TransposeSIMD( fltx4 & x, fltx4 & y, fltx4 & z, fltx4 & w ) +{ +#define SWAP_FLOATS( _a_, _ia_, _b_, _ib_ ) { float tmp = SubFloat( _a_, _ia_ ); SubFloat( _a_, _ia_ ) = SubFloat( _b_, _ib_ ); SubFloat( _b_, _ib_ ) = tmp; } + SWAP_FLOATS( x, 1, y, 0 ); + SWAP_FLOATS( x, 2, z, 0 ); + SWAP_FLOATS( x, 3, w, 0 ); + SWAP_FLOATS( y, 2, z, 1 ); + SWAP_FLOATS( y, 3, w, 1 ); + SWAP_FLOATS( z, 3, w, 2 ); +} + +// find the lowest component of a.x, a.y, a.z, +// and replicate it to the whole return value. +FORCEINLINE fltx4 FindLowestSIMD3( const fltx4 & a ) +{ + float lowest = min( min( SubFloat(a, 0), SubFloat(a, 1) ), SubFloat(a, 2)); + return ReplicateX4(lowest); +} + +// find the highest component of a.x, a.y, a.z, +// and replicate it to the whole return value. +FORCEINLINE fltx4 FindHighestSIMD3( const fltx4 & a ) +{ + float highest = max( max( SubFloat(a, 0), SubFloat(a, 1) ), SubFloat(a, 2)); + return ReplicateX4(highest); +} + +// Fixed-point conversion and save as SIGNED INTS. +// pDest->x = Int (vSrc.x) +// note: some architectures have means of doing +// fixed point conversion when the fix depth is +// specified as an immediate.. but there is no way +// to guarantee an immediate as a parameter to function +// like this. +FORCEINLINE void ConvertStoreAsIntsSIMD(intx4 * RESTRICT pDest, const fltx4 &vSrc) +{ + (*pDest)[0] = SubFloat(vSrc, 0); + (*pDest)[1] = SubFloat(vSrc, 1); + (*pDest)[2] = SubFloat(vSrc, 2); + (*pDest)[3] = SubFloat(vSrc, 3); +} + +// ------------------------------------ +// INTEGER SIMD OPERATIONS. +// ------------------------------------ +// splat all components of a vector to a signed immediate int number. +FORCEINLINE fltx4 IntSetImmediateSIMD( int nValue ) +{ + fltx4 retval; + SubInt( retval, 0 ) = SubInt( retval, 1 ) = SubInt( retval, 2 ) = SubInt( retval, 3) = nValue; + return retval; +} + +// Load 4 aligned words into a SIMD register +FORCEINLINE i32x4 LoadAlignedIntSIMD(const void * RESTRICT pSIMD) +{ + return *( reinterpret_cast< const i32x4 *> ( pSIMD ) ); +} + +// Load 4 unaligned words into a SIMD register +FORCEINLINE i32x4 LoadUnalignedIntSIMD( const void * RESTRICT pSIMD) +{ + return *( reinterpret_cast< const i32x4 *> ( pSIMD ) ); +} + +// save into four words, 16-byte aligned +FORCEINLINE void StoreAlignedIntSIMD( int32 *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< i32x4 *> ( pSIMD ) ) = a; +} + +FORCEINLINE void StoreAlignedIntSIMD( intx4 &pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< i32x4 *> ( pSIMD.Base() ) ) = a; +} + +FORCEINLINE void StoreUnalignedIntSIMD( int32 *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< i32x4 *> ( pSIMD ) ) = a; +} + +// Load four consecutive uint16's, and turn them into floating point numbers. +// This function isn't especially fast and could be made faster if anyone is +// using it heavily. +FORCEINLINE fltx4 LoadAndConvertUint16SIMD( const uint16 *pInts ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = pInts[0]; + SubFloat( retval, 1 ) = pInts[1]; + SubFloat( retval, 2 ) = pInts[2]; + SubFloat( retval, 3 ) = pInts[3]; +} + + +// Take a fltx4 containing fixed-point uints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 UnsignedIntConvertToFltSIMD( const u32x4 &vSrcA ) +{ + Assert(0); /* pc has no such operation */ + fltx4 retval; + SubFloat( retval, 0 ) = ( (float) SubInt( vSrcA, 0 ) ); + SubFloat( retval, 1 ) = ( (float) SubInt( vSrcA, 1 ) ); + SubFloat( retval, 2 ) = ( (float) SubInt( vSrcA, 2 ) ); + SubFloat( retval, 3 ) = ( (float) SubInt( vSrcA, 3 ) ); + return retval; +} + + +#if 0 /* pc has no such op */ +// Take a fltx4 containing fixed-point sints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 SignedIntConvertToFltSIMD( const i32x4 &vSrcA ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = ( (float) (reinterpret_cast(&vSrcA.m128_s32[0])) ); + SubFloat( retval, 1 ) = ( (float) (reinterpret_cast(&vSrcA.m128_s32[1])) ); + SubFloat( retval, 2 ) = ( (float) (reinterpret_cast(&vSrcA.m128_s32[2])) ); + SubFloat( retval, 3 ) = ( (float) (reinterpret_cast(&vSrcA.m128_s32[3])) ); + return retval; +} + + +/* + works on fltx4's as if they are four uints. + the first parameter contains the words to be shifted, + the second contains the amount to shift by AS INTS + + for i = 0 to 3 + shift = vSrcB_i*32:(i*32)+4 + vReturned_i*32:(i*32)+31 = vSrcA_i*32:(i*32)+31 << shift +*/ +FORCEINLINE i32x4 IntShiftLeftWordSIMD(const i32x4 &vSrcA, const i32x4 &vSrcB) +{ + i32x4 retval; + SubInt(retval, 0) = SubInt(vSrcA, 0) << SubInt(vSrcB, 0); + SubInt(retval, 1) = SubInt(vSrcA, 1) << SubInt(vSrcB, 1); + SubInt(retval, 2) = SubInt(vSrcA, 2) << SubInt(vSrcB, 2); + SubInt(retval, 3) = SubInt(vSrcA, 3) << SubInt(vSrcB, 3); + + + return retval; +} +#endif + +#elif ( defined( _X360 ) ) + +//--------------------------------------------------------------------- +// X360 implementation +//--------------------------------------------------------------------- + +FORCEINLINE float & FloatSIMD( fltx4 & a, int idx ) +{ + fltx4_union & a_union = (fltx4_union &)a; + return a_union.m128_f32[idx]; +} + +FORCEINLINE unsigned int & UIntSIMD( fltx4 & a, int idx ) +{ + fltx4_union & a_union = (fltx4_union &)a; + return a_union.m128_u32[idx]; +} + +FORCEINLINE fltx4 AddSIMD( const fltx4 & a, const fltx4 & b ) +{ + return __vaddfp( a, b ); +} + +FORCEINLINE fltx4 SubSIMD( const fltx4 & a, const fltx4 & b ) // a-b +{ + return __vsubfp( a, b ); +} + +FORCEINLINE fltx4 MulSIMD( const fltx4 & a, const fltx4 & b ) // a*b +{ + return __vmulfp( a, b ); +} + +FORCEINLINE fltx4 MaddSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // a*b + c +{ + return __vmaddfp( a, b, c ); +} + +FORCEINLINE fltx4 MsubSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // c - a*b +{ + return __vnmsubfp( a, b, c ); +}; + +FORCEINLINE fltx4 Dot3SIMD( const fltx4 &a, const fltx4 &b ) +{ + return __vmsum3fp( a, b ); +} + +FORCEINLINE fltx4 Dot4SIMD( const fltx4 &a, const fltx4 &b ) +{ + return __vmsum4fp( a, b ); +} + +FORCEINLINE fltx4 SinSIMD( const fltx4 &radians ) +{ + return XMVectorSin( radians ); +} + +FORCEINLINE void SinCos3SIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) +{ + XMVectorSinCos( &sine, &cosine, radians ); +} + +FORCEINLINE void SinCosSIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) +{ + XMVectorSinCos( &sine, &cosine, radians ); +} + +FORCEINLINE void CosSIMD( fltx4 &cosine, const fltx4 &radians ) +{ + cosine = XMVectorCos( radians ); +} + +FORCEINLINE fltx4 ArcSinSIMD( const fltx4 &sine ) +{ + return XMVectorASin( sine ); +} + +FORCEINLINE fltx4 ArcCosSIMD( const fltx4 &cs ) +{ + return XMVectorACos( cs ); +} + +// tan^1(a/b) .. ie, pass sin in as a and cos in as b +FORCEINLINE fltx4 ArcTan2SIMD( const fltx4 &a, const fltx4 &b ) +{ + return XMVectorATan2( a, b ); +} + +// DivSIMD defined further down, since it uses ReciprocalSIMD + +FORCEINLINE fltx4 MaxSIMD( const fltx4 & a, const fltx4 & b ) // max(a,b) +{ + return __vmaxfp( a, b ); +} + +FORCEINLINE fltx4 MinSIMD( const fltx4 & a, const fltx4 & b ) // min(a,b) +{ + return __vminfp( a, b ); +} + +FORCEINLINE fltx4 AndSIMD( const fltx4 & a, const fltx4 & b ) // a & b +{ + return __vand( a, b ); +} + +FORCEINLINE fltx4 AndNotSIMD( const fltx4 & a, const fltx4 & b ) // ~a & b +{ + // NOTE: a and b are swapped in the call: SSE complements the first argument, VMX the second + return __vandc( b, a ); +} + +FORCEINLINE fltx4 XorSIMD( const fltx4 & a, const fltx4 & b ) // a ^ b +{ + return __vxor( a, b ); +} + +FORCEINLINE fltx4 OrSIMD( const fltx4 & a, const fltx4 & b ) // a | b +{ + return __vor( a, b ); +} + +FORCEINLINE fltx4 NegSIMD(const fltx4 &a) // negate: -a +{ + return XMVectorNegate(a); +} + +FORCEINLINE bool IsAllZeros( const fltx4 & a ) // all floats of a zero? +{ + unsigned int equalFlags = 0; + __vcmpeqfpR( a, Four_Zeros, &equalFlags ); + return XMComparisonAllTrue( equalFlags ); +} + +FORCEINLINE bool IsAnyZeros( const fltx4 & a ) // any floats are zero? +{ + unsigned int conditionregister; + XMVectorEqualR(&conditionregister, a, XMVectorZero()); + return XMComparisonAnyTrue(conditionregister); +} + +FORCEINLINE bool IsAnyXYZZero( const fltx4 &a ) // are any of x,y,z zero? +{ + // copy a's x component into w, in case w was zero. + fltx4 temp = __vrlimi(a, a, 1, 1); + unsigned int conditionregister; + XMVectorEqualR(&conditionregister, temp, XMVectorZero()); + return XMComparisonAnyTrue(conditionregister); +} + +// for branching when a.xyzw > b.xyzw +FORCEINLINE bool IsAllGreaterThan( const fltx4 &a, const fltx4 &b ) +{ + unsigned int cr; + XMVectorGreaterR(&cr,a,b); + return XMComparisonAllTrue(cr); +} + +// for branching when a.xyzw >= b.xyzw +FORCEINLINE bool IsAllGreaterThanOrEq( const fltx4 &a, const fltx4 &b ) +{ + unsigned int cr; + XMVectorGreaterOrEqualR(&cr,a,b); + return XMComparisonAllTrue(cr); +} + +// for branching when a.xyzw > b.xyzw +FORCEINLINE bool IsAnyGreaterThan( const fltx4 &a, const fltx4 &b ) +{ + unsigned int cr; + XMVectorGreaterR(&cr,a,b); + return XMComparisonAnyTrue(cr); +} + +// for branching when a.xyzw >= b.xyzw +FORCEINLINE bool IsAnyGreaterThanOrEq( const fltx4 &a, const fltx4 &b ) +{ + unsigned int cr; + XMVectorGreaterOrEqualR(&cr,a,b); + return XMComparisonAnyTrue(cr); +} + +// For branching if all a.xyzw == b.xyzw +FORCEINLINE bool IsAllEqual( const fltx4 & a, const fltx4 & b ) +{ + unsigned int cr; + XMVectorEqualR(&cr,a,b); + return XMComparisonAllTrue(cr); +} + + +FORCEINLINE int TestSignSIMD( const fltx4 & a ) // mask of which floats have the high bit set +{ + // NOTE: this maps to SSE way better than it does to VMX (most code uses IsAnyNegative(), though) + int nRet = 0; + + const fltx4_union & a_union = (const fltx4_union &)a; + nRet |= ( a_union.m128_u32[0] & 0x80000000 ) >> 31; // sign(x) -> bit 0 + nRet |= ( a_union.m128_u32[1] & 0x80000000 ) >> 30; // sign(y) -> bit 1 + nRet |= ( a_union.m128_u32[2] & 0x80000000 ) >> 29; // sign(z) -> bit 2 + nRet |= ( a_union.m128_u32[3] & 0x80000000 ) >> 28; // sign(w) -> bit 3 + + return nRet; +} + +// Squelch the w component of a vector to +0.0. +// Most efficient when you say a = SetWToZeroSIMD(a) (avoids a copy) +FORCEINLINE fltx4 SetWToZeroSIMD( const fltx4 & a ) +{ + return __vrlimi( a, __vzero(), 1, 0 ); +} + +FORCEINLINE bool IsAnyNegative( const fltx4 & a ) // (a.x < 0) || (a.y < 0) || (a.z < 0) || (a.w < 0) +{ + // NOTE: this tests the top bits of each vector element using integer math + // (so it ignores NaNs - it will return true for "-NaN") + unsigned int equalFlags = 0; + fltx4 signMask = __vspltisw( -1 ); // 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF 0xFFFFFFFF (low order 5 bits of each element = 31) + signMask = __vslw( signMask, signMask ); // 0x80000000 0x80000000 0x80000000 0x80000000 + __vcmpequwR( Four_Zeros, __vand( signMask, a ), &equalFlags ); + return !XMComparisonAllTrue( equalFlags ); +} + +FORCEINLINE fltx4 CmpEqSIMD( const fltx4 & a, const fltx4 & b ) // (a==b) ? ~0:0 +{ + return __vcmpeqfp( a, b ); +} + + +FORCEINLINE fltx4 CmpGtSIMD( const fltx4 & a, const fltx4 & b ) // (a>b) ? ~0:0 +{ + return __vcmpgtfp( a, b ); +} + +FORCEINLINE fltx4 CmpGeSIMD( const fltx4 & a, const fltx4 & b ) // (a>=b) ? ~0:0 +{ + return __vcmpgefp( a, b ); +} + +FORCEINLINE fltx4 CmpLtSIMD( const fltx4 & a, const fltx4 & b ) // (a= -b) ? ~0 : 0 +{ + return XMVectorInBounds( a, b ); +} + +// returned[i] = ReplacementMask[i] == 0 ? OldValue : NewValue +FORCEINLINE fltx4 MaskedAssign( const fltx4 & ReplacementMask, const fltx4 & NewValue, const fltx4 & OldValue ) +{ + return __vsel( OldValue, NewValue, ReplacementMask ); +} + +// AKA "Broadcast", "Splat" +FORCEINLINE fltx4 ReplicateX4( float flValue ) // a,a,a,a +{ + // NOTE: if flValue comes from a register, this causes a Load-Hit-Store stall (don't mix fpu/vpu math!) + float * pValue = &flValue; + Assert( pValue ); + Assert( ((unsigned int)pValue & 3) == 0); + return __vspltw( __lvlx( pValue, 0 ), 0 ); +} + +FORCEINLINE fltx4 ReplicateX4( const float *pValue ) // a,a,a,a +{ + Assert( pValue ); + return __vspltw( __lvlx( pValue, 0 ), 0 ); +} + +/// replicate a single 32 bit integer value to all 4 components of an m128 +FORCEINLINE fltx4 ReplicateIX4( int nValue ) +{ + // NOTE: if nValue comes from a register, this causes a Load-Hit-Store stall (should not mix ints with fltx4s!) + int * pValue = &nValue; + Assert( pValue ); + Assert( ((unsigned int)pValue & 3) == 0); + return __vspltw( __lvlx( pValue, 0 ), 0 ); +} + +// Round towards positive infinity +FORCEINLINE fltx4 CeilSIMD( const fltx4 &a ) +{ + return __vrfip(a); +} + +// Round towards nearest integer +FORCEINLINE fltx4 RoundSIMD( const fltx4 &a ) +{ + return __vrfin(a); +} + +// Round towards negative infinity +FORCEINLINE fltx4 FloorSIMD( const fltx4 &a ) +{ + return __vrfim(a); +} + +FORCEINLINE fltx4 SqrtEstSIMD( const fltx4 & a ) // sqrt(a), more or less +{ + // This is emulated from rsqrt + return XMVectorSqrtEst( a ); +} + +FORCEINLINE fltx4 SqrtSIMD( const fltx4 & a ) // sqrt(a) +{ + // This is emulated from rsqrt + return XMVectorSqrt( a ); +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSIMD( const fltx4 & a ) // 1/sqrt(a), more or less +{ + return __vrsqrtefp( a ); +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSaturateSIMD( const fltx4 & a ) +{ + // Convert zeros to epsilons + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 a_safe = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + return ReciprocalSqrtEstSIMD( a_safe ); +} + +FORCEINLINE fltx4 ReciprocalSqrtSIMD( const fltx4 & a ) // 1/sqrt(a) +{ + // This uses Newton-Raphson to improve the HW result + return XMVectorReciprocalSqrt( a ); +} + +FORCEINLINE fltx4 ReciprocalEstSIMD( const fltx4 & a ) // 1/a, more or less +{ + return __vrefp( a ); +} + +/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. +/// No error checking! +FORCEINLINE fltx4 ReciprocalSIMD( const fltx4 & a ) // 1/a +{ + // This uses Newton-Raphson to improve the HW result + return XMVectorReciprocal( a ); +} + +// FIXME: on 360, this is very slow, since it uses ReciprocalSIMD (do we need DivEstSIMD?) +FORCEINLINE fltx4 DivSIMD( const fltx4 & a, const fltx4 & b ) // a/b +{ + return MulSIMD( ReciprocalSIMD( b ), a ); +} + +/// 1/x for all 4 values. +/// 1/0 will result in a big but NOT infinite result +FORCEINLINE fltx4 ReciprocalEstSaturateSIMD( const fltx4 & a ) +{ + // Convert zeros to epsilons + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 a_safe = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + return ReciprocalEstSIMD( a_safe ); +} + +FORCEINLINE fltx4 ReciprocalSaturateSIMD( const fltx4 & a ) +{ + // Convert zeros to epsilons + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 a_safe = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + return ReciprocalSIMD( a_safe ); + + // FIXME: This could be faster (BUT: it doesn't preserve the sign of -0.0, whereas the above does) + // fltx4 zeroMask = CmpEqSIMD( Four_Zeros, a ); + // fltx4 a_safe = XMVectorSelect( a, Four_Epsilons, zeroMask ); + // return ReciprocalSIMD( a_safe ); +} + +// CHRISG: is it worth doing integer bitfiddling for this? +// 2^x for all values (the antilog) +FORCEINLINE fltx4 ExpSIMD( const fltx4 &toPower ) +{ + return XMVectorExp(toPower); +} + +// Clamps the components of a vector to a specified minimum and maximum range. +FORCEINLINE fltx4 ClampVectorSIMD( FLTX4 in, FLTX4 min, FLTX4 max) +{ + return XMVectorClamp(in, min, max); +} + +FORCEINLINE fltx4 LoadUnalignedSIMD( const void *pSIMD ) +{ + return XMLoadVector4( pSIMD ); +} + +// load a 3-vector (as opposed to LoadUnalignedSIMD, which loads a 4-vec). +FORCEINLINE fltx4 LoadUnaligned3SIMD( const void *pSIMD ) +{ + return XMLoadVector3( pSIMD ); +} + +// load a single unaligned float into the x component of a SIMD word +FORCEINLINE fltx4 LoadUnalignedFloatSIMD( const float *pFlt ) +{ + return __lvlx( pFlt, 0 ); +} + +FORCEINLINE fltx4 LoadAlignedSIMD( const void *pSIMD ) +{ + return *( reinterpret_cast< const fltx4 *> ( pSIMD ) ); +} + +// for the transitional class -- load a 3-by VectorAligned and squash its w component +FORCEINLINE fltx4 LoadAlignedSIMD( const VectorAligned & pSIMD ) +{ + fltx4 out = XMLoadVector3A(pSIMD.Base()); + // squelch w + return __vrlimi( out, __vzero(), 1, 0 ); +} + +// for the transitional class -- load a 3-by VectorAligned and squash its w component +FORCEINLINE fltx4 LoadAlignedSIMD( const VectorAligned * RESTRICT pSIMD ) +{ + fltx4 out = XMLoadVector3A(pSIMD); + // squelch w + return __vrlimi( out, __vzero(), 1, 0 ); +} + +FORCEINLINE void StoreAlignedSIMD( float *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< fltx4 *> ( pSIMD ) ) = a; +} + +FORCEINLINE void StoreUnalignedSIMD( float *pSIMD, const fltx4 & a ) +{ + XMStoreVector4( pSIMD, a ); +} + +FORCEINLINE void StoreUnaligned3SIMD( float *pSIMD, const fltx4 & a ) +{ + XMStoreVector3( pSIMD, a ); +} + + +// strongly typed -- for typechecking as we transition to SIMD +FORCEINLINE void StoreAligned3SIMD( VectorAligned * RESTRICT pSIMD, const fltx4 & a ) +{ + XMStoreVector3A(pSIMD->Base(),a); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination[0], pDestination[1], pDestination[2], pDestination[3] +// The Vectors are assumed to be unaligned. +FORCEINLINE void StoreFourUnalignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + // since four Vec3s == 48 bytes, we can use full-vector stores here, so long as + // we arrange the data properly first. + // The vrlimi ops trash the destination param which is why we require + // pass-by-copy. I'm counting on the compiler to schedule these properly. + b = __vrlimi( b, b, 15, 1 ); // b = y1z1__x1 + c = __vrlimi( c, c, 15, 2 ); // c = z2__x2y2 + + a = __vrlimi( a, b, 1, 0 ); // a = x0y0z0x1 + b = __vrlimi( b, c, 2|1, 0 ); // b = y1z1x2y2 + c = __vrlimi( c, d, 4|2|1, 3 ); // c = z2x3y3z3 + + float *RESTRICT pOut = pDestination->Base(); + StoreUnalignedSIMD( pOut + 0, a ); + StoreUnalignedSIMD( pOut + 4, b ); + StoreUnalignedSIMD( pOut + 8, c ); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination , pDestination + 1, pDestination + 2, pDestination + 3 +// The Vectors are assumed to start on an ALIGNED address, that is, +// pDestination is 16-byte aligned (thhough obviously pDestination+1 is not). +FORCEINLINE void StoreFourAlignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + // since four Vec3s == 48 bytes, we can use full-vector stores here, so long as + // we arrange the data properly first. + // The vrlimi ops trash the destination param which is why we require + // pass-by-copy. I'm counting on the compiler to schedule these properly. + b = __vrlimi( b, b, 15, 1 ); // b = y1z1__x1 + c = __vrlimi( c, c, 15, 2 ); // c = z2__x2y2 + + a = __vrlimi( a, b, 1, 0 ); // a = x0y0z0x1 + b = __vrlimi( b, c, 2|1, 0 ); // b = y1z1x2y2 + c = __vrlimi( c, d, 4|2|1, 3 ); // c = z2x3y3z3 + + float *RESTRICT pOut = pDestination->Base(); + StoreAlignedSIMD( pOut + 0, a ); + StoreAlignedSIMD( pOut + 4, b ); + StoreAlignedSIMD( pOut + 8, c ); +} + +// Fixed-point conversion and save as SIGNED INTS. +// pDest->x = Int (vSrc.x) +// note: some architectures have means of doing +// fixed point conversion when the fix depth is +// specified as an immediate.. but there is no way +// to guarantee an immediate as a parameter to function +// like this. +FORCEINLINE void ConvertStoreAsIntsSIMD(intx4 * RESTRICT pDest, const fltx4 &vSrc) +{ + fltx4 asInt = __vctsxs( vSrc, 0 ); + XMStoreVector4A(pDest->Base(), asInt); +} + +FORCEINLINE void TransposeSIMD( fltx4 & x, fltx4 & y, fltx4 & z, fltx4 & w ) +{ + XMMATRIX xyzwMatrix = _XMMATRIX( x, y, z, w ); + xyzwMatrix = XMMatrixTranspose( xyzwMatrix ); + x = xyzwMatrix.r[0]; + y = xyzwMatrix.r[1]; + z = xyzwMatrix.r[2]; + w = xyzwMatrix.r[3]; +} + +// Return one in the fastest way -- faster even than loading. +FORCEINLINE fltx4 LoadZeroSIMD( void ) +{ + return XMVectorZero(); +} + +// Return one in the fastest way -- faster even than loading. +FORCEINLINE fltx4 LoadOneSIMD( void ) +{ + return XMVectorSplatOne(); +} + +FORCEINLINE fltx4 SplatXSIMD( fltx4 a ) +{ + return XMVectorSplatX( a ); +} + +FORCEINLINE fltx4 SplatYSIMD( fltx4 a ) +{ + return XMVectorSplatY( a ); +} + +FORCEINLINE fltx4 SplatZSIMD( fltx4 a ) +{ + return XMVectorSplatZ( a ); +} + +FORCEINLINE fltx4 SplatWSIMD( fltx4 a ) +{ + return XMVectorSplatW( a ); +} + +FORCEINLINE fltx4 SetXSIMD( const fltx4& a, const fltx4& x ) +{ + fltx4 result = __vrlimi(a, x, 8, 0); + return result; +} + +FORCEINLINE fltx4 SetYSIMD( const fltx4& a, const fltx4& y ) +{ + fltx4 result = __vrlimi(a, y, 4, 0); + return result; +} + +FORCEINLINE fltx4 SetZSIMD( const fltx4& a, const fltx4& z ) +{ + fltx4 result = __vrlimi(a, z, 2, 0); + return result; +} + +FORCEINLINE fltx4 SetWSIMD( const fltx4& a, const fltx4& w ) +{ + fltx4 result = __vrlimi(a, w, 1, 0); + return result; +} + +FORCEINLINE fltx4 SetComponentSIMD( const fltx4& a, int nComponent, float flValue ) +{ + static int s_nVrlimiMask[4] = { 8, 4, 2, 1 }; + fltx4 val = ReplicateX4( flValue ); + fltx4 result = __vrlimi(a, val, s_nVrlimiMask[nComponent], 0); + return result; +} + +FORCEINLINE fltx4 RotateLeft( const fltx4 & a ) +{ + fltx4 compareOne = a; + return __vrlimi( compareOne, a, 8 | 4 | 2 | 1, 1 ); +} + +FORCEINLINE fltx4 RotateLeft2( const fltx4 & a ) +{ + fltx4 compareOne = a; + return __vrlimi( compareOne, a, 8 | 4 | 2 | 1, 2 ); +} + +FORCEINLINE fltx4 RotateRight( const fltx4 & a ) +{ + fltx4 compareOne = a; + return __vrlimi( compareOne, a, 8 | 4 | 2 | 1, 3 ); +} + +FORCEINLINE fltx4 RotateRight2( const fltx4 & a ) +{ + fltx4 compareOne = a; + return __vrlimi( compareOne, a, 8 | 4 | 2 | 1, 2 ); +} + + +//// Compressed vector formats: unpack Vector48 and Quaternion48 onto SIMD registers. +// Only available on 360 for now because SSE1 lacks the necessary operations. SSE2 could +// do it but we can't count on that yet. +// If you have many v48's or q48's to stream, please note the functions designed to +// work on them many at a time. + +extern const uint16 ALIGN16 g_SIMD_Quat48_Unpack_Shift[]; //< Shuffles the z component of the quat48 left by one bit. +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute0[16]; +extern const fltx4 g_SIMD_Quat48_Unpack_Magic_Constants; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute1[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute2[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute3[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute4[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute5[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute6[16]; +extern const uint8 ALIGN16 g_SIMD_Quat48_Unpack_Permute7[16]; + +// unpack a single vector48 at the pointer into the x,y,z components of a fltx4. +// the w is total garbage. +FORCEINLINE fltx4 UnpackVector48SIMD( const Vector48 *pVec ) +{ + // load the three 16-bit floats into the first 48 bits of ret: + fltx4 ret = XMLoadVector4((const void *)&pVec->x); + // shuffle the top 64 bits of ret down to the least significant (the z,w) -- 16 of those bits are garbage. + ret = __vrlimi( ret, ret, 2 | 1, 2 ); // rotate left by 2 words and insert into z,w components + // now unpack the 16-bit floats into 32-bit floats. This is a hardware op, woohoo! + ret = __vupkd3d( ret , VPACK_FLOAT16_4 ); + + return ret; +} + +// unpack a single Quaternion48 at the pointer into the x,y,z,w components of a fltx4 +FORCEINLINE fltx4 UnpackQuaternion48SIMD( const Quaternion48 * RESTRICT pVec ) +{ + // A quaternion 48 stores the x and y components as 0..65535 , which is almost mapped onto -1.0..1.0 via (x - 32768) / 32768.5 . + // z is stored as 0..32767, which is almost mapped onto -1..1 via (z - 16384) / 16384.5 . + // w is inferred from 1 - the dot product of the other tree components. the top bit of what would otherwise be the 16-bit z is + // w's sign bit. + fltx4 q16s = XMLoadVector3((const void *)pVec); + fltx4 shift = __lvx(&g_SIMD_Quat48_Unpack_Shift, 0); // load the aligned shift mask that we use to shuffle z. + fltx4 permute = __lvx(&g_SIMD_Quat48_Unpack_Permute0, 0); // load the permute word that shuffles x,y,z into their own words + bool wneg = pVec->wneg; // loading pVec into two different kinds of registers -- but not shuffling between (I hope!) so no LHS. + + q16s = __vperm( q16s, Four_Threes, permute ); // permute so that x, y, and z are now each in their own words. The top half is the floating point rep of 3.0f + q16s = __vslh(q16s, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + + // each word of q16s contains 3.0 + n * 2^-22 -- convert this so that we get numbers on the range -1..1 + const fltx4 vUpkMul = SplatXSIMD(g_SIMD_Quat48_Unpack_Magic_Constants); // { UnpackMul16s, UnpackMul16s, UnpackMul16s, UnpackMul16s }; + const fltx4 vUpkAdd = SplatYSIMD(g_SIMD_Quat48_Unpack_Magic_Constants); + + /* + fltx4 ret = __vcfux( q16s, 0 ); // convert from uint16 to floats. + + // scale from 0..65535 to -1..1 : tmp.x = ((int)x - 32768) * (1 / 32768.0); + ret = __vmaddfp( ret, g_SIMD_Quat48_DivByU15, Four_NegativeOnes ); + */ + fltx4 ret = __vmaddfp( q16s, vUpkMul, vUpkAdd ); + + // now, work out what w must be. + fltx4 dotxyz = Dot3SIMD( ret, ret ); // all components are dot product of ret w/ self. + dotxyz = ClampVectorSIMD( dotxyz, Four_Zeros, Four_Ones ); + + fltx4 ww = SubSIMD( Four_Ones, dotxyz ); // all components are 1 - dotxyz + ww = SqrtSIMD(ww); // all components are sqrt(1-dotxyz) + if (wneg) + { + ret = __vrlimi( ret, NegSIMD(ww), 1, 0 ); // insert one element from the ww vector into the w component of ret + } + else + { + ret = __vrlimi( ret, ww, 1, 0 ); // insert one element from the ww vector into the w component of ret + } + return ret; +} + +// Many-at-a-time unpackers. + + +/// Unpack eight consecutive Vector48's in memory onto eight SIMD registers. +/// The Vector48 pointer must be 16-byte aligned. Eight Vector48s add up +/// to 48 bytes long. You should maybe think about prefetching. +FORCEINLINE void UnpackEightVector48SIMD( fltx4 &out1, fltx4 &out2, fltx4 &out3, fltx4 &out4, + fltx4 &out5, fltx4 &out6, fltx4 &out7, fltx4 &out8, + Vector48 * RESTRICT pVecs ) +{ + AssertMsg((reinterpret_cast(pVecs) & 0x0F) == 0, "Input to UnpackEightVector48SIMD is not 16-byte aligned." ); + + // first load the data onto three packed SIMD vectors, which contain eight Vector48s between them. + // I've named them very explicitly so you can follow the movement of the input data. + fltx4 x0y0z0x1y1z1x2y2, z2x3y3z3x4y4z4x5, y5z5x6y6z6x7y7z7; + x0y0z0x1y1z1x2y2 = __lvx( pVecs, 0 ); // load reintrepret_cast(pVecs) + 0 + z2x3y3z3x4y4z4x5 = __lvx( pVecs, 16 ); // load reintrepret_cast(pVecs) + 1 + y5z5x6y6z6x7y7z7 = __lvx( pVecs, 32 ); // load reintrepret_cast(pVecs) + 2 + + // Now, start unpacking. The __vupkd3d operation can turn 16-bit floats into 32-bit floats in a single op! + // It converts the contents of the z and w words of the input fltx4 , so we need to process a word to do + // one half, then rotate it to do the other half. + fltx4 y1z1x2y2 = __vupkd3d( x0y0z0x1y1z1x2y2 , VPACK_FLOAT16_4 ); + x0y0z0x1y1z1x2y2 = __vrlimi( x0y0z0x1y1z1x2y2, x0y0z0x1y1z1x2y2, 0xf, 2 ); // actually y1z1x2y2x0y0z0x1 now. For perf it's important that the first param to vrlimi also be the assignee. + fltx4 x4y4z4x5 = __vupkd3d( z2x3y3z3x4y4z4x5 , VPACK_FLOAT16_4 ); + z2x3y3z3x4y4z4x5 = __vrlimi( z2x3y3z3x4y4z4x5, z2x3y3z3x4y4z4x5, 0xf, 2 ); + fltx4 z6x7y7z7 = __vupkd3d( y5z5x6y6z6x7y7z7 , VPACK_FLOAT16_4 ); + y5z5x6y6z6x7y7z7 = __vrlimi( y5z5x6y6z6x7y7z7, y5z5x6y6z6x7y7z7, 0xf, 2 ); + fltx4 x0y0z0x1 = __vupkd3d( x0y0z0x1y1z1x2y2 , VPACK_FLOAT16_4 ); + fltx4 z2x3y3z3 = __vupkd3d( z2x3y3z3x4y4z4x5 , VPACK_FLOAT16_4 ); + fltx4 y5z5x6y6 = __vupkd3d( y5z5x6y6z6x7y7z7 , VPACK_FLOAT16_4 ); + + // permute to populate the out-registers with part of their vectors: + out1 = x0y0z0x1; // DONE + out2 = __vpermwi( y1z1x2y2, VPERMWI_CONST(0, 0, 1, 0) ); // __y1z1__ + out3 = __vpermwi( y1z1x2y2, VPERMWI_CONST(2, 3, 0, 0) ); // x2y2____ + out4 = __vpermwi( z2x3y3z3, VPERMWI_CONST(1, 2, 3, 0) ); // x3y3z3__ // DONE + out5 = x4y4z4x5; // DONE + out6 = __vpermwi( y5z5x6y6, VPERMWI_CONST(0, 0, 1, 0) ); // __y5z5__ + out7 = __vpermwi( y5z5x6y6, VPERMWI_CONST(2, 3, 0, 0) ); // x6y6____ + out8 = __vpermwi( z6x7y7z7, VPERMWI_CONST(1, 2, 3, 0) ); // x7y7z7__ // DONE + + // there are four more to finish, which we do with a masked insert + out2 = __vrlimi( out2, x0y0z0x1, 8, 3 ); // x1y1z1__ + out3 = __vrlimi( out3, z2x3y3z3, 2, 2 ); // x2y2x2__ + out6 = __vrlimi( out6, x4y4z4x5, 8, 3 ); // x5y5z5__ + out7 = __vrlimi( out7, z6x7y7z7, 2, 2 ); // x6y6z6__ + + // and we're done! +} + + + +/// Unpack eight consecutive Quaternion48's in memory onto eight SIMD registers. +/// The Quaternion48 pointer must be 16-byte aligned. Eight Quaternion48s add up +/// to 48 bytes long. You should maybe think about prefetching. +// +// This could be improved with verticalization, so that the W sqrts happen +// on two rather than eight vectors, and then transposing. This would make +// the initial permuatation even more complicated. +FORCEINLINE void UnpackEightQuaternion48SIMD( fltx4 &out0, fltx4 &out1, fltx4 &out2, fltx4 &out3, + fltx4 &out4, fltx4 &out5, fltx4 &out6, fltx4 &out7, + Quaternion48 * RESTRICT pVecs ) +{ + AssertMsg((reinterpret_cast(pVecs) & 0x0F) == 0, "Input to UnpackEightQuaternion48SIMD is not 16-byte aligned." ); + // each word of q16s contains 3.0 + n * 2^-22 -- convert this so that we get numbers on the range -1..1 + const fltx4 vUpkMul = SplatXSIMD(g_SIMD_Quat48_Unpack_Magic_Constants); // { UnpackMul16s, UnpackMul16s, UnpackMul16s, UnpackMul16s }; + const fltx4 vUpkAdd = SplatYSIMD(g_SIMD_Quat48_Unpack_Magic_Constants); + const fltx4 shift = __lvx(&g_SIMD_Quat48_Unpack_Shift, 0); // load the aligned shift mask that we use to shuffle z left by one bit. + + // first load the data onto three packed SIMD vectors, which contain eight Quaternion48s between them. + // I've named them very explicitly so you can follow the movement of the input data. + fltx4 x0y0z0x1y1z1x2y2, z2x3y3z3x4y4z4x5, y5z5x6y6z6x7y7z7; + x0y0z0x1y1z1x2y2 = __lvx( pVecs, 0 ); // load reintrepret_cast(pVecs) + 0 + z2x3y3z3x4y4z4x5 = __lvx( pVecs, 16 ); // load reintrepret_cast(pVecs) + 1 + y5z5x6y6z6x7y7z7 = __lvx( pVecs, 32 ); // load reintrepret_cast(pVecs) + 2 + + // shove each quat onto its own fltx4, by using the permute operation + // each halfword argument goes into the bottom 16 bits of the floating + // point rep of 3.0f, then we use a magic constant to scale them. + out0 = __vperm( x0y0z0x1y1z1x2y2, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute0) ); // __x0__y0__z0____ + out1 = __vperm( x0y0z0x1y1z1x2y2, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute1) ); // __x1__y1__z1____ + // postpone 2 since it straddles two words, we'll get back to it + out3 = __vperm( z2x3y3z3x4y4z4x5, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute3) ); // __x3__y3__z3__z2 // z2 is important, goes into out2 + out4 = __vperm( z2x3y3z3x4y4z4x5, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute4) ); // __x4__y4__z4__x5 // x5 is important, goes into out5 + // 5 straddles two words + out6 = __vperm( y5z5x6y6z6x7y7z7, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute6) ); // __x6__y6__z6____ + out7 = __vperm( y5z5x6y6z6x7y7z7, Four_Threes, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute7) ); // __x7__y7__z7____ + // now get back to the straddlers, which we make by blending together a prior output and the other source word + out2 = __vperm( x0y0z0x1y1z1x2y2, out3, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute2) ); // __x2__y2__z2____ + out5 = __vperm( y5z5x6y6z6x7y7z7, out4, *reinterpret_cast(&g_SIMD_Quat48_Unpack_Permute5) ); // __x5__y5__z5____ + + // the top bit of the z component in each word isn't part of the number; it's + // a flag indicating whether the eventual w component should be negative. + // so, we need to move the 0x00008000 bit of the z word onto the top bit + // of the w word, which is a rotation two bytes right, or 14 bytes left. + fltx4 wneg[8]; + // juggle all the z halfwords left one bit (toss the wneg sign bit, multiply by two) + wneg[0] = __vsldoi( out0, out0, 14 ); + out0 = __vslh(out0, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[1] = __vsldoi( out1, out1, 14 ); + out1 = __vslh(out1, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[2] = __vsldoi( out2, out2, 14 ); + out2 = __vslh(out2, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[3] = __vsldoi( out3, out3, 14 ); + out3 = __vslh(out3, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[4] = __vsldoi( out4, out4, 14 ); + out4 = __vslh(out4, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[5] = __vsldoi( out5, out5, 14 ); + out5 = __vslh(out5, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[6] = __vsldoi( out6, out6, 14 ); + out6 = __vslh(out6, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + wneg[7] = __vsldoi( out7, out7, 14 ); + out7 = __vslh(out7, shift); // shift the z component left by one bit, tossing out the wneg sign bit and mapping z from [0..2^15) to [0..2^16) + + // create a mask that is just the sign bit of the w word. + fltx4 vAllOneBits = __vspltisw(-1); // Shift 31 + fltx4 signMask = __vslw(vAllOneBits, vAllOneBits); // all the sign bits + signMask = __vrlimi( signMask, Four_Zeros, 14, 0 ); // zero out x,y,z words + + // this macro defines the operations that will be performed on each of the eight words: + // * scale from 0..65535 to -1..1 : tmp.x = ((int)x - 32768) * (1 / 32768.0); + // * take the xyz dot product to get 1 - w^2 + // * subtract from one to get w^2 + // * square root to get zero + // * OR in the wneg sign mask to get sign for zero. + // though the macro makes it look like these are being done in serial, + // in fact the compiler will reorder them to minimize stalls. + fltx4 ONE = Four_Ones; + fltx4 dotxyz[8]; + fltx4 ww[8]; + // out0 = __vmaddfp( out0, vUpkMul, vUpkAdd ); + // dotxyz[0] = Dot3SIMD( out0, out0 ); + // clamnp dotxyz if it's more than 1.0 + // all components are 1 - dotxyz + // clear all but w's sign bit in wneg + // all components are sqrt(1-dotxyz) + // toggle w's sign where necessary + // insert one element from the ww vector into the w component of ret +#define COMPUTE( target, number ) \ + target ## number = __vmaddfp( target ## number, vUpkMul, vUpkAdd ); \ + dotxyz[number] = Dot3SIMD( target ## number, target ## number ); \ + dotxyz[number] = __vminfp( dotxyz[number], ONE ); \ + ww[number] = SubSIMD( ONE, dotxyz[number] ); \ + wneg[number] = AndSIMD( wneg[number], signMask ) ; \ + ww[number] = SqrtSIMD(ww[number]); \ + ww[number] = OrSIMD( ww[number], wneg[number] ); \ + target ## number = __vrlimi( target ## number, ww[number], 1, 0 ); + + COMPUTE(out, 0); + COMPUTE(out, 1); + COMPUTE(out, 2); + COMPUTE(out, 3); + COMPUTE(out, 4); + COMPUTE(out, 5); + COMPUTE(out, 6); + COMPUTE(out, 7); + +#undef COMPUTE +} + + +// find the lowest component of a.x, a.y, a.z, +// and replicate it to the whole return value. +// ignores a.w. +// Though this is only five instructions long, +// they are all dependent, making this stall city. +// Forcing this inline should hopefully help with scheduling. +FORCEINLINE fltx4 FindLowestSIMD3( const fltx4 & a ) +{ + // a is [x,y,z,G] (where G is garbage) + // rotate left by one + fltx4 compareOne = a ; + compareOne = __vrlimi( compareOne, a, 8 | 4 , 1 ); + // compareOne is [y,z,G,G] + fltx4 retval = MinSIMD( a, compareOne ); + // retVal is [min(x,y), min(y,z), G, G] + compareOne = __vrlimi( compareOne, a, 8 , 2); + // compareOne is [z, G, G, G] + retval = MinSIMD( retval, compareOne ); + // retVal = [ min(min(x,y),z), G, G, G ] + + // splat the x component out to the whole vector and return + return SplatXSIMD( retval ); +} + +// find the highest component of a.x, a.y, a.z, +// and replicate it to the whole return value. +// ignores a.w. +// Though this is only five instructions long, +// they are all dependent, making this stall city. +// Forcing this inline should hopefully help with scheduling. +FORCEINLINE fltx4 FindHighestSIMD3( const fltx4 & a ) +{ + // a is [x,y,z,G] (where G is garbage) + // rotate left by one + fltx4 compareOne = a ; + compareOne = __vrlimi( compareOne, a, 8 | 4 , 1 ); + // compareOne is [y,z,G,G] + fltx4 retval = MaxSIMD( a, compareOne ); + // retVal is [max(x,y), max(y,z), G, G] + compareOne = __vrlimi( compareOne, a, 8 , 2); + // compareOne is [z, G, G, G] + retval = MaxSIMD( retval, compareOne ); + // retVal = [ max(max(x,y),z), G, G, G ] + + // splat the x component out to the whole vector and return + return SplatXSIMD( retval ); +} + + +// Transform many (horizontal) points in-place by a 3x4 matrix, +// here already loaded onto three fltx4 registers. +// The points must be stored as 16-byte aligned. They are points +// and not vectors because we assume the w-component to be 1. +// To spare yourself the annoyance of loading the matrix yourself, +// use one of the overloads below. +void TransformManyPointsBy(VectorAligned * RESTRICT pVectors, unsigned int numVectors, FLTX4 mRow1, FLTX4 mRow2, FLTX4 mRow3); + +// Transform many (horizontal) points in-place by a 3x4 matrix. +// The points must be stored as 16-byte aligned. They are points +// and not vectors because we assume the w-component to be 1. +// In this function, the matrix need not be aligned. +FORCEINLINE void TransformManyPointsBy(VectorAligned * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t &pMatrix) +{ + return TransformManyPointsBy(pVectors, numVectors, + LoadUnalignedSIMD( pMatrix[0] ), LoadUnalignedSIMD( pMatrix[1] ), LoadUnalignedSIMD( pMatrix[2] ) ); +} + +// Transform many (horizontal) points in-place by a 3x4 matrix. +// The points must be stored as 16-byte aligned. They are points +// and not vectors because we assume the w-component to be 1. +// In this function, the matrix must itself be aligned on a 16-byte +// boundary. +FORCEINLINE void TransformManyPointsByA(VectorAligned * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t &pMatrix) +{ + return TransformManyPointsBy(pVectors, numVectors, + LoadAlignedSIMD( pMatrix[0] ), LoadAlignedSIMD( pMatrix[1] ), LoadAlignedSIMD( pMatrix[2] ) ); +} + +// ------------------------------------ +// INTEGER SIMD OPERATIONS. +// ------------------------------------ + +// Load 4 aligned words into a SIMD register +FORCEINLINE i32x4 LoadAlignedIntSIMD( const void * RESTRICT pSIMD) +{ + return XMLoadVector4A(pSIMD); +} + +// Load 4 unaligned words into a SIMD register +FORCEINLINE i32x4 LoadUnalignedIntSIMD(const void * RESTRICT pSIMD) +{ + return XMLoadVector4( pSIMD ); +} + +// save into four words, 16-byte aligned +FORCEINLINE void StoreAlignedIntSIMD( int32 *pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< i32x4 *> ( pSIMD ) ) = a; +} + +FORCEINLINE void StoreAlignedIntSIMD( intx4 &pSIMD, const fltx4 & a ) +{ + *( reinterpret_cast< i32x4 *> ( pSIMD.Base() ) ) = a; +} + +FORCEINLINE void StoreUnalignedIntSIMD( int32 *pSIMD, const fltx4 & a ) +{ + XMStoreVector4(pSIMD, a); +} + +// Load four consecutive uint16's, and turn them into floating point numbers. +// This function isn't especially fast and could be made faster if anyone is +// using it heavily. +FORCEINLINE fltx4 LoadAndConvertUint16SIMD( const uint16 *pInts ) +{ + return XMLoadUShort4(reinterpret_cast(pInts)); +} + +// a={ a.x, a.z, b.x, b.z } +// combine two fltx4s by throwing away every other field. +FORCEINLINE fltx4 CompressSIMD( fltx4 const & a, fltx4 const &b ) +{ + return XMVectorPermute( a, b, XMVectorPermuteControl( 0, 2, 4, 6 ) ); +} + +// a={ a.x, b.x, c.x, d.x } +// combine 4 fltx4s by throwing away 3/4s of the fields +// TODO: make more efficient by doing this in a parallel way at the caller +// Compress4SIMD(FourVectors.. ) +FORCEINLINE fltx4 Compress4SIMD( fltx4 const a, fltx4 const &b, fltx4 const &c, fltx4 const &d ) +{ + fltx4 abcd = __vrlimi( a, b, 4, 3 ); // a.x, b.x, a.z, a.w + abcd = __vrlimi( abcd, c, 2, 2 ); // ax, bx, cx, aw + abcd = __vrlimi( abcd, d, 1, 1 ); // ax, bx, cx, dx + + return abcd; +} + +// Take a fltx4 containing fixed-point uints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 UnsignedIntConvertToFltSIMD( const i32x4 &vSrcA ) +{ + return __vcfux( vSrcA, 0 ); +} + +// Take a fltx4 containing fixed-point sints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 SignedIntConvertToFltSIMD( const i32x4 &vSrcA ) +{ + return __vcfsx( vSrcA, 0 ); +} + +// Take a fltx4 containing fixed-point uints and +// return them as single precision floats. Each uint +// will be divided by 2^immed after conversion +// (eg, this is fixed point math). +/* as if: + FORCEINLINE fltx4 UnsignedIntConvertToFltSIMD( const i32x4 &vSrcA, unsigned int uImmed ) + { + return __vcfux( vSrcA, uImmed ); + } +*/ +#define UnsignedFixedIntConvertToFltSIMD(vSrcA, uImmed) (__vcfux( (vSrcA), (uImmed) )) + +// Take a fltx4 containing fixed-point sints and +// return them as single precision floats. Each int +// will be divided by 2^immed (eg, this is fixed point +// math). +/* as if: + FORCEINLINE fltx4 SignedIntConvertToFltSIMD( const i32x4 &vSrcA, unsigned int uImmed ) + { + return __vcfsx( vSrcA, uImmed ); + } +*/ +#define SignedFixedIntConvertToFltSIMD(vSrcA, uImmed) (__vcfsx( (vSrcA), (uImmed) )) + +// set all components of a vector to a signed immediate int number. +/* as if: + FORCEINLINE fltx4 IntSetImmediateSIMD(int toImmediate) + { + return __vspltisw( toImmediate ); + } +*/ +#define IntSetImmediateSIMD(x) (__vspltisw(x)) + +/* + works on fltx4's as if they are four uints. + the first parameter contains the words to be shifted, + the second contains the amount to shift by AS INTS + + for i = 0 to 3 + shift = vSrcB_i*32:(i*32)+4 + vReturned_i*32:(i*32)+31 = vSrcA_i*32:(i*32)+31 << shift +*/ +FORCEINLINE fltx4 IntShiftLeftWordSIMD(fltx4 vSrcA, fltx4 vSrcB) +{ + return __vslw(vSrcA, vSrcB); +} + +FORCEINLINE float SubFloat( const fltx4 & a, int idx ) +{ + // NOTE: if the output goes into a register, this causes a Load-Hit-Store stall (don't mix fpu/vpu math!) + const fltx4_union & a_union = (const fltx4_union &)a; + return a_union.m128_f32[ idx ]; +} + +FORCEINLINE float & SubFloat( fltx4 & a, int idx ) +{ + fltx4_union & a_union = (fltx4_union &)a; + return a_union.m128_f32[idx]; +} + +FORCEINLINE uint32 SubFloatConvertToInt( const fltx4 & a, int idx ) +{ + fltx4 t = __vctuxs( a, 0 ); + const fltx4_union & a_union = (const fltx4_union &)t; + return a_union.m128_u32[idx]; +} + + +FORCEINLINE uint32 SubInt( const fltx4 & a, int idx ) +{ + const fltx4_union & a_union = (const fltx4_union &)a; + return a_union.m128_u32[idx]; +} + +FORCEINLINE uint32 & SubInt( fltx4 & a, int idx ) +{ + fltx4_union & a_union = (fltx4_union &)a; + return a_union.m128_u32[idx]; +} + +#else + +//--------------------------------------------------------------------- +// Intel/SSE implementation +//--------------------------------------------------------------------- + +FORCEINLINE void StoreAlignedSIMD( float * RESTRICT pSIMD, const fltx4 & a ) +{ + _mm_store_ps( pSIMD, a ); +} + +FORCEINLINE void StoreUnalignedSIMD( float * RESTRICT pSIMD, const fltx4 & a ) +{ + _mm_storeu_ps( pSIMD, a ); +} + + +FORCEINLINE fltx4 RotateLeft( const fltx4 & a ); +FORCEINLINE fltx4 RotateLeft2( const fltx4 & a ); + +FORCEINLINE void StoreUnaligned3SIMD( float *pSIMD, const fltx4 & a ) +{ + _mm_store_ss(pSIMD, a); + _mm_store_ss(pSIMD+1, RotateLeft(a)); + _mm_store_ss(pSIMD+2, RotateLeft2(a)); +} + +// strongly typed -- syntactic castor oil used for typechecking as we transition to SIMD +FORCEINLINE void StoreAligned3SIMD( VectorAligned * RESTRICT pSIMD, const fltx4 & a ) +{ + StoreAlignedSIMD( pSIMD->Base(),a ); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination[0], pDestination[1], pDestination[2], pDestination[3] +// The Vectors are assumed to be unaligned. +FORCEINLINE void StoreFourUnalignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + StoreUnaligned3SIMD( pDestination->Base(), a ); + StoreUnaligned3SIMD( (pDestination+1)->Base(), b ); + StoreUnaligned3SIMD( (pDestination+2)->Base(), c ); + StoreUnaligned3SIMD( (pDestination+3)->Base(), d ); +} + +// Store the x,y,z components of the four FLTX4 parameters +// into the four consecutive Vectors: +// pDestination , pDestination + 1, pDestination + 2, pDestination + 3 +// The Vectors are assumed to start on an ALIGNED address, that is, +// pDestination is 16-byte aligned (thhough obviously pDestination+1 is not). +FORCEINLINE void StoreFourAlignedVector3SIMD( fltx4 a, fltx4 b, fltx4 c, FLTX4 d, // first three passed by copy (deliberate) + Vector * const pDestination ) +{ + StoreUnaligned3SIMD( pDestination->Base(), a ); + StoreUnaligned3SIMD( (pDestination+1)->Base(), b ); + StoreUnaligned3SIMD( (pDestination+2)->Base(), c ); + StoreUnaligned3SIMD( (pDestination+3)->Base(), d ); +} + +FORCEINLINE fltx4 LoadAlignedSIMD( const void *pSIMD ) +{ + return _mm_load_ps( reinterpret_cast< const float *> ( pSIMD ) ); +} + +FORCEINLINE fltx4 AndSIMD( const fltx4 & a, const fltx4 & b ) // a & b +{ + return _mm_and_ps( a, b ); +} + +FORCEINLINE fltx4 AndNotSIMD( const fltx4 & a, const fltx4 & b ) // a & ~b +{ + return _mm_andnot_ps( a, b ); +} + +FORCEINLINE fltx4 XorSIMD( const fltx4 & a, const fltx4 & b ) // a ^ b +{ + return _mm_xor_ps( a, b ); +} + +FORCEINLINE fltx4 OrSIMD( const fltx4 & a, const fltx4 & b ) // a | b +{ + return _mm_or_ps( a, b ); +} + +// Squelch the w component of a vector to +0.0. +// Most efficient when you say a = SetWToZeroSIMD(a) (avoids a copy) +FORCEINLINE fltx4 SetWToZeroSIMD( const fltx4 & a ) +{ + return AndSIMD( a, LoadAlignedSIMD( g_SIMD_clear_wmask ) ); +} + +// for the transitional class -- load a 3-by VectorAligned and squash its w component +FORCEINLINE fltx4 LoadAlignedSIMD( const VectorAligned & pSIMD ) +{ + return SetWToZeroSIMD( LoadAlignedSIMD(pSIMD.Base()) ); +} + +FORCEINLINE fltx4 LoadUnalignedSIMD( const void *pSIMD ) +{ + return _mm_loadu_ps( reinterpret_cast( pSIMD ) ); +} + +FORCEINLINE fltx4 LoadUnaligned3SIMD( const void *pSIMD ) +{ + return _mm_loadu_ps( reinterpret_cast( pSIMD ) ); +} + +// load a single unaligned float into the x component of a SIMD word +FORCEINLINE fltx4 LoadUnalignedFloatSIMD( const float *pFlt ) +{ + return _mm_load_ss(pFlt); +} + +/// replicate a single 32 bit integer value to all 4 components of an m128 +FORCEINLINE fltx4 ReplicateIX4( int i ) +{ + fltx4 value = _mm_set_ss( * ( ( float *) &i ) );; + return _mm_shuffle_ps( value, value, 0); +} + + +FORCEINLINE fltx4 ReplicateX4( float flValue ) +{ + __m128 value = _mm_set_ss( flValue ); + return _mm_shuffle_ps( value, value, 0 ); +} + + +FORCEINLINE float SubFloat( const fltx4 & a, int idx ) +{ + // NOTE: if the output goes into a register, this causes a Load-Hit-Store stall (don't mix fpu/vpu math!) +#ifndef POSIX + return a.m128_f32[ idx ]; +#else + return (reinterpret_cast(&a))[idx]; +#endif +} + +FORCEINLINE float & SubFloat( fltx4 & a, int idx ) +{ +#ifndef POSIX + return a.m128_f32[ idx ]; +#else + return (reinterpret_cast(&a))[idx]; +#endif +} + +FORCEINLINE uint32 SubFloatConvertToInt( const fltx4 & a, int idx ) +{ + return (uint32)SubFloat(a,idx); +} + +FORCEINLINE uint32 SubInt( const fltx4 & a, int idx ) +{ +#ifndef POSIX + return a.m128_u32[idx]; +#else + return (reinterpret_cast(&a))[idx]; +#endif +} + +FORCEINLINE uint32 & SubInt( fltx4 & a, int idx ) +{ +#ifndef POSIX + return a.m128_u32[idx]; +#else + return (reinterpret_cast(&a))[idx]; +#endif +} + +// Return one in the fastest way -- on the x360, faster even than loading. +FORCEINLINE fltx4 LoadZeroSIMD( void ) +{ + return Four_Zeros; +} + +// Return one in the fastest way -- on the x360, faster even than loading. +FORCEINLINE fltx4 LoadOneSIMD( void ) +{ + return Four_Ones; +} + +FORCEINLINE fltx4 MaskedAssign( const fltx4 & ReplacementMask, const fltx4 & NewValue, const fltx4 & OldValue ) +{ + return OrSIMD( + AndSIMD( ReplacementMask, NewValue ), + AndNotSIMD( ReplacementMask, OldValue ) ); +} + +// remember, the SSE numbers its words 3 2 1 0 +// The way we want to specify shuffles is backwards from the default +// MM_SHUFFLE_REV is in array index order (default is reversed) +#define MM_SHUFFLE_REV(a,b,c,d) _MM_SHUFFLE(d,c,b,a) + +FORCEINLINE fltx4 SplatXSIMD( fltx4 const & a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 0, 0, 0, 0 ) ); +} + +FORCEINLINE fltx4 SplatYSIMD( fltx4 const &a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 1, 1, 1, 1 ) ); +} + +FORCEINLINE fltx4 SplatZSIMD( fltx4 const &a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 2, 2, 2, 2 ) ); +} + +FORCEINLINE fltx4 SplatWSIMD( fltx4 const &a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 3, 3, 3, 3 ) ); +} + +FORCEINLINE fltx4 SetXSIMD( const fltx4& a, const fltx4& x ) +{ + fltx4 result = MaskedAssign( LoadAlignedSIMD( g_SIMD_ComponentMask[0] ), x, a ); + return result; +} + +FORCEINLINE fltx4 SetYSIMD( const fltx4& a, const fltx4& y ) +{ + fltx4 result = MaskedAssign( LoadAlignedSIMD( g_SIMD_ComponentMask[1] ), y, a ); + return result; +} + +FORCEINLINE fltx4 SetZSIMD( const fltx4& a, const fltx4& z ) +{ + fltx4 result = MaskedAssign( LoadAlignedSIMD( g_SIMD_ComponentMask[2] ), z, a ); + return result; +} + +FORCEINLINE fltx4 SetWSIMD( const fltx4& a, const fltx4& w ) +{ + fltx4 result = MaskedAssign( LoadAlignedSIMD( g_SIMD_ComponentMask[3] ), w, a ); + return result; +} + +FORCEINLINE fltx4 SetComponentSIMD( const fltx4& a, int nComponent, float flValue ) +{ + fltx4 val = ReplicateX4( flValue ); + fltx4 result = MaskedAssign( LoadAlignedSIMD( g_SIMD_ComponentMask[nComponent] ), val, a ); + return result; +} + +// a b c d -> b c d a +FORCEINLINE fltx4 RotateLeft( const fltx4 & a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 1, 2, 3, 0 ) ); +} + +// a b c d -> c d a b +FORCEINLINE fltx4 RotateLeft2( const fltx4 & a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 2, 3, 0, 1 ) ); +} + +// a b c d -> d a b c +FORCEINLINE fltx4 RotateRight( const fltx4 & a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 3, 0, 1, 2 ) ); +} + +// a b c d -> c d a b +FORCEINLINE fltx4 RotateRight2( const fltx4 & a ) +{ + return _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 2, 3, 0, 1 ) ); +} + +FORCEINLINE fltx4 AddSIMD( const fltx4 & a, const fltx4 & b ) // a+b +{ + return _mm_add_ps( a, b ); +} + +FORCEINLINE fltx4 SubSIMD( const fltx4 & a, const fltx4 & b ) // a-b +{ + return _mm_sub_ps( a, b ); +}; + +FORCEINLINE fltx4 MulSIMD( const fltx4 & a, const fltx4 & b ) // a*b +{ + return _mm_mul_ps( a, b ); +}; + +FORCEINLINE fltx4 DivSIMD( const fltx4 & a, const fltx4 & b ) // a/b +{ + return _mm_div_ps( a, b ); +}; + +FORCEINLINE fltx4 MaddSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // a*b + c +{ + return AddSIMD( MulSIMD(a,b), c ); +} + +FORCEINLINE fltx4 MsubSIMD( const fltx4 & a, const fltx4 & b, const fltx4 & c ) // c - a*b +{ + return SubSIMD( c, MulSIMD(a,b) ); +}; + +FORCEINLINE fltx4 Dot3SIMD( const fltx4 &a, const fltx4 &b ) +{ + fltx4 m = MulSIMD( a, b ); + float flDot = SubFloat( m, 0 ) + SubFloat( m, 1 ) + SubFloat( m, 2 ); + return ReplicateX4( flDot ); +} + +FORCEINLINE fltx4 Dot4SIMD( const fltx4 &a, const fltx4 &b ) +{ + fltx4 m = MulSIMD( a, b ); + float flDot = SubFloat( m, 0 ) + SubFloat( m, 1 ) + SubFloat( m, 2 ) + SubFloat( m, 3 ); + return ReplicateX4( flDot ); +} + +//TODO: implement as four-way Taylor series (see xbox implementation) +FORCEINLINE fltx4 SinSIMD( const fltx4 &radians ) +{ + fltx4 result; + SubFloat( result, 0 ) = sin( SubFloat( radians, 0 ) ); + SubFloat( result, 1 ) = sin( SubFloat( radians, 1 ) ); + SubFloat( result, 2 ) = sin( SubFloat( radians, 2 ) ); + SubFloat( result, 3 ) = sin( SubFloat( radians, 3 ) ); + return result; +} + +FORCEINLINE void SinCos3SIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) +{ + // FIXME: Make a fast SSE version + SinCos( SubFloat( radians, 0 ), &SubFloat( sine, 0 ), &SubFloat( cosine, 0 ) ); + SinCos( SubFloat( radians, 1 ), &SubFloat( sine, 1 ), &SubFloat( cosine, 1 ) ); + SinCos( SubFloat( radians, 2 ), &SubFloat( sine, 2 ), &SubFloat( cosine, 2 ) ); +} + +FORCEINLINE void SinCosSIMD( fltx4 &sine, fltx4 &cosine, const fltx4 &radians ) // a*b + c +{ + // FIXME: Make a fast SSE version + SinCos( SubFloat( radians, 0 ), &SubFloat( sine, 0 ), &SubFloat( cosine, 0 ) ); + SinCos( SubFloat( radians, 1 ), &SubFloat( sine, 1 ), &SubFloat( cosine, 1 ) ); + SinCos( SubFloat( radians, 2 ), &SubFloat( sine, 2 ), &SubFloat( cosine, 2 ) ); + SinCos( SubFloat( radians, 3 ), &SubFloat( sine, 3 ), &SubFloat( cosine, 3 ) ); +} + +//TODO: implement as four-way Taylor series (see xbox implementation) +FORCEINLINE fltx4 ArcSinSIMD( const fltx4 &sine ) +{ + // FIXME: Make a fast SSE version + fltx4 result; + SubFloat( result, 0 ) = asin( SubFloat( sine, 0 ) ); + SubFloat( result, 1 ) = asin( SubFloat( sine, 1 ) ); + SubFloat( result, 2 ) = asin( SubFloat( sine, 2 ) ); + SubFloat( result, 3 ) = asin( SubFloat( sine, 3 ) ); + return result; +} + +FORCEINLINE fltx4 ArcCosSIMD( const fltx4 &cs ) +{ + fltx4 result; + SubFloat( result, 0 ) = acos( SubFloat( cs, 0 ) ); + SubFloat( result, 1 ) = acos( SubFloat( cs, 1 ) ); + SubFloat( result, 2 ) = acos( SubFloat( cs, 2 ) ); + SubFloat( result, 3 ) = acos( SubFloat( cs, 3 ) ); + return result; +} + +// tan^1(a/b) .. ie, pass sin in as a and cos in as b +FORCEINLINE fltx4 ArcTan2SIMD( const fltx4 &a, const fltx4 &b ) +{ + fltx4 result; + SubFloat( result, 0 ) = atan2( SubFloat( a, 0 ), SubFloat( b, 0 ) ); + SubFloat( result, 1 ) = atan2( SubFloat( a, 1 ), SubFloat( b, 1 ) ); + SubFloat( result, 2 ) = atan2( SubFloat( a, 2 ), SubFloat( b, 2 ) ); + SubFloat( result, 3 ) = atan2( SubFloat( a, 3 ), SubFloat( b, 3 ) ); + return result; +} + +FORCEINLINE fltx4 NegSIMD(const fltx4 &a) // negate: -a +{ + return SubSIMD(LoadZeroSIMD(),a); +} + +FORCEINLINE int TestSignSIMD( const fltx4 & a ) // mask of which floats have the high bit set +{ + return _mm_movemask_ps( a ); +} + +FORCEINLINE bool IsAnyNegative( const fltx4 & a ) // (a.x < 0) || (a.y < 0) || (a.z < 0) || (a.w < 0) +{ + return (0 != TestSignSIMD( a )); +} + +FORCEINLINE fltx4 CmpEqSIMD( const fltx4 & a, const fltx4 & b ) // (a==b) ? ~0:0 +{ + return _mm_cmpeq_ps( a, b ); +} + +FORCEINLINE fltx4 CmpGtSIMD( const fltx4 & a, const fltx4 & b ) // (a>b) ? ~0:0 +{ + return _mm_cmpgt_ps( a, b ); +} + +FORCEINLINE fltx4 CmpGeSIMD( const fltx4 & a, const fltx4 & b ) // (a>=b) ? ~0:0 +{ + return _mm_cmpge_ps( a, b ); +} + +FORCEINLINE fltx4 CmpLtSIMD( const fltx4 & a, const fltx4 & b ) // (a b.xyzw +FORCEINLINE bool IsAllGreaterThan( const fltx4 &a, const fltx4 &b ) +{ + return TestSignSIMD( CmpLeSIMD( a, b ) ) == 0; +} + +// for branching when a.xyzw >= b.xyzw +FORCEINLINE bool IsAllGreaterThanOrEq( const fltx4 &a, const fltx4 &b ) +{ + return TestSignSIMD( CmpLtSIMD( a, b ) ) == 0; +} + +// For branching if all a.xyzw == b.xyzw +FORCEINLINE bool IsAllEqual( const fltx4 & a, const fltx4 & b ) +{ + return TestSignSIMD( CmpEqSIMD( a, b ) ) == 0xf; +} + +FORCEINLINE fltx4 CmpInBoundsSIMD( const fltx4 & a, const fltx4 & b ) // (a <= b && a >= -b) ? ~0 : 0 +{ + return AndSIMD( CmpLeSIMD(a,b), CmpGeSIMD(a, NegSIMD(b)) ); +} + +FORCEINLINE fltx4 MinSIMD( const fltx4 & a, const fltx4 & b ) // min(a,b) +{ + return _mm_min_ps( a, b ); +} + +FORCEINLINE fltx4 MaxSIMD( const fltx4 & a, const fltx4 & b ) // max(a,b) +{ + return _mm_max_ps( a, b ); +} + + + +// SSE lacks rounding operations. +// Really. +// You can emulate them by setting the rounding mode for the +// whole processor and then converting to int, and then back again. +// But every time you set the rounding mode, you clear out the +// entire pipeline. So, I can't do them per operation. You +// have to do it once, before the loop that would call these. +// Round towards positive infinity +FORCEINLINE fltx4 CeilSIMD( const fltx4 &a ) +{ + fltx4 retVal; + SubFloat( retVal, 0 ) = ceil( SubFloat( a, 0 ) ); + SubFloat( retVal, 1 ) = ceil( SubFloat( a, 1 ) ); + SubFloat( retVal, 2 ) = ceil( SubFloat( a, 2 ) ); + SubFloat( retVal, 3 ) = ceil( SubFloat( a, 3 ) ); + return retVal; + +} + +fltx4 fabs( const fltx4 & x ); +// Round towards negative infinity +// This is the implementation that was here before; it assumes +// you are in round-to-floor mode, which I guess is usually the +// case for us vis-a-vis SSE. It's totally unnecessary on +// VMX, which has a native floor op. +FORCEINLINE fltx4 FloorSIMD( const fltx4 &val ) +{ + fltx4 fl4Abs = fabs( val ); + fltx4 ival = SubSIMD( AddSIMD( fl4Abs, Four_2ToThe23s ), Four_2ToThe23s ); + ival = MaskedAssign( CmpGtSIMD( ival, fl4Abs ), SubSIMD( ival, Four_Ones ), ival ); + return XorSIMD( ival, XorSIMD( val, fl4Abs ) ); // restore sign bits +} + + + +FORCEINLINE bool IsAnyZeros( const fltx4 & a ) // any floats are zero? +{ + return TestSignSIMD( CmpEqSIMD( a, Four_Zeros ) ) != 0; +} + +inline bool IsAllZeros( const fltx4 & var ) +{ + return TestSignSIMD( CmpEqSIMD( var, Four_Zeros ) ) == 0xF; +} + +FORCEINLINE fltx4 SqrtEstSIMD( const fltx4 & a ) // sqrt(a), more or less +{ + return _mm_sqrt_ps( a ); +} + +FORCEINLINE fltx4 SqrtSIMD( const fltx4 & a ) // sqrt(a) +{ + return _mm_sqrt_ps( a ); +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSIMD( const fltx4 & a ) // 1/sqrt(a), more or less +{ + return _mm_rsqrt_ps( a ); +} + +FORCEINLINE fltx4 ReciprocalSqrtEstSaturateSIMD( const fltx4 & a ) +{ + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 ret = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + ret = ReciprocalSqrtEstSIMD( ret ); + return ret; +} + +/// uses newton iteration for higher precision results than ReciprocalSqrtEstSIMD +FORCEINLINE fltx4 ReciprocalSqrtSIMD( const fltx4 & a ) // 1/sqrt(a) +{ + fltx4 guess = ReciprocalSqrtEstSIMD( a ); + // newton iteration for 1/sqrt(a) : y(n+1) = 1/2 (y(n)*(3-a*y(n)^2)); + guess = MulSIMD( guess, SubSIMD( Four_Threes, MulSIMD( a, MulSIMD( guess, guess )))); + guess = MulSIMD( Four_PointFives, guess); + return guess; +} + +FORCEINLINE fltx4 ReciprocalEstSIMD( const fltx4 & a ) // 1/a, more or less +{ + return _mm_rcp_ps( a ); +} + +/// 1/x for all 4 values, more or less +/// 1/0 will result in a big but NOT infinite result +FORCEINLINE fltx4 ReciprocalEstSaturateSIMD( const fltx4 & a ) +{ + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 ret = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + ret = ReciprocalEstSIMD( ret ); + return ret; +} + +/// 1/x for all 4 values. uses reciprocal approximation instruction plus newton iteration. +/// No error checking! +FORCEINLINE fltx4 ReciprocalSIMD( const fltx4 & a ) // 1/a +{ + fltx4 ret = ReciprocalEstSIMD( a ); + // newton iteration is: Y(n+1) = 2*Y(n)-a*Y(n)^2 + ret = SubSIMD( AddSIMD( ret, ret ), MulSIMD( a, MulSIMD( ret, ret ) ) ); + return ret; +} + +/// 1/x for all 4 values. +/// 1/0 will result in a big but NOT infinite result +FORCEINLINE fltx4 ReciprocalSaturateSIMD( const fltx4 & a ) +{ + fltx4 zero_mask = CmpEqSIMD( a, Four_Zeros ); + fltx4 ret = OrSIMD( a, AndSIMD( Four_Epsilons, zero_mask ) ); + ret = ReciprocalSIMD( ret ); + return ret; +} + +// CHRISG: is it worth doing integer bitfiddling for this? +// 2^x for all values (the antilog) +FORCEINLINE fltx4 ExpSIMD( const fltx4 &toPower ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = powf( 2, SubFloat(toPower, 0) ); + SubFloat( retval, 1 ) = powf( 2, SubFloat(toPower, 1) ); + SubFloat( retval, 2 ) = powf( 2, SubFloat(toPower, 2) ); + SubFloat( retval, 3 ) = powf( 2, SubFloat(toPower, 3) ); + + return retval; +} + +// Clamps the components of a vector to a specified minimum and maximum range. +FORCEINLINE fltx4 ClampVectorSIMD( FLTX4 in, FLTX4 min, FLTX4 max) +{ + return MaxSIMD( min, MinSIMD( max, in ) ); +} + +FORCEINLINE void TransposeSIMD( fltx4 & x, fltx4 & y, fltx4 & z, fltx4 & w) +{ + _MM_TRANSPOSE4_PS( x, y, z, w ); +} + +FORCEINLINE fltx4 FindLowestSIMD3( const fltx4 &a ) +{ + // a is [x,y,z,G] (where G is garbage) + // rotate left by one + fltx4 compareOne = RotateLeft( a ); + // compareOne is [y,z,G,x] + fltx4 retval = MinSIMD( a, compareOne ); + // retVal is [min(x,y), ... ] + compareOne = RotateLeft2( a ); + // compareOne is [z, G, x, y] + retval = MinSIMD( retval, compareOne ); + // retVal = [ min(min(x,y),z)..] + // splat the x component out to the whole vector and return + return SplatXSIMD( retval ); + +} + +FORCEINLINE fltx4 FindHighestSIMD3( const fltx4 &a ) +{ + // a is [x,y,z,G] (where G is garbage) + // rotate left by one + fltx4 compareOne = RotateLeft( a ); + // compareOne is [y,z,G,x] + fltx4 retval = MaxSIMD( a, compareOne ); + // retVal is [max(x,y), ... ] + compareOne = RotateLeft2( a ); + // compareOne is [z, G, x, y] + retval = MaxSIMD( retval, compareOne ); + // retVal = [ max(max(x,y),z)..] + // splat the x component out to the whole vector and return + return SplatXSIMD( retval ); + +} + +// ------------------------------------ +// INTEGER SIMD OPERATIONS. +// ------------------------------------ + + +#if 0 /* pc does not have these ops */ +// splat all components of a vector to a signed immediate int number. +FORCEINLINE fltx4 IntSetImmediateSIMD(int to) +{ + //CHRISG: SSE2 has this, but not SSE1. What to do? + fltx4 retval; + SubInt( retval, 0 ) = to; + SubInt( retval, 1 ) = to; + SubInt( retval, 2 ) = to; + SubInt( retval, 3 ) = to; + return retval; +} +#endif + +// Load 4 aligned words into a SIMD register +FORCEINLINE i32x4 LoadAlignedIntSIMD( const void * RESTRICT pSIMD) +{ + return _mm_load_ps( reinterpret_cast(pSIMD) ); +} + +// Load 4 unaligned words into a SIMD register +FORCEINLINE i32x4 LoadUnalignedIntSIMD( const void * RESTRICT pSIMD) +{ + return _mm_loadu_ps( reinterpret_cast(pSIMD) ); +} + +// save into four words, 16-byte aligned +FORCEINLINE void StoreAlignedIntSIMD( int32 * RESTRICT pSIMD, const fltx4 & a ) +{ + _mm_store_ps( reinterpret_cast(pSIMD), a ); +} + +FORCEINLINE void StoreAlignedIntSIMD( intx4 &pSIMD, const fltx4 & a ) +{ + _mm_store_ps( reinterpret_cast(pSIMD.Base()), a ); +} + +FORCEINLINE void StoreUnalignedIntSIMD( int32 * RESTRICT pSIMD, const fltx4 & a ) +{ + _mm_storeu_ps( reinterpret_cast(pSIMD), a ); +} + +// a={ a.x, a.z, b.x, b.z } +// combine two fltx4s by throwing away every other field. +FORCEINLINE fltx4 CompressSIMD( fltx4 const & a, fltx4 const &b ) +{ + return _mm_shuffle_ps( a, b, MM_SHUFFLE_REV( 0, 2, 0, 2 ) ); +} + +// Load four consecutive uint16's, and turn them into floating point numbers. +// This function isn't especially fast and could be made faster if anyone is +// using it heavily. +FORCEINLINE fltx4 LoadAndConvertUint16SIMD( const uint16 *pInts ) +{ +#ifdef POSIX + fltx4 retval; + SubFloat( retval, 0 ) = pInts[0]; + SubFloat( retval, 1 ) = pInts[1]; + SubFloat( retval, 2 ) = pInts[2]; + SubFloat( retval, 3 ) = pInts[3]; +#else + __m128i inA = _mm_loadl_epi64( (__m128i const*) pInts); // Load the lower 64 bits of the value pointed to by p into the lower 64 bits of the result, zeroing the upper 64 bits of the result. + inA = _mm_unpacklo_epi16( inA, _mm_setzero_si128() ); // unpack unsigned 16's to signed 32's + return _mm_cvtepi32_ps(inA); +#endif +} + + +// a={ a.x, b.x, c.x, d.x } +// combine 4 fltx4s by throwing away 3/4s of the fields +FORCEINLINE fltx4 Compress4SIMD( fltx4 const a, fltx4 const &b, fltx4 const &c, fltx4 const &d ) +{ + fltx4 aacc = _mm_shuffle_ps( a, c, MM_SHUFFLE_REV( 0, 0, 0, 0 ) ); + fltx4 bbdd = _mm_shuffle_ps( b, d, MM_SHUFFLE_REV( 0, 0, 0, 0 ) ); + return MaskedAssign( LoadAlignedSIMD( g_SIMD_EveryOtherMask ), bbdd, aacc ); +} + +// outa={a.x, a.x, a.y, a.y}, outb = a.z, a.z, a.w, a.w } +FORCEINLINE void ExpandSIMD( fltx4 const &a, fltx4 &fl4OutA, fltx4 &fl4OutB ) +{ + fl4OutA = _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 0, 0, 1, 1 ) ); + fl4OutB = _mm_shuffle_ps( a, a, MM_SHUFFLE_REV( 2, 2, 3, 3 ) ); + +} + +// CHRISG: the conversion functions all seem to operate on m64's only... +// how do we make them work here? + +// Take a fltx4 containing fixed-point uints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 UnsignedIntConvertToFltSIMD( const u32x4 &vSrcA ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = ( (float) SubInt( retval, 0 ) ); + SubFloat( retval, 1 ) = ( (float) SubInt( retval, 1 ) ); + SubFloat( retval, 2 ) = ( (float) SubInt( retval, 2 ) ); + SubFloat( retval, 3 ) = ( (float) SubInt( retval, 3 ) ); + return retval; +} + + +// Take a fltx4 containing fixed-point sints and +// return them as single precision floats. No +// fixed point conversion is done. +FORCEINLINE fltx4 SignedIntConvertToFltSIMD( const i32x4 &vSrcA ) +{ + fltx4 retval; + SubFloat( retval, 0 ) = ( (float) (reinterpret_cast(&vSrcA)[0])); + SubFloat( retval, 1 ) = ( (float) (reinterpret_cast(&vSrcA)[1])); + SubFloat( retval, 2 ) = ( (float) (reinterpret_cast(&vSrcA)[2])); + SubFloat( retval, 3 ) = ( (float) (reinterpret_cast(&vSrcA)[3])); + return retval; +} + +/* + works on fltx4's as if they are four uints. + the first parameter contains the words to be shifted, + the second contains the amount to shift by AS INTS + + for i = 0 to 3 + shift = vSrcB_i*32:(i*32)+4 + vReturned_i*32:(i*32)+31 = vSrcA_i*32:(i*32)+31 << shift +*/ +FORCEINLINE i32x4 IntShiftLeftWordSIMD(const i32x4 &vSrcA, const i32x4 &vSrcB) +{ + i32x4 retval; + SubInt(retval, 0) = SubInt(vSrcA, 0) << SubInt(vSrcB, 0); + SubInt(retval, 1) = SubInt(vSrcA, 1) << SubInt(vSrcB, 1); + SubInt(retval, 2) = SubInt(vSrcA, 2) << SubInt(vSrcB, 2); + SubInt(retval, 3) = SubInt(vSrcA, 3) << SubInt(vSrcB, 3); + + + return retval; +} + + +// Fixed-point conversion and save as SIGNED INTS. +// pDest->x = Int (vSrc.x) +// note: some architectures have means of doing +// fixed point conversion when the fix depth is +// specified as an immediate.. but there is no way +// to guarantee an immediate as a parameter to function +// like this. +FORCEINLINE void ConvertStoreAsIntsSIMD(intx4 * RESTRICT pDest, const fltx4 &vSrc) +{ + __m64 bottom = _mm_cvttps_pi32( vSrc ); + __m64 top = _mm_cvttps_pi32( _mm_movehl_ps(vSrc,vSrc) ); + + *reinterpret_cast<__m64 *>(&(*pDest)[0]) = bottom; + *reinterpret_cast<__m64 *>(&(*pDest)[2]) = top; + + _mm_empty(); +} + + + +#endif + +// a={a.y, a.z, a.w, b.x } b={b.y, b.z, b.w, b.x } +FORCEINLINE void RotateLeftDoubleSIMD( fltx4 &a, fltx4 &b ) +{ + a = SetWSIMD( RotateLeft( a ), SplatXSIMD( b ) ); + b = RotateLeft( b ); +} + + +// // Some convenience operator overloads, which are just aliasing the functions above. +// Unneccessary on 360, as you already have them from xboxmath.h +#if !defined(_X360) && !defined( POSIX ) +#if 1 // TODO: verify generation of non-bad code. +// Componentwise add +FORCEINLINE fltx4 operator+( FLTX4 a, FLTX4 b ) +{ + return AddSIMD( a, b ); +} + +// Componentwise subtract +FORCEINLINE fltx4 operator-( FLTX4 a, FLTX4 b ) +{ + return SubSIMD( a, b ); +} + +// Componentwise multiply +FORCEINLINE fltx4 operator*( FLTX4 a, FLTX4 b ) +{ + return MulSIMD( a, b ); +} + +// No divide. You need to think carefully about whether you want a reciprocal +// or a reciprocal estimate. + +// bitwise and +FORCEINLINE fltx4 operator&( FLTX4 a, FLTX4 b ) +{ + return AndSIMD( a ,b ); +} + +// bitwise or +FORCEINLINE fltx4 operator|( FLTX4 a, FLTX4 b ) +{ + return OrSIMD( a, b ); +} + +// bitwise xor +FORCEINLINE fltx4 operator^( FLTX4 a, FLTX4 b ) +{ + return XorSIMD( a, b ); +} + +// unary negate +FORCEINLINE fltx4 operator-( FLTX4 a ) +{ + return NegSIMD( a ); +} +#endif // 0 +#endif + +struct ALIGN16 fourplanes_t +{ + fltx4 nX; + fltx4 nY; + fltx4 nZ; + fltx4 dist; + fltx4 xSign; + fltx4 ySign; + fltx4 zSign; + fltx4 nXAbs; + fltx4 nYAbs; + fltx4 nZAbs; + + void ComputeSignbits(); + + // fast SIMD loads + void Set4Planes( const VPlane *pPlanes ); + void Set2Planes( const VPlane *pPlanes ); + void Get4Planes( VPlane *pPlanesOut ); + void Get2Planes( VPlane *pPlanesOut ); + // not-SIMD, much slower + void GetPlane( int index, Vector *pNormal, float *pDist ) const; + void SetPlane( int index, const Vector &vecNormal, float planeDist ); +}; + +class ALIGN16 Frustum_t +{ +public: + Frustum_t(); + void SetPlane( int i, const Vector &vecNormal, float dist ); + void GetPlane( int i, Vector *pNormalOut, float *pDistOut ) const; + void SetPlanes( const VPlane *pPlanes ); + void GetPlanes( VPlane *pPlanesOut ); + // returns false if the box is within the frustum, true if it is outside + bool CullBox( const Vector &mins, const Vector &maxs ) const; + bool CullBoxCenterExtents( const Vector ¢er, const Vector &extents ) const; + + bool CullBox( const fltx4 &fl4Mins, const fltx4 &fl4Maxs ) const; + bool CullBoxCenterExtents( const fltx4 &fl4Center, const fltx4 &fl4Extents ) const; + + fourplanes_t planes[2]; +}; + +/// class FourVectors stores 4 independent vectors for use in SIMD processing. These vectors are +/// stored in the format x x x x y y y y z z z z so that they can be efficiently SIMD-accelerated. +class ALIGN16 FourVectors +{ +public: + fltx4 x, y, z; + + FourVectors(void) + { + } + + FourVectors( FourVectors const &src ) + { + x=src.x; + y=src.y; + z=src.z; + } + + FORCEINLINE FourVectors( float a ) + { + fltx4 aReplicated = ReplicateX4( a ); + x = y = z = aReplicated; + } + + /// construct a FourVectors from 4 separate Vectors + FORCEINLINE FourVectors(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + LoadAndSwizzle(a,b,c,d); + } + + /// construct a FourVectors from 4 separate Vectors + FORCEINLINE FourVectors(VectorAligned const &a, VectorAligned const &b, VectorAligned const &c, VectorAligned const &d) + { + LoadAndSwizzleAligned(a,b,c,d); + } + + // construct from twelve floats; really only useful for static const constructors. + // input arrays must be aligned, and in the fourvectors' native format + // (eg in xxxx,yyyy,zzzz form) + // each pointer should be to an aligned array of four floats + FORCEINLINE FourVectors( const float *xs , const float *ys, const float *zs ) : + x( LoadAlignedSIMD(xs) ), y( LoadAlignedSIMD(ys) ), z( LoadAlignedSIMD(zs) ) + {}; + + FORCEINLINE void DuplicateVector(Vector const &v) //< set all 4 vectors to the same vector value + { + x=ReplicateX4(v.x); + y=ReplicateX4(v.y); + z=ReplicateX4(v.z); + } + + FORCEINLINE fltx4 const & operator[](int idx) const + { + return *((&x)+idx); + } + + FORCEINLINE fltx4 & operator[](int idx) + { + return *((&x)+idx); + } + + FORCEINLINE void operator+=(FourVectors const &b) //< add 4 vectors to another 4 vectors + { + x=AddSIMD(x,b.x); + y=AddSIMD(y,b.y); + z=AddSIMD(z,b.z); + } + + FORCEINLINE FourVectors operator+(FourVectors const &b) //< add 4 vectors to another 4 vectors + { + FourVectors result; + result.x=AddSIMD(x,b.x); + result.y=AddSIMD(y,b.y); + result.z=AddSIMD(z,b.z); + return result; + } + + FORCEINLINE void operator-=(FourVectors const &b) //< subtract 4 vectors from another 4 + { + x=SubSIMD(x,b.x); + y=SubSIMD(y,b.y); + z=SubSIMD(z,b.z); + } + + FORCEINLINE FourVectors operator-(FourVectors const &b) //< add 4 vectors to another 4 vectors + { + FourVectors result; + result.x=SubSIMD(x,b.x); + result.y=SubSIMD(y,b.y); + result.z=SubSIMD(z,b.z); + return result; + } + + FORCEINLINE void operator*=(FourVectors const &b) //< scale all four vectors per component scale + { + x=MulSIMD(x,b.x); + y=MulSIMD(y,b.y); + z=MulSIMD(z,b.z); + } + + FORCEINLINE void operator*=(const fltx4 & scale) //< scale + { + x=MulSIMD(x,scale); + y=MulSIMD(y,scale); + z=MulSIMD(z,scale); + } + + FORCEINLINE void operator*=(float scale) //< uniformly scale all 4 vectors + { + fltx4 scalepacked = ReplicateX4(scale); + *this *= scalepacked; + } + + FORCEINLINE fltx4 operator*(FourVectors const &b) const //< 4 dot products + { + fltx4 dot=MulSIMD(x,b.x); + dot=MaddSIMD(y,b.y,dot); + dot=MaddSIMD(z,b.z,dot); + return dot; + } + + FORCEINLINE fltx4 operator*(Vector const &b) const //< dot product all 4 vectors with 1 vector + { + fltx4 dot=MulSIMD(x,ReplicateX4(b.x)); + dot=MaddSIMD(y,ReplicateX4(b.y), dot); + dot=MaddSIMD(z,ReplicateX4(b.z), dot); + return dot; + } + + FORCEINLINE FourVectors operator*(float b) const //< scale + { + fltx4 scalepacked = ReplicateX4( b ); + FourVectors res; + res.x = MulSIMD( x, scalepacked ); + res.y = MulSIMD( y, scalepacked ); + res.z = MulSIMD( z, scalepacked ); + return res; + } + + FORCEINLINE void VProduct(FourVectors const &b) //< component by component mul + { + x=MulSIMD(x,b.x); + y=MulSIMD(y,b.y); + z=MulSIMD(z,b.z); + } + FORCEINLINE void MakeReciprocal(void) //< (x,y,z)=(1/x,1/y,1/z) + { + x=ReciprocalSIMD(x); + y=ReciprocalSIMD(y); + z=ReciprocalSIMD(z); + } + + FORCEINLINE void MakeReciprocalSaturate(void) //< (x,y,z)=(1/x,1/y,1/z), 1/0=1.0e23 + { + x=ReciprocalSaturateSIMD(x); + y=ReciprocalSaturateSIMD(y); + z=ReciprocalSaturateSIMD(z); + } + + // Assume the given matrix is a rotation, and rotate these vectors by it. + // If you have a long list of FourVectors structures that you all want + // to rotate by the same matrix, use FourVectors::RotateManyBy() instead. + inline void RotateBy(const matrix3x4_t& matrix); + + /// You can use this to rotate a long array of FourVectors all by the same + /// matrix. The first parameter is the head of the array. The second is the + /// number of vectors to rotate. The third is the matrix. + static void RotateManyBy(FourVectors * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t& rotationMatrix ); + + static void RotateManyBy(FourVectors * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t& rotationMatrix, FourVectors * RESTRICT pOut ); + + /// Assume the vectors are points, and transform them in place by the matrix. + inline void TransformBy(const matrix3x4_t& matrix); + + /// You can use this to Transform a long array of FourVectors all by the same + /// matrix. The first parameter is the head of the array. The second is the + /// number of vectors to rotate. The third is the matrix. The fourth is the + /// output buffer, which must not overlap the pVectors buffer. This is not + /// an in-place transformation. + static void TransformManyBy(FourVectors * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t& rotationMatrix, FourVectors * RESTRICT pOut ); + + /// You can use this to Transform a long array of FourVectors all by the same + /// matrix. The first parameter is the head of the array. The second is the + /// number of vectors to rotate. The third is the matrix. The fourth is the + /// output buffer, which must not overlap the pVectors buffer. + /// This is an in-place transformation. + static void TransformManyBy(FourVectors * RESTRICT pVectors, unsigned int numVectors, const matrix3x4_t& rotationMatrix ); + + static void CalcClosestPointOnLineSIMD( const FourVectors &P, const FourVectors &vLineA, const FourVectors &vLineB, FourVectors &vClosest, fltx4 *outT = 0); + static fltx4 CalcClosestPointToLineTSIMD( const FourVectors &P, const FourVectors &vLineA, const FourVectors &vLineB, FourVectors &vDir ); + + // X(),Y(),Z() - get at the desired component of the i'th (0..3) vector. + FORCEINLINE const float & X(int idx) const + { + // NOTE: if the output goes into a register, this causes a Load-Hit-Store stall (don't mix fpu/vpu math!) + return SubFloat( (fltx4 &)x, idx ); + } + + FORCEINLINE const float & Y(int idx) const + { + return SubFloat( (fltx4 &)y, idx ); + } + + FORCEINLINE const float & Z(int idx) const + { + return SubFloat( (fltx4 &)z, idx ); + } + + FORCEINLINE float & X(int idx) + { + return SubFloat( x, idx ); + } + + FORCEINLINE float & Y(int idx) + { + return SubFloat( y, idx ); + } + + FORCEINLINE float & Z(int idx) + { + return SubFloat( z, idx ); + } + + FORCEINLINE Vector Vec(int idx) const //< unpack one of the vectors + { + return Vector( X(idx), Y(idx), Z(idx) ); + } + + FORCEINLINE void operator=( FourVectors const &src ) + { + x=src.x; + y=src.y; + z=src.z; + } + + /// LoadAndSwizzle - load 4 Vectors into a FourVectors, performing transpose op + FORCEINLINE void LoadAndSwizzle(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + // TransposeSIMD has large sub-expressions that the compiler can't eliminate on x360 + // use an unfolded implementation here +#if _X360 + fltx4 tx = LoadUnalignedSIMD( &a.x ); + fltx4 ty = LoadUnalignedSIMD( &b.x ); + fltx4 tz = LoadUnalignedSIMD( &c.x ); + fltx4 tw = LoadUnalignedSIMD( &d.x ); + fltx4 r0 = __vmrghw(tx, tz); + fltx4 r1 = __vmrghw(ty, tw); + fltx4 r2 = __vmrglw(tx, tz); + fltx4 r3 = __vmrglw(ty, tw); + + x = __vmrghw(r0, r1); + y = __vmrglw(r0, r1); + z = __vmrghw(r2, r3); +#else + x = LoadUnalignedSIMD( &( a.x )); + y = LoadUnalignedSIMD( &( b.x )); + z = LoadUnalignedSIMD( &( c.x )); + fltx4 w = LoadUnalignedSIMD( &( d.x )); + // now, matrix is: + // x y z ? + // x y z ? + // x y z ? + // x y z ? + TransposeSIMD(x, y, z, w); +#endif + } + + FORCEINLINE void LoadAndSwizzle(Vector const &a) + { + LoadAndSwizzle( a, a, a, a ); + } + + // Broadcasts a, b, c, and d into the four vectors + // This is only performant if the floats are ALREADY IN MEMORY + // and not on registers -- eg, + // .Load( &fltArrray[0], &fltArrray[1], &fltArrray[2], &fltArrray[3] ) is okay, + // .Load( fltArrray[0] * 0.5f, fltArrray[1] * 0.5f, fltArrray[2] * 0.5f, fltArrray[3] * 0.5f ) is not. + FORCEINLINE void Load( const float &a, const float &b, const float &c, const float &d ) + { +#if _X360 + fltx4 temp[4]; + temp[0] = LoadUnalignedFloatSIMD( &a ); + temp[1] = LoadUnalignedFloatSIMD( &b ); + temp[2] = LoadUnalignedFloatSIMD( &c ); + temp[3] = LoadUnalignedFloatSIMD( &d ); + y = __vmrghw( temp[0], temp[2] ); // ac__ + z = __vmrghw( temp[1], temp[3] ); // bd__ + + x = __vmrghw( y, z ); // abcd + y = x; + z = x; +#else + ALIGN16 float temp[4]; + temp[0] = a; temp[1] = b; temp[2] = c; temp[3] = d; + fltx4 v = LoadAlignedSIMD( temp ); + x = v; + y = v; + z = v; +#endif + } + + // transform four horizontal vectors into the internal vertical ones + FORCEINLINE void LoadAndSwizzle( FLTX4 a, FLTX4 b, FLTX4 c, FLTX4 d ) + { +#if _X360 + fltx4 tx = a; + fltx4 ty = b; + fltx4 tz = c; + fltx4 tw = d; + fltx4 r0 = __vmrghw(tx, tz); + fltx4 r1 = __vmrghw(ty, tw); + fltx4 r2 = __vmrglw(tx, tz); + fltx4 r3 = __vmrglw(ty, tw); + + x = __vmrghw(r0, r1); + y = __vmrglw(r0, r1); + z = __vmrghw(r2, r3); +#else + x = a; + y = b; + z = c; + fltx4 w = d; + // now, matrix is: + // x y z ? + // x y z ? + // x y z ? + // x y z ? + TransposeSIMD(x, y, z, w); +#endif + } + + /// LoadAndSwizzleAligned - load 4 Vectors into a FourVectors, performing transpose op. + /// all 4 vectors must be 128 bit boundary + FORCEINLINE void LoadAndSwizzleAligned(const float *RESTRICT a, const float *RESTRICT b, const float *RESTRICT c, const float *RESTRICT d) + { +#if _X360 + fltx4 tx = LoadAlignedSIMD(a); + fltx4 ty = LoadAlignedSIMD(b); + fltx4 tz = LoadAlignedSIMD(c); + fltx4 tw = LoadAlignedSIMD(d); + fltx4 r0 = __vmrghw(tx, tz); + fltx4 r1 = __vmrghw(ty, tw); + fltx4 r2 = __vmrglw(tx, tz); + fltx4 r3 = __vmrglw(ty, tw); + + x = __vmrghw(r0, r1); + y = __vmrglw(r0, r1); + z = __vmrghw(r2, r3); +#else + x = LoadAlignedSIMD( a ); + y = LoadAlignedSIMD( b ); + z = LoadAlignedSIMD( c ); + fltx4 w = LoadAlignedSIMD( d ); + // now, matrix is: + // x y z ? + // x y z ? + // x y z ? + // x y z ? + TransposeSIMD( x, y, z, w ); +#endif + } + + FORCEINLINE void LoadAndSwizzleAligned(Vector const &a, Vector const &b, Vector const &c, Vector const &d) + { + LoadAndSwizzleAligned( &a.x, &b.x, &c.x, &d.x ); + } + + /// Unpack a FourVectors back into four horizontal fltx4s. + /// Since the FourVectors doesn't store a w row, you can optionally + /// specify your own; otherwise it will be 0. + /// This function ABSOLUTELY MUST be inlined or the reference parameters will + /// induce a severe load-hit-store. + FORCEINLINE void TransposeOnto( fltx4 &out0, fltx4 &out1, fltx4 &out2, fltx4 &out3, FLTX4 w = Four_Zeros ) const + { + // TransposeSIMD has large sub-expressions that the compiler can't eliminate on x360 + // use an unfolded implementation here +#if _X360 + fltx4 r0 = __vmrghw(x, z); + fltx4 r1 = __vmrghw(y, w); + fltx4 r2 = __vmrglw(x, z); + fltx4 r3 = __vmrglw(y, w); + + out0 = __vmrghw(r0, r1); + out1 = __vmrglw(r0, r1); + out2 = __vmrghw(r2, r3); + out3 = __vmrglw(r2, r3); +#else + out0 = x; + out1 = y; + out2 = z; + out3 = w; + + TransposeSIMD(out0, out1, out2, out3); +#endif + } + + /// Store a FourVectors into four NON-CONTIGUOUS Vector*'s. + FORCEINLINE void StoreUnalignedVector3SIMD( Vector * RESTRICT out0, Vector * RESTRICT out1, Vector * RESTRICT out2, Vector * RESTRICT out3 ) const; + + /// Store a FourVectors into four NON-CONTIGUOUS VectorAligned s. + FORCEINLINE void StoreAlignedVectorSIMD( VectorAligned * RESTRICT out0, VectorAligned * RESTRICT out1, VectorAligned * RESTRICT out2, VectorAligned * RESTRICT out3 ) const; + + /// Store a FourVectors into four CONSECUTIVE Vectors in memory, + /// where the first vector IS NOT aligned on a 16-byte boundary. + FORCEINLINE void StoreUnalignedContigVector3SIMD( Vector * RESTRICT pDestination ) + { + fltx4 a,b,c,d; + TransposeOnto(a,b,c,d); + StoreFourUnalignedVector3SIMD( a, b, c, d, pDestination ); + } + + /// Store a FourVectors into four CONSECUTIVE Vectors in memory, + /// where the first vector IS aligned on a 16-byte boundary. + /// (since four Vector3s = 48 bytes, groups of four can be said + /// to be 16-byte aligned though obviously the 2nd, 3d, and 4th + /// vectors in the group individually are not) + FORCEINLINE void StoreAlignedContigVector3SIMD( Vector * RESTRICT pDestination ) + { + fltx4 a,b,c,d; + TransposeOnto(a,b,c,d); + StoreFourAlignedVector3SIMD( a, b, c, d, pDestination ); + } + + /// Store a FourVectors into four CONSECUTIVE VectorAligneds in memory + FORCEINLINE void StoreAlignedContigVectorASIMD( VectorAligned * RESTRICT pDestination ) + { + StoreAlignedVectorSIMD( pDestination, pDestination + 1, pDestination + 2, pDestination + 3 ); + } + + /// return the squared length of all 4 vectors + FORCEINLINE fltx4 length2(void) const + { + return (*this)*(*this); + } + + /// return the approximate length of all 4 vectors. uses the sqrt approximation instruction + FORCEINLINE fltx4 length(void) const + { + return SqrtEstSIMD(length2()); + } + + /// normalize all 4 vectors in place. not mega-accurate (uses reciprocal approximation instruction) + FORCEINLINE void VectorNormalizeFast(void) + { + fltx4 mag_sq=(*this)*(*this); // length^2 + (*this) *= ReciprocalSqrtEstSIMD(mag_sq); // *(1.0/sqrt(length^2)) + } + + /// normalize all 4 vectors in place. + FORCEINLINE void VectorNormalize(void) + { + fltx4 mag_sq=(*this)*(*this); // length^2 + (*this) *= ReciprocalSqrtSIMD(mag_sq); // *(1.0/sqrt(length^2)) + } + + FORCEINLINE fltx4 DistToSqr( FourVectors const &pnt ) + { + fltx4 fl4dX = SubSIMD( pnt.x, x ); + fltx4 fl4dY = SubSIMD( pnt.y, y ); + fltx4 fl4dZ = SubSIMD( pnt.z, z ); + return AddSIMD( MulSIMD( fl4dX, fl4dX), AddSIMD( MulSIMD( fl4dY, fl4dY ), MulSIMD( fl4dZ, fl4dZ ) ) ); + + } + + FORCEINLINE fltx4 TValueOfClosestPointOnLine( FourVectors const &p0, FourVectors const &p1 ) const + { + FourVectors lineDelta = p1; + lineDelta -= p0; + fltx4 OOlineDirDotlineDir = ReciprocalSIMD( p1 * p1 ); + FourVectors v4OurPnt = *this; + v4OurPnt -= p0; + return MulSIMD( OOlineDirDotlineDir, v4OurPnt * lineDelta ); + } + + FORCEINLINE fltx4 DistSqrToLineSegment( FourVectors const &p0, FourVectors const &p1 ) const + { + FourVectors lineDelta = p1; + FourVectors v4OurPnt = *this; + v4OurPnt -= p0; + lineDelta -= p0; + + fltx4 OOlineDirDotlineDir = ReciprocalSIMD( lineDelta * lineDelta ); + + fltx4 fl4T = MulSIMD( OOlineDirDotlineDir, v4OurPnt * lineDelta ); + + fl4T = MinSIMD( fl4T, Four_Ones ); + fl4T = MaxSIMD( fl4T, Four_Zeros ); + lineDelta *= fl4T; + return v4OurPnt.DistToSqr( lineDelta ); + } +}; + +// +inline FourVectors Mul( const FourVectors &a, const fltx4 &b ) +{ + FourVectors ret; + ret.x = MulSIMD( a.x, b ); + ret.y = MulSIMD( a.y, b ); + ret.z = MulSIMD( a.z, b ); + return ret; +} + +inline FourVectors Mul( const FourVectors &a, const FourVectors &b ) +{ + FourVectors ret; + ret.x = MulSIMD( a.x, b.x ); + ret.y = MulSIMD( a.y, b.y ); + ret.z = MulSIMD( a.z, b.z ); + return ret; +} + +inline FourVectors Madd( const FourVectors &a, const fltx4 &b, const FourVectors &c ) // a*b + c +{ + FourVectors ret; + ret.x = MaddSIMD( a.x, b, c.x ); + ret.y = MaddSIMD( a.y, b, c.y ); + ret.z = MaddSIMD( a.z, b, c.z ); + return ret; +} + +/// form 4 cross products +inline FourVectors operator ^(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=SubSIMD(MulSIMD(a.y,b.z),MulSIMD(a.z,b.y)); + ret.y=SubSIMD(MulSIMD(a.z,b.x),MulSIMD(a.x,b.z)); + ret.z=SubSIMD(MulSIMD(a.x,b.y),MulSIMD(a.y,b.x)); + return ret; +} + +inline FourVectors operator-(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=SubSIMD(a.x,b.x); + ret.y=SubSIMD(a.y,b.y); + ret.z=SubSIMD(a.z,b.z); + return ret; +} + +/// component-by-componentwise MAX operator +inline FourVectors maximum(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=MaxSIMD(a.x,b.x); + ret.y=MaxSIMD(a.y,b.y); + ret.z=MaxSIMD(a.z,b.z); + return ret; +} + +/// component-by-componentwise MIN operator +inline FourVectors minimum(const FourVectors &a, const FourVectors &b) +{ + FourVectors ret; + ret.x=MinSIMD(a.x,b.x); + ret.y=MinSIMD(a.y,b.y); + ret.z=MinSIMD(a.z,b.z); + return ret; +} + +FORCEINLINE FourVectors RotateLeft( const FourVectors &src ) +{ + FourVectors ret; + ret.x = RotateLeft( src.x ); + ret.y = RotateLeft( src.y ); + ret.z = RotateLeft( src.z ); + return ret; +} + +FORCEINLINE FourVectors RotateRight( const FourVectors &src ) +{ + FourVectors ret; + ret.x = RotateRight( src.x ); + ret.y = RotateRight( src.y ); + ret.z = RotateRight( src.z ); + return ret; +} +FORCEINLINE FourVectors MaskedAssign( const fltx4 & ReplacementMask, const FourVectors & NewValue, const FourVectors & OldValue ) +{ + FourVectors ret; + ret.x = MaskedAssign( ReplacementMask, NewValue.x, OldValue.x ); + ret.y = MaskedAssign( ReplacementMask, NewValue.y, OldValue.y ); + ret.z = MaskedAssign( ReplacementMask, NewValue.z, OldValue.z ); + return ret; +} + +/// calculate reflection vector. incident and normal dir assumed normalized +FORCEINLINE FourVectors VectorReflect( const FourVectors &incident, const FourVectors &normal ) +{ + FourVectors ret = incident; + fltx4 iDotNx2 = incident * normal; + iDotNx2 = AddSIMD( iDotNx2, iDotNx2 ); + FourVectors nPart = normal; + nPart *= iDotNx2; + ret -= nPart; // i-2(n*i)n + return ret; +} + +/// calculate slide vector. removes all components of a vector which are perpendicular to a normal vector. +FORCEINLINE FourVectors VectorSlide( const FourVectors &incident, const FourVectors &normal ) +{ + FourVectors ret = incident; + fltx4 iDotN = incident * normal; + FourVectors nPart = normal; + nPart *= iDotN; + ret -= nPart; // i-(n*i)n + return ret; +} + +/// normalize all 4 vectors in place. not mega-accurate (uses reciprocal approximation instruction) +FORCEINLINE FourVectors VectorNormalizeFast( const FourVectors &src ) +{ + fltx4 mag_sq = ReciprocalSqrtEstSIMD( src * src ); // *(1.0/sqrt(length^2)) + FourVectors result; + result.x = MulSIMD( src.x, mag_sq ); + result.y = MulSIMD( src.y, mag_sq ); + result.z = MulSIMD( src.z, mag_sq ); + return result; +} + +/// Store a FourVectors into four NON-CONTIGUOUS Vector*'s. +FORCEINLINE void FourVectors::StoreUnalignedVector3SIMD( Vector * RESTRICT out0, Vector * RESTRICT out1, Vector * RESTRICT out2, Vector * RESTRICT out3 ) const +{ +#ifdef _X360 + fltx4 x0,x1,x2,x3, y0,y1,y2,y3, z0,z1,z2,z3; + x0 = SplatXSIMD(x); // all x0x0x0x0 + x1 = SplatYSIMD(x); + x2 = SplatZSIMD(x); + x3 = SplatWSIMD(x); + + y0 = SplatXSIMD(y); + y1 = SplatYSIMD(y); + y2 = SplatZSIMD(y); + y3 = SplatWSIMD(y); + + z0 = SplatXSIMD(z); + z1 = SplatYSIMD(z); + z2 = SplatZSIMD(z); + z3 = SplatWSIMD(z); + + __stvewx( x0, out0->Base(), 0 ); // store X word + __stvewx( y0, out0->Base(), 4 ); // store Y word + __stvewx( z0, out0->Base(), 8 ); // store Z word + + __stvewx( x1, out1->Base(), 0 ); // store X word + __stvewx( y1, out1->Base(), 4 ); // store Y word + __stvewx( z1, out1->Base(), 8 ); // store Z word + + __stvewx( x2, out2->Base(), 0 ); // store X word + __stvewx( y2, out2->Base(), 4 ); // store Y word + __stvewx( z2, out2->Base(), 8 ); // store Z word + + __stvewx( x3, out3->Base(), 0 ); // store X word + __stvewx( y3, out3->Base(), 4 ); // store Y word + __stvewx( z3, out3->Base(), 8 ); // store Z word +#else + fltx4 a,b,c,d; + TransposeOnto(a,b,c,d); + StoreUnaligned3SIMD( out0->Base(), a ); + StoreUnaligned3SIMD( out1->Base(), b ); + StoreUnaligned3SIMD( out2->Base(), c ); + StoreUnaligned3SIMD( out3->Base(), d ); +#endif +} + +/// Store a FourVectors into four NON-CONTIGUOUS VectorAligned s. +FORCEINLINE void FourVectors::StoreAlignedVectorSIMD( VectorAligned * RESTRICT out0, VectorAligned * RESTRICT out1, VectorAligned * RESTRICT out2, VectorAligned * RESTRICT out3 ) const +{ + fltx4 a,b,c,d; + TransposeOnto(a,b,c,d); + StoreAligned3SIMD( out0, a ); + StoreAligned3SIMD( out1, b ); + StoreAligned3SIMD( out2, c ); + StoreAligned3SIMD( out3, d ); + +} + + +// Assume the given matrix is a rotation, and rotate these vectors by it. +// If you have a long list of FourVectors structures that you all want +// to rotate by the same matrix, use FourVectors::RotateManyBy() instead. +void FourVectors::RotateBy(const matrix3x4_t& matrix) +{ + // Splat out each of the entries in the matrix to a fltx4. Do this + // in the order that we will need them, to hide latency. I'm + // avoiding making an array of them, so that they'll remain in + // registers. + fltx4 matSplat00, matSplat01, matSplat02, + matSplat10, matSplat11, matSplat12, + matSplat20, matSplat21, matSplat22; + + // Load the matrix into local vectors. Sadly, matrix3x4_ts are + // often unaligned. The w components will be the tranpose row of + // the matrix, but we don't really care about that. + fltx4 matCol0 = LoadUnalignedSIMD( matrix[0] ); + fltx4 matCol1 = LoadUnalignedSIMD( matrix[1] ); + fltx4 matCol2 = LoadUnalignedSIMD( matrix[2] ); + + matSplat00 = SplatXSIMD( matCol0 ); + matSplat01 = SplatYSIMD( matCol0 ); + matSplat02 = SplatZSIMD( matCol0 ); + + matSplat10 = SplatXSIMD( matCol1 ); + matSplat11 = SplatYSIMD( matCol1 ); + matSplat12 = SplatZSIMD( matCol1 ); + + matSplat20 = SplatXSIMD( matCol2 ); + matSplat21 = SplatYSIMD( matCol2 ); + matSplat22 = SplatZSIMD( matCol2 ); + + // Trust in the compiler to schedule these operations correctly: + fltx4 outX, outY, outZ; + outX = AddSIMD( AddSIMD( MulSIMD( x, matSplat00 ), MulSIMD( y, matSplat01 ) ), MulSIMD( z, matSplat02 ) ); + outY = AddSIMD( AddSIMD( MulSIMD( x, matSplat10 ), MulSIMD( y, matSplat11 ) ), MulSIMD( z, matSplat12 ) ); + outZ = AddSIMD( AddSIMD( MulSIMD( x, matSplat20 ), MulSIMD( y, matSplat21 ) ), MulSIMD( z, matSplat22 ) ); + + x = outX; + y = outY; + z = outZ; +} + + +// Assume the given matrix is a rotation, and rotate these vectors by it. +// If you have a long list of FourVectors structures that you all want +// to rotate by the same matrix, use FourVectors::RotateManyBy() instead. +void FourVectors::TransformBy(const matrix3x4_t& matrix) +{ + // Splat out each of the entries in the matrix to a fltx4. Do this + // in the order that we will need them, to hide latency. I'm + // avoiding making an array of them, so that they'll remain in + // registers. + fltx4 matSplat00, matSplat01, matSplat02, + matSplat10, matSplat11, matSplat12, + matSplat20, matSplat21, matSplat22; + + // Load the matrix into local vectors. Sadly, matrix3x4_ts are + // often unaligned. The w components will be the tranpose row of + // the matrix, but we don't really care about that. + fltx4 matCol0 = LoadUnalignedSIMD( matrix[0] ); + fltx4 matCol1 = LoadUnalignedSIMD( matrix[1] ); + fltx4 matCol2 = LoadUnalignedSIMD( matrix[2] ); + + matSplat00 = SplatXSIMD( matCol0 ); + matSplat01 = SplatYSIMD( matCol0 ); + matSplat02 = SplatZSIMD( matCol0 ); + + matSplat10 = SplatXSIMD( matCol1 ); + matSplat11 = SplatYSIMD( matCol1 ); + matSplat12 = SplatZSIMD( matCol1 ); + + matSplat20 = SplatXSIMD( matCol2 ); + matSplat21 = SplatYSIMD( matCol2 ); + matSplat22 = SplatZSIMD( matCol2 ); + + // Trust in the compiler to schedule these operations correctly: + fltx4 outX, outY, outZ; + + outX = MaddSIMD( z, matSplat02, AddSIMD( MulSIMD( x, matSplat00 ), MulSIMD( y, matSplat01 ) ) ); + outY = MaddSIMD( z, matSplat12, AddSIMD( MulSIMD( x, matSplat10 ), MulSIMD( y, matSplat11 ) ) ); + outZ = MaddSIMD( z, matSplat22, AddSIMD( MulSIMD( x, matSplat20 ), MulSIMD( y, matSplat21 ) ) ); + + x = AddSIMD( outX, ReplicateX4( matrix[0][3] )); + y = AddSIMD( outY, ReplicateX4( matrix[1][3] )); + z = AddSIMD( outZ, ReplicateX4( matrix[2][3] )); +} + + +/// quick, low quality perlin-style noise() function suitable for real time use. +/// return value is -1..1. Only reliable around +/- 1 million or so. +fltx4 NoiseSIMD( const fltx4 & x, const fltx4 & y, const fltx4 & z ); +fltx4 NoiseSIMD( FourVectors const &v ); + +// vector valued noise direction +FourVectors DNoiseSIMD( FourVectors const &v ); + +// vector value "curl" noise function. see http://hyperphysics.phy-astr.gsu.edu/hbase/curl.html +FourVectors CurlNoiseSIMD( FourVectors const &v ); + + +/// calculate the absolute value of a packed single +inline fltx4 fabs( const fltx4 & x ) +{ + return AndSIMD( x, LoadAlignedSIMD( g_SIMD_clear_signmask ) ); +} + +/// negate all four components of a SIMD packed single +inline fltx4 fnegate( const fltx4 & x ) +{ + return XorSIMD( x, LoadAlignedSIMD( g_SIMD_signmask ) ); +} + + +fltx4 Pow_FixedPoint_Exponent_SIMD( const fltx4 & x, int exponent); + +// PowSIMD - raise a SIMD register to a power. This is analogous to the C pow() function, with some +// restictions: fractional exponents are only handled with 2 bits of precision. Basically, +// fractions of 0,.25,.5, and .75 are handled. PowSIMD(x,.30) will be the same as PowSIMD(x,.25). +// negative and fractional powers are handled by the SIMD reciprocal and square root approximation +// instructions and so are not especially accurate ----Note that this routine does not raise +// numeric exceptions because it uses SIMD--- This routine is O(log2(exponent)). +inline fltx4 PowSIMD( const fltx4 & x, float exponent ) +{ + return Pow_FixedPoint_Exponent_SIMD(x,(int) (4.0*exponent)); +} + + + +// random number generation - generate 4 random numbers quickly. + +void SeedRandSIMD(uint32 seed); // seed the random # generator +fltx4 RandSIMD( int nContext = 0 ); // return 4 numbers in the 0..1 range + +// for multithreaded, you need to use these and use the argument form of RandSIMD: +int GetSIMDRandContext( void ); +void ReleaseSIMDRandContext( int nContext ); + +FORCEINLINE fltx4 RandSignedSIMD( void ) // -1..1 +{ + return SubSIMD( MulSIMD( Four_Twos, RandSIMD() ), Four_Ones ); +} + + +FORCEINLINE fltx4 LerpSIMD ( const fltx4 &percent, const fltx4 &a, const fltx4 &b) +{ + return AddSIMD( a, MulSIMD( SubSIMD( b, a ), percent ) ); +} + +FORCEINLINE fltx4 RemapValClampedSIMD(const fltx4 &val, const fltx4 &a, const fltx4 &b, const fltx4 &c, const fltx4 &d) // Remap val from clamped range between a and b to new range between c and d +{ + fltx4 range = MaskedAssign( CmpEqSIMD( a, b ), Four_Ones, SubSIMD( b, a ) ); //make sure range > 0 + fltx4 cVal = MaxSIMD( Four_Zeros, MinSIMD( Four_Ones, DivSIMD( SubSIMD( val, a ), range ) ) ); //saturate + return LerpSIMD( cVal, c, d ); +} + +// SIMD versions of mathlib simplespline functions +// hermite basis function for smooth interpolation +// Similar to Gain() above, but very cheap to call +// value should be between 0 & 1 inclusive +inline fltx4 SimpleSpline( const fltx4 & value ) +{ + // Arranged to avoid a data dependency between these two MULs: + fltx4 valueDoubled = MulSIMD( value, Four_Twos ); + fltx4 valueSquared = MulSIMD( value, value ); + + // Nice little ease-in, ease-out spline-like curve + return SubSIMD( + MulSIMD( Four_Threes, valueSquared ), + MulSIMD( valueDoubled, valueSquared ) ); +} + +// remaps a value in [startInterval, startInterval+rangeInterval] from linear to +// spline using SimpleSpline +inline fltx4 SimpleSplineRemapValWithDeltas( const fltx4 & val, + const fltx4 & A, const fltx4 & BMinusA, + const fltx4 & OneOverBMinusA, const fltx4 & C, + const fltx4 & DMinusC ) +{ +// if ( A == B ) +// return val >= B ? D : C; + fltx4 cVal = MulSIMD( SubSIMD( val, A), OneOverBMinusA ); + return AddSIMD( C, MulSIMD( DMinusC, SimpleSpline( cVal ) ) ); +} + +inline fltx4 SimpleSplineRemapValWithDeltasClamped( const fltx4 & val, + const fltx4 & A, const fltx4 & BMinusA, + const fltx4 & OneOverBMinusA, const fltx4 & C, + const fltx4 & DMinusC ) +{ +// if ( A == B ) +// return val >= B ? D : C; + fltx4 cVal = MulSIMD( SubSIMD( val, A), OneOverBMinusA ); + cVal = MinSIMD( Four_Ones, MaxSIMD( Four_Zeros, cVal ) ); + return AddSIMD( C, MulSIMD( DMinusC, SimpleSpline( cVal ) ) ); +} + +FORCEINLINE fltx4 FracSIMD( const fltx4 &val ) +{ + fltx4 fl4Abs = fabs( val ); + fltx4 ival = SubSIMD( AddSIMD( fl4Abs, Four_2ToThe23s ), Four_2ToThe23s ); + ival = MaskedAssign( CmpGtSIMD( ival, fl4Abs ), SubSIMD( ival, Four_Ones ), ival ); + return XorSIMD( SubSIMD( fl4Abs, ival ), XorSIMD( val, fl4Abs ) ); // restore sign bits +} + +FORCEINLINE fltx4 Mod2SIMD( const fltx4 &val ) +{ + fltx4 fl4Abs = fabs( val ); + fltx4 ival = SubSIMD( AndSIMD( LoadAlignedSIMD( (float *) g_SIMD_lsbmask ), AddSIMD( fl4Abs, Four_2ToThe23s ) ), Four_2ToThe23s ); + ival = MaskedAssign( CmpGtSIMD( ival, fl4Abs ), SubSIMD( ival, Four_Twos ), ival ); + return XorSIMD( SubSIMD( fl4Abs, ival ), XorSIMD( val, fl4Abs ) ); // restore sign bits +} + +FORCEINLINE fltx4 Mod2SIMDPositiveInput( const fltx4 &val ) +{ + fltx4 ival = SubSIMD( AndSIMD( LoadAlignedSIMD( g_SIMD_lsbmask ), AddSIMD( val, Four_2ToThe23s ) ), Four_2ToThe23s ); + ival = MaskedAssign( CmpGtSIMD( ival, val ), SubSIMD( ival, Four_Twos ), ival ); + return SubSIMD( val, ival ); +} + + +// approximate sin of an angle, with -1..1 representing the whole sin wave period instead of -pi..pi. +// no range reduction is done - for values outside of 0..1 you won't like the results +FORCEINLINE fltx4 _SinEst01SIMD( const fltx4 &val ) +{ + // really rough approximation - x*(4-x*4) - a parabola. s(0) = 0, s(.5) = 1, s(1)=0, smooth in-between. + // sufficient for simple oscillation. + return MulSIMD( val, SubSIMD( Four_Fours, MulSIMD( val, Four_Fours ) ) ); +} + +FORCEINLINE fltx4 _Sin01SIMD( const fltx4 &val ) +{ + // not a bad approximation : parabola always over-estimates. Squared parabola always + // underestimates. So lets blend between them: goodsin = badsin + .225*( badsin^2-badsin) + fltx4 fl4BadEst = MulSIMD( val, SubSIMD( Four_Fours, MulSIMD( val, Four_Fours ) ) ); + return AddSIMD( MulSIMD( Four_Point225s, SubSIMD( MulSIMD( fl4BadEst, fl4BadEst ), fl4BadEst ) ), fl4BadEst ); +} + +// full range useable implementations +FORCEINLINE fltx4 SinEst01SIMD( const fltx4 &val ) +{ + fltx4 fl4Abs = fabs( val ); + fltx4 fl4Reduced2 = Mod2SIMDPositiveInput( fl4Abs ); + fltx4 fl4OddMask = CmpGeSIMD( fl4Reduced2, Four_Ones ); + fltx4 fl4val = SubSIMD( fl4Reduced2, AndSIMD( Four_Ones, fl4OddMask ) ); + fltx4 fl4Sin = _SinEst01SIMD( fl4val ); + fl4Sin = XorSIMD( fl4Sin, AndSIMD( LoadAlignedSIMD( g_SIMD_signmask ), XorSIMD( val, fl4OddMask ) ) ); + return fl4Sin; + +} + +FORCEINLINE fltx4 Sin01SIMD( const fltx4 &val ) +{ + fltx4 fl4Abs = fabs( val ); + fltx4 fl4Reduced2 = Mod2SIMDPositiveInput( fl4Abs ); + fltx4 fl4OddMask = CmpGeSIMD( fl4Reduced2, Four_Ones ); + fltx4 fl4val = SubSIMD( fl4Reduced2, AndSIMD( Four_Ones, fl4OddMask ) ); + fltx4 fl4Sin = _Sin01SIMD( fl4val ); + fl4Sin = XorSIMD( fl4Sin, AndSIMD( LoadAlignedSIMD( g_SIMD_signmask ), XorSIMD( val, fl4OddMask ) ) ); + return fl4Sin; + +} + +FORCEINLINE fltx4 NatExpSIMD( const fltx4 &val ) // why is ExpSimd( x ) defined to be 2^x? +{ + // need to write this. just stub with normal float implementation for now + fltx4 fl4Result; + SubFloat( fl4Result, 0 ) = exp( SubFloat( val, 0 ) ); + SubFloat( fl4Result, 1 ) = exp( SubFloat( val, 1 ) ); + SubFloat( fl4Result, 2 ) = exp( SubFloat( val, 2 ) ); + SubFloat( fl4Result, 3 ) = exp( SubFloat( val, 3 ) ); + return fl4Result; +} + +// Schlick style Bias approximation see graphics gems 4 : bias(t,a)= t/( (1/a-2)*(1-t)+1) + +FORCEINLINE fltx4 PreCalcBiasParameter( const fltx4 &bias_parameter ) +{ + // convert perlin-style-bias parameter to the value right for the approximation + return SubSIMD( ReciprocalSIMD( bias_parameter ), Four_Twos ); +} + +FORCEINLINE fltx4 BiasSIMD( const fltx4 &val, const fltx4 &precalc_param ) +{ + // similar to bias function except pass precalced bias value from calling PreCalcBiasParameter. + + //!!speed!! use reciprocal est? + //!!speed!! could save one op by precalcing _2_ values + return DivSIMD( val, AddSIMD( MulSIMD( precalc_param, SubSIMD( Four_Ones, val ) ), Four_Ones ) ); +} + +//----------------------------------------------------------------------------- +// Box/plane test +// NOTE: The w component of emins + emaxs must be 1 for this to work +//----------------------------------------------------------------------------- +FORCEINLINE int BoxOnPlaneSideSIMD( const fltx4& emins, const fltx4& emaxs, const cplane_t *p, float tolerance = 0.f ) +{ + fltx4 corners[2]; + fltx4 normal = LoadUnalignedSIMD( p->normal.Base() ); + fltx4 dist = ReplicateX4( -p->dist ); + normal = SetWSIMD( normal, dist ); + fltx4 t4 = ReplicateX4( tolerance ); + fltx4 negt4 = ReplicateX4( -tolerance ); + fltx4 cmp = CmpGeSIMD( normal, Four_Zeros ); + corners[0] = MaskedAssign( cmp, emaxs, emins ); + corners[1] = MaskedAssign( cmp, emins, emaxs ); + fltx4 dot1 = Dot4SIMD( normal, corners[0] ); + fltx4 dot2 = Dot4SIMD( normal, corners[1] ); + cmp = CmpGeSIMD( dot1, t4 ); + fltx4 cmp2 = CmpGtSIMD( negt4, dot2 ); + fltx4 result = MaskedAssign( cmp, Four_Ones, Four_Zeros ); + fltx4 result2 = MaskedAssign( cmp2, Four_Twos, Four_Zeros ); + result = AddSIMD( result, result2 ); + intx4 sides; + ConvertStoreAsIntsSIMD( &sides, result ); + return sides[0]; +} + + +// k-dop bounding volume. 26-dop bounds with 13 plane-pairs plus 3 other "arbitrary bounds". The arbitrary values could be used to hold type info, etc, +// which can compare against "for free" +class KDop32_t +{ +public: + fltx4 m_Mins[4]; + fltx4 m_Maxes[4]; + + FORCEINLINE bool Intersects( KDop32_t const &other ) const; + + FORCEINLINE void operator|=( KDop32_t const & other ); + + FORCEINLINE bool IsEmpty( void ) const; + + FORCEINLINE void Init( void ) + { + for( int i = 0; i < ARRAYSIZE( m_Mins ); i++ ) + { + m_Mins[i] = Four_FLT_MAX; + m_Maxes[i] = Four_Negative_FLT_MAX; + } + } + + // given a set of points, expand the kdop to contain them + void AddPointSet( Vector const *pPoints, int nPnts ); + + void CreateFromPointSet( Vector const *pPoints, int nPnts ); +}; + +FORCEINLINE void KDop32_t::operator|=( KDop32_t const & other ) +{ + m_Mins[0] = MinSIMD( m_Mins[0], other.m_Mins[0] ); + m_Mins[1] = MinSIMD( m_Mins[1], other.m_Mins[1] ); + m_Mins[2] = MinSIMD( m_Mins[2], other.m_Mins[2] ); + m_Mins[3] = MinSIMD( m_Mins[3], other.m_Mins[3] ); + + m_Maxes[0] = MaxSIMD( m_Maxes[0], other.m_Maxes[0] ); + m_Maxes[1] = MaxSIMD( m_Maxes[1], other.m_Maxes[1] ); + m_Maxes[2] = MaxSIMD( m_Maxes[2], other.m_Maxes[2] ); + m_Maxes[3] = MaxSIMD( m_Maxes[3], other.m_Maxes[3] ); + + +} + +FORCEINLINE bool KDop32_t::Intersects( KDop32_t const &other ) const +{ + fltx4 c00 = CmpLeSIMD( m_Mins[0], other.m_Maxes[0] ); + fltx4 c01 = CmpLeSIMD( m_Mins[1], other.m_Maxes[1] ); + fltx4 c02 = CmpLeSIMD( m_Mins[2], other.m_Maxes[2] ); + fltx4 c03 = CmpLeSIMD( m_Mins[3], other.m_Maxes[3] ); + + fltx4 c10 = CmpGeSIMD( m_Maxes[0], other.m_Mins[0] ); + fltx4 c11 = CmpGeSIMD( m_Maxes[1], other.m_Mins[1] ); + fltx4 c12 = CmpGeSIMD( m_Maxes[2], other.m_Mins[2] ); + fltx4 c13 = CmpGeSIMD( m_Maxes[3], other.m_Mins[3] ); + + fltx4 a0 = AndSIMD( AndSIMD( c00, c01 ), AndSIMD( c02, c03 ) ); + fltx4 a1 = AndSIMD( AndSIMD( c10, c11 ), AndSIMD( c12, c13 ) ); + + return ! ( IsAnyZeros( AndSIMD( a1, a0 ) ) ); +} + + +FORCEINLINE bool KDop32_t::IsEmpty( void ) const +{ + fltx4 c00 = CmpLtSIMD( m_Maxes[0], m_Mins[0] ); + fltx4 c01 = CmpLtSIMD( m_Maxes[1], m_Mins[1] ); + fltx4 c02 = CmpLtSIMD( m_Maxes[2], m_Mins[2] ); + fltx4 c03 = CmpLtSIMD( m_Maxes[3], m_Mins[3] ); + + return IsAnyNegative( OrSIMD( OrSIMD( c00, c01 ), OrSIMD( c02, c03 ) ) ); +} + + +extern const fltx4 g_KDop32XDirs[4]; +extern const fltx4 g_KDop32YDirs[4]; +extern const fltx4 g_KDop32ZDirs[4]; + + +#endif // _ssemath_h diff --git a/public/mathlib/ssequaternion.h b/public/mathlib/ssequaternion.h new file mode 100644 index 0000000..80f7b79 --- /dev/null +++ b/public/mathlib/ssequaternion.h @@ -0,0 +1,1096 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: - defines SIMD "structure of arrays" classes and functions. +// +//===========================================================================// +#ifndef SSEQUATMATH_H +#define SSEQUATMATH_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/ssemath.h" + +// Use this #define to allow SSE versions of Quaternion math +// to exist on PC. +// On PC, certain horizontal vector operations are not supported. +// This causes the SSE implementation of quaternion math to mix the +// vector and scalar floating point units, which is extremely +// performance negative if you don't compile to native SSE2 (which +// we don't as of Sept 1, 2007). So, it's best not to allow these +// functions to exist at all. It's not good enough to simply replace +// the contents of the functions with scalar math, because each call +// to LoadAligned and StoreAligned will result in an unnecssary copy +// of the quaternion, and several moves to and from the XMM registers. +// +// Basically, the problem you run into is that for efficient SIMD code, +// you need to load the quaternions and vectors into SIMD registers and +// keep them there as long as possible while doing only SIMD math, +// whereas for efficient scalar code, each time you copy onto or ever +// use a fltx4, it hoses your pipeline. So the difference has to be +// in the management of temporary variables in the calling function, +// not inside the math functions. +// +// If you compile assuming the presence of SSE2, the MSVC will abandon +// the traditional x87 FPU operations altogether and make everything use +// the SSE2 registers, which lessens this problem a little. + +// permitted only on 360, as we've done careful tuning on its Altivec math. +// FourQuaternions, however, are always allowed, because vertical ops are +// fine on SSE. +#ifdef _X360 +#define ALLOW_SIMD_QUATERNION_MATH 1 // not on PC! +#endif + + + +//--------------------------------------------------------------------- +// Load/store quaternions +//--------------------------------------------------------------------- +#ifndef _X360 +// Using STDC or SSE +FORCEINLINE fltx4 LoadAlignedSIMD( const QuaternionAligned & pSIMD ) +{ + fltx4 retval = LoadAlignedSIMD( pSIMD.Base() ); + return retval; +} + +FORCEINLINE fltx4 LoadAlignedSIMD( const QuaternionAligned * RESTRICT pSIMD ) +{ + fltx4 retval = LoadAlignedSIMD( pSIMD->Base() ); + return retval; +} + +FORCEINLINE void StoreAlignedSIMD( QuaternionAligned * RESTRICT pSIMD, const fltx4 & a ) +{ + StoreAlignedSIMD( pSIMD->Base(), a ); +} +#else + +// for the transitional class -- load a QuaternionAligned +FORCEINLINE fltx4 LoadAlignedSIMD( const QuaternionAligned & pSIMD ) +{ + fltx4 retval = XMLoadVector4A( pSIMD.Base() ); + return retval; +} + +FORCEINLINE fltx4 LoadAlignedSIMD( const QuaternionAligned * RESTRICT pSIMD ) +{ + fltx4 retval = XMLoadVector4A( pSIMD ); + return retval; +} + +FORCEINLINE void StoreAlignedSIMD( QuaternionAligned * RESTRICT pSIMD, const fltx4 & a ) +{ + XMStoreVector4A( pSIMD->Base(), a ); +} + +// From a RadianEuler packed onto a fltx4, to a quaternion +fltx4 AngleQuaternionSIMD( FLTX4 vAngles ); + +#endif + + +#if ALLOW_SIMD_QUATERNION_MATH +//--------------------------------------------------------------------- +// Make sure quaternions are within 180 degrees of one another, if not, reverse q +//--------------------------------------------------------------------- +FORCEINLINE fltx4 QuaternionAlignSIMD( const fltx4 &p, const fltx4 &q ) +{ + // decide if one of the quaternions is backwards + fltx4 a = SubSIMD( p, q ); + fltx4 b = AddSIMD( p, q ); + a = Dot4SIMD( a, a ); + b = Dot4SIMD( b, b ); + fltx4 cmp = CmpGtSIMD( a, b ); + fltx4 result = MaskedAssign( cmp, NegSIMD(q), q ); + return result; +} + +//--------------------------------------------------------------------- +// Normalize Quaternion +//--------------------------------------------------------------------- +#if USE_STDC_FOR_SIMD + +FORCEINLINE fltx4 QuaternionNormalizeSIMD( const fltx4 &q ) +{ + fltx4 radius, result; + radius = Dot4SIMD( q, q ); + + if ( SubFloat( radius, 0 ) ) // > FLT_EPSILON && ((radius < 1.0f - 4*FLT_EPSILON) || (radius > 1.0f + 4*FLT_EPSILON)) + { + float iradius = 1.0f / sqrt( SubFloat( radius, 0 ) ); + result = ReplicateX4( iradius ); + result = MulSIMD( result, q ); + return result; + } + return q; +} + +#else + +// SSE + X360 implementation +FORCEINLINE fltx4 QuaternionNormalizeSIMD( const fltx4 &q ) +{ + fltx4 radius, result, mask; + radius = Dot4SIMD( q, q ); + mask = CmpEqSIMD( radius, Four_Zeros ); // all ones iff radius = 0 + result = ReciprocalSqrtSIMD( radius ); + result = MulSIMD( result, q ); + return MaskedAssign( mask, q, result ); // if radius was 0, just return q +} + +#endif + + +//--------------------------------------------------------------------- +// 0.0 returns p, 1.0 return q. +//--------------------------------------------------------------------- +FORCEINLINE fltx4 QuaternionBlendNoAlignSIMD( const fltx4 &p, const fltx4 &q, float t ) +{ + fltx4 sclp, sclq, result; + sclq = ReplicateX4( t ); + sclp = SubSIMD( Four_Ones, sclq ); + result = MulSIMD( sclp, p ); + result = MaddSIMD( sclq, q, result ); + return QuaternionNormalizeSIMD( result ); +} + + +//--------------------------------------------------------------------- +// Blend Quaternions +//--------------------------------------------------------------------- +FORCEINLINE fltx4 QuaternionBlendSIMD( const fltx4 &p, const fltx4 &q, float t ) +{ + // decide if one of the quaternions is backwards + fltx4 q2, result; + q2 = QuaternionAlignSIMD( p, q ); + result = QuaternionBlendNoAlignSIMD( p, q2, t ); + return result; +} + + +//--------------------------------------------------------------------- +// Multiply Quaternions +//--------------------------------------------------------------------- +#ifndef _X360 + +// SSE and STDC +FORCEINLINE fltx4 QuaternionMultSIMD( const fltx4 &p, const fltx4 &q ) +{ + // decide if one of the quaternions is backwards + fltx4 q2, result; + q2 = QuaternionAlignSIMD( p, q ); + SubFloat( result, 0 ) = SubFloat( p, 0 ) * SubFloat( q2, 3 ) + SubFloat( p, 1 ) * SubFloat( q2, 2 ) - SubFloat( p, 2 ) * SubFloat( q2, 1 ) + SubFloat( p, 3 ) * SubFloat( q2, 0 ); + SubFloat( result, 1 ) = -SubFloat( p, 0 ) * SubFloat( q2, 2 ) + SubFloat( p, 1 ) * SubFloat( q2, 3 ) + SubFloat( p, 2 ) * SubFloat( q2, 0 ) + SubFloat( p, 3 ) * SubFloat( q2, 1 ); + SubFloat( result, 2 ) = SubFloat( p, 0 ) * SubFloat( q2, 1 ) - SubFloat( p, 1 ) * SubFloat( q2, 0 ) + SubFloat( p, 2 ) * SubFloat( q2, 3 ) + SubFloat( p, 3 ) * SubFloat( q2, 2 ); + SubFloat( result, 3 ) = -SubFloat( p, 0 ) * SubFloat( q2, 0 ) - SubFloat( p, 1 ) * SubFloat( q2, 1 ) - SubFloat( p, 2 ) * SubFloat( q2, 2 ) + SubFloat( p, 3 ) * SubFloat( q2, 3 ); + return result; +} + +#else + +// X360 +extern const fltx4 g_QuatMultRowSign[4]; +FORCEINLINE fltx4 QuaternionMultSIMD( const fltx4 &p, const fltx4 &q ) +{ + fltx4 q2, row, result; + q2 = QuaternionAlignSIMD( p, q ); + + row = XMVectorSwizzle( q2, 3, 2, 1, 0 ); + row = MulSIMD( row, g_QuatMultRowSign[0] ); + result = Dot4SIMD( row, p ); + + row = XMVectorSwizzle( q2, 2, 3, 0, 1 ); + row = MulSIMD( row, g_QuatMultRowSign[1] ); + row = Dot4SIMD( row, p ); + result = __vrlimi( result, row, 4, 0 ); + + row = XMVectorSwizzle( q2, 1, 0, 3, 2 ); + row = MulSIMD( row, g_QuatMultRowSign[2] ); + row = Dot4SIMD( row, p ); + result = __vrlimi( result, row, 2, 0 ); + + row = MulSIMD( q2, g_QuatMultRowSign[3] ); + row = Dot4SIMD( row, p ); + result = __vrlimi( result, row, 1, 0 ); + return result; +} + +#endif + + +//--------------------------------------------------------------------- +// Quaternion scale +//--------------------------------------------------------------------- +#ifndef _X360 + +// SSE and STDC +FORCEINLINE fltx4 QuaternionScaleSIMD( const fltx4 &p, float t ) +{ + float r; + fltx4 q; + + // FIXME: nick, this isn't overly sensitive to accuracy, and it may be faster to + // use the cos part (w) of the quaternion (sin(omega)*N,cos(omega)) to figure the new scale. + float sinom = sqrt( SubFloat( p, 0 ) * SubFloat( p, 0 ) + SubFloat( p, 1 ) * SubFloat( p, 1 ) + SubFloat( p, 2 ) * SubFloat( p, 2 ) ); + sinom = min( sinom, 1.f ); + + float sinsom = sin( asin( sinom ) * t ); + + t = sinsom / (sinom + FLT_EPSILON); + SubFloat( q, 0 ) = t * SubFloat( p, 0 ); + SubFloat( q, 1 ) = t * SubFloat( p, 1 ); + SubFloat( q, 2 ) = t * SubFloat( p, 2 ); + + // rescale rotation + r = 1.0f - sinsom * sinsom; + + // Assert( r >= 0 ); + if (r < 0.0f) + r = 0.0f; + r = sqrt( r ); + + // keep sign of rotation + SubFloat( q, 3 ) = fsel( SubFloat( p, 3 ), r, -r ); + return q; +} + +#else + +// X360 +FORCEINLINE fltx4 QuaternionScaleSIMD( const fltx4 &p, float t ) +{ + fltx4 sinom = Dot3SIMD( p, p ); + sinom = SqrtSIMD( sinom ); + sinom = MinSIMD( sinom, Four_Ones ); + fltx4 sinsom = ArcSinSIMD( sinom ); + fltx4 t4 = ReplicateX4( t ); + sinsom = MulSIMD( sinsom, t4 ); + sinsom = SinSIMD( sinsom ); + sinom = AddSIMD( sinom, Four_Epsilons ); + sinom = ReciprocalSIMD( sinom ); + t4 = MulSIMD( sinsom, sinom ); + fltx4 result = MulSIMD( p, t4 ); + + // rescale rotation + sinsom = MulSIMD( sinsom, sinsom ); + fltx4 r = SubSIMD( Four_Ones, sinsom ); + r = MaxSIMD( r, Four_Zeros ); + r = SqrtSIMD( r ); + + // keep sign of rotation + fltx4 cmp = CmpGeSIMD( p, Four_Zeros ); + r = MaskedAssign( cmp, r, NegSIMD( r ) ); + + result = __vrlimi(result, r, 1, 0); + return result; +} + +// X360 +// assumes t4 contains a float replicated to each slot +FORCEINLINE fltx4 QuaternionScaleSIMD( const fltx4 &p, const fltx4 &t4 ) +{ + fltx4 sinom = Dot3SIMD( p, p ); + sinom = SqrtSIMD( sinom ); + sinom = MinSIMD( sinom, Four_Ones ); + fltx4 sinsom = ArcSinSIMD( sinom ); + sinsom = MulSIMD( sinsom, t4 ); + sinsom = SinSIMD( sinsom ); + sinom = AddSIMD( sinom, Four_Epsilons ); + sinom = ReciprocalSIMD( sinom ); + fltx4 result = MulSIMD( p, MulSIMD( sinsom, sinom ) ); + + // rescale rotation + sinsom = MulSIMD( sinsom, sinsom ); + fltx4 r = SubSIMD( Four_Ones, sinsom ); + r = MaxSIMD( r, Four_Zeros ); + r = SqrtSIMD( r ); + + // keep sign of rotation + fltx4 cmp = CmpGeSIMD( p, Four_Zeros ); + r = MaskedAssign( cmp, r, NegSIMD( r ) ); + + result = __vrlimi(result, r, 1, 0); + return result; +} + +#endif + + +//----------------------------------------------------------------------------- +// Quaternion sphereical linear interpolation +//----------------------------------------------------------------------------- +#ifndef _X360 + +// SSE and STDC +FORCEINLINE fltx4 QuaternionSlerpNoAlignSIMD( const fltx4 &p, const fltx4 &q, float t ) +{ + float omega, cosom, sinom, sclp, sclq; + + fltx4 result; + + // 0.0 returns p, 1.0 return q. + cosom = SubFloat( p, 0 ) * SubFloat( q, 0 ) + SubFloat( p, 1 ) * SubFloat( q, 1 ) + + SubFloat( p, 2 ) * SubFloat( q, 2 ) + SubFloat( p, 3 ) * SubFloat( q, 3 ); + + if ( (1.0f + cosom ) > 0.000001f ) + { + if ( (1.0f - cosom ) > 0.000001f ) + { + omega = acos( cosom ); + sinom = sin( omega ); + sclp = sin( (1.0f - t)*omega) / sinom; + sclq = sin( t*omega ) / sinom; + } + else + { + // TODO: add short circuit for cosom == 1.0f? + sclp = 1.0f - t; + sclq = t; + } + SubFloat( result, 0 ) = sclp * SubFloat( p, 0 ) + sclq * SubFloat( q, 0 ); + SubFloat( result, 1 ) = sclp * SubFloat( p, 1 ) + sclq * SubFloat( q, 1 ); + SubFloat( result, 2 ) = sclp * SubFloat( p, 2 ) + sclq * SubFloat( q, 2 ); + SubFloat( result, 3 ) = sclp * SubFloat( p, 3 ) + sclq * SubFloat( q, 3 ); + } + else + { + SubFloat( result, 0 ) = -SubFloat( q, 1 ); + SubFloat( result, 1 ) = SubFloat( q, 0 ); + SubFloat( result, 2 ) = -SubFloat( q, 3 ); + SubFloat( result, 3 ) = SubFloat( q, 2 ); + sclp = sin( (1.0f - t) * (0.5f * M_PI)); + sclq = sin( t * (0.5f * M_PI)); + SubFloat( result, 0 ) = sclp * SubFloat( p, 0 ) + sclq * SubFloat( result, 0 ); + SubFloat( result, 1 ) = sclp * SubFloat( p, 1 ) + sclq * SubFloat( result, 1 ); + SubFloat( result, 2 ) = sclp * SubFloat( p, 2 ) + sclq * SubFloat( result, 2 ); + } + + return result; +} + +#else + +// X360 +FORCEINLINE fltx4 QuaternionSlerpNoAlignSIMD( const fltx4 &p, const fltx4 &q, float t ) +{ + return XMQuaternionSlerp( p, q, t ); +} + +#endif + + +FORCEINLINE fltx4 QuaternionSlerpSIMD( const fltx4 &p, const fltx4 &q, float t ) +{ + fltx4 q2, result; + q2 = QuaternionAlignSIMD( p, q ); + result = QuaternionSlerpNoAlignSIMD( p, q2, t ); + return result; +} + + +#endif // ALLOW_SIMD_QUATERNION_MATH + + +/// class FourVectors stores 4 independent vectors for use in SIMD processing. These vectors are +/// stored in the format x x x x y y y y z z z z so that they can be efficiently SIMD-accelerated. +class ALIGN16 FourQuaternions +{ +public: + fltx4 x,y,z,w; + + FourQuaternions(void) + { + } + + FourQuaternions( const fltx4 &_x, + const fltx4 &_y, + const fltx4 &_z, + const fltx4 &_w ) + : x(_x), y(_y), z(_z), w(_w) + {} + + FourQuaternions( FourQuaternions const &src ) + { + x=src.x; + y=src.y; + z=src.z; + w=src.w; + } + + FORCEINLINE void operator=( FourQuaternions const &src ) + { + x=src.x; + y=src.y; + z=src.z; + w=src.w; + } + + /// this = this * q; + FORCEINLINE FourQuaternions Mul( FourQuaternions const &q ) const; + + /// negate the vector part + FORCEINLINE FourQuaternions Conjugate() const; + + /// for a quaternion representing a rotation of angle theta, return + /// one of angle s*theta + /// scale is four floats -- one for each quat + FORCEINLINE FourQuaternions ScaleAngle( const fltx4 &scale ) const; + + /// ret = this * ( s * q ) + /// In other words, for a quaternion representing a rotation of angle theta, return + /// one of angle s*theta + /// s is four floats in a fltx4 -- one for each quaternion + FORCEINLINE FourQuaternions MulAc( const fltx4 &s, const FourQuaternions &q ) const; + + /// ret = ( s * this ) * q + FORCEINLINE FourQuaternions ScaleMul( const fltx4 &s, const FourQuaternions &q ) const; + + /// Slerp four quaternions at once, FROM me TO the specified out. + FORCEINLINE FourQuaternions Slerp( const FourQuaternions &to, const fltx4 &t ); + + FORCEINLINE FourQuaternions SlerpNoAlign( const FourQuaternions &originalto, const fltx4 &t ); + + /// LoadAndSwizzleAligned - load 4 QuaternionAligneds into a FourQuaternions, performing transpose op. + /// all 4 vectors must be 128 bit boundary + FORCEINLINE void LoadAndSwizzleAligned(const float *RESTRICT a, const float *RESTRICT b, const float *RESTRICT c, const float *RESTRICT d) + { +#if _X360 + fltx4 tx = LoadAlignedSIMD(a); + fltx4 ty = LoadAlignedSIMD(b); + fltx4 tz = LoadAlignedSIMD(c); + fltx4 tw = LoadAlignedSIMD(d); + fltx4 r0 = __vmrghw(tx, tz); + fltx4 r1 = __vmrghw(ty, tw); + fltx4 r2 = __vmrglw(tx, tz); + fltx4 r3 = __vmrglw(ty, tw); + + x = __vmrghw(r0, r1); + y = __vmrglw(r0, r1); + z = __vmrghw(r2, r3); + w = __vmrglw(r2, r3); +#else + x = LoadAlignedSIMD(a); + y = LoadAlignedSIMD(b); + z = LoadAlignedSIMD(c); + w = LoadAlignedSIMD(d); + // now, matrix is: + // x y z w + // x y z w + // x y z w + // x y z w + TransposeSIMD(x, y, z, w); +#endif + } + + FORCEINLINE void LoadAndSwizzleAligned(const QuaternionAligned * RESTRICT a, + const QuaternionAligned * RESTRICT b, + const QuaternionAligned * RESTRICT c, + const QuaternionAligned * RESTRICT d) + { + LoadAndSwizzleAligned(a->Base(), b->Base(), c->Base(), d->Base() ); + } + + + /// LoadAndSwizzleAligned - load 4 consecutive QuaternionAligneds into a FourQuaternions, + /// performing transpose op. + /// all 4 vectors must be 128 bit boundary + FORCEINLINE void LoadAndSwizzleAligned(const QuaternionAligned *qs) + { +#if _X360 + fltx4 tx = LoadAlignedSIMD(qs++); + fltx4 ty = LoadAlignedSIMD(qs++); + fltx4 tz = LoadAlignedSIMD(qs++); + fltx4 tw = LoadAlignedSIMD(qs); + fltx4 r0 = __vmrghw(tx, tz); + fltx4 r1 = __vmrghw(ty, tw); + fltx4 r2 = __vmrglw(tx, tz); + fltx4 r3 = __vmrglw(ty, tw); + + x = __vmrghw(r0, r1); + y = __vmrglw(r0, r1); + z = __vmrghw(r2, r3); + w = __vmrglw(r2, r3); +#else + x = LoadAlignedSIMD(qs++); + y = LoadAlignedSIMD(qs++); + z = LoadAlignedSIMD(qs++); + w = LoadAlignedSIMD(qs++); + // now, matrix is: + // x y z w + // x y z w + // x y z w + // x y z w + TransposeSIMD(x, y, z, w); +#endif + } + + // Store the FourQuaternions out to four nonconsecutive ordinary quaternions in memory. + FORCEINLINE void SwizzleAndStoreAligned(QuaternionAligned *a, QuaternionAligned *b, QuaternionAligned *c, QuaternionAligned *d) + { +#if _X360 + fltx4 r0 = __vmrghw(x, z); + fltx4 r1 = __vmrghw(y, w); + fltx4 r2 = __vmrglw(x, z); + fltx4 r3 = __vmrglw(y, w); + + fltx4 rx = __vmrghw(r0, r1); + fltx4 ry = __vmrglw(r0, r1); + fltx4 rz = __vmrghw(r2, r3); + fltx4 rw = __vmrglw(r2, r3); + + StoreAlignedSIMD(a, rx); + StoreAlignedSIMD(b, ry); + StoreAlignedSIMD(c, rz); + StoreAlignedSIMD(d, rw); +#else + fltx4 dupes[4] = { x, y, z, w }; + TransposeSIMD(dupes[0], dupes[1], dupes[2], dupes[3]); + StoreAlignedSIMD(a, dupes[0]); + StoreAlignedSIMD(b, dupes[1]); + StoreAlignedSIMD(c, dupes[2]); + StoreAlignedSIMD(d, dupes[3]); +#endif + } + + // Store the FourQuaternions out to four consecutive ordinary quaternions in memory. + FORCEINLINE void SwizzleAndStoreAligned(QuaternionAligned *qs) + { +#if _X360 + fltx4 r0 = __vmrghw(x, z); + fltx4 r1 = __vmrghw(y, w); + fltx4 r2 = __vmrglw(x, z); + fltx4 r3 = __vmrglw(y, w); + + fltx4 rx = __vmrghw(r0, r1); + fltx4 ry = __vmrglw(r0, r1); + fltx4 rz = __vmrghw(r2, r3); + fltx4 rw = __vmrglw(r2, r3); + + StoreAlignedSIMD(qs, rx); + StoreAlignedSIMD(++qs, ry); + StoreAlignedSIMD(++qs, rz); + StoreAlignedSIMD(++qs, rw); +#else + SwizzleAndStoreAligned(qs, qs+1, qs+2, qs+3); +#endif + } + + // Store the FourQuaternions out to four consecutive ordinary quaternions in memory. + // The mask specifies which of the quaternions are actually written out -- each + // word in the fltx4 should be all binary ones or zeros. Ones means the corresponding + // quat will be written. + FORCEINLINE void SwizzleAndStoreAlignedMasked(QuaternionAligned * RESTRICT qs, const fltx4 &controlMask) + { + fltx4 originals[4]; + originals[0] = LoadAlignedSIMD(qs); + originals[1] = LoadAlignedSIMD(qs+1); + originals[2] = LoadAlignedSIMD(qs+2); + originals[3] = LoadAlignedSIMD(qs+3); + + fltx4 masks[4] = { SplatXSIMD(controlMask), + SplatYSIMD(controlMask), + SplatZSIMD(controlMask), + SplatWSIMD(controlMask) }; + +#if _X360 + fltx4 r0 = __vmrghw(x, z); + fltx4 r1 = __vmrghw(y, w); + fltx4 r2 = __vmrglw(x, z); + fltx4 r3 = __vmrglw(y, w); + + fltx4 rx = __vmrghw(r0, r1); + fltx4 ry = __vmrglw(r0, r1); + fltx4 rz = __vmrghw(r2, r3); + fltx4 rw = __vmrglw(r2, r3); +#else + fltx4 rx = x; + fltx4 ry = y; + fltx4 rz = z; + fltx4 rw = w; + TransposeSIMD( rx, ry, rz, rw ); +#endif + + StoreAlignedSIMD( qs+0, MaskedAssign(masks[0], rx, originals[0])); + StoreAlignedSIMD( qs+1, MaskedAssign(masks[1], ry, originals[1])); + StoreAlignedSIMD( qs+2, MaskedAssign(masks[2], rz, originals[2])); + StoreAlignedSIMD( qs+3, MaskedAssign(masks[3], rw, originals[3])); + } +}; + +FORCEINLINE FourQuaternions FourQuaternions::Conjugate( ) const +{ + return FourQuaternions( NegSIMD(x), NegSIMD(y), NegSIMD(z), w ); +} + + + + +FORCEINLINE const fltx4 Dot(const FourQuaternions &a, const FourQuaternions &b) +{ + return + MaddSIMD(a.x, b.x, + MaddSIMD(a.y, b.y, + MaddSIMD(a.z,b.z, MulSIMD(a.w,b.w)) + ) + ); +} + + +FORCEINLINE const FourQuaternions Madd(const FourQuaternions &a, const fltx4 &scale, const FourQuaternions &c) +{ + FourQuaternions ret; + ret.x = MaddSIMD(a.x,scale,c.x); + ret.y = MaddSIMD(a.y,scale,c.y); + ret.z = MaddSIMD(a.z,scale,c.z); + ret.w = MaddSIMD(a.w,scale,c.w); + return ret; +} + +FORCEINLINE const FourQuaternions Mul(const FourQuaternions &a, const fltx4 &scale) +{ + FourQuaternions ret; + ret.x = MulSIMD(a.x,scale); + ret.y = MulSIMD(a.y,scale); + ret.z = MulSIMD(a.z,scale); + ret.w = MulSIMD(a.w,scale); + return ret; +} + +FORCEINLINE const FourQuaternions Add(const FourQuaternions &a,const FourQuaternions &b) +{ + FourQuaternions ret; + ret.x = AddSIMD(a.x,b.x); + ret.y = AddSIMD(a.y,b.y); + ret.z = AddSIMD(a.z,b.z); + ret.w = AddSIMD(a.w,b.w); + return ret; +} + +FORCEINLINE const FourQuaternions Sub(const FourQuaternions &a,const FourQuaternions &b) +{ + FourQuaternions ret; + ret.x = SubSIMD(a.x,b.x); + ret.y = SubSIMD(a.y,b.y); + ret.z = SubSIMD(a.z,b.z); + ret.w = SubSIMD(a.w,b.w); + return ret; +} + +FORCEINLINE const FourQuaternions Neg(const FourQuaternions &q) +{ + FourQuaternions ret; + ret.x = NegSIMD(q.x); + ret.y = NegSIMD(q.y); + ret.z = NegSIMD(q.z); + ret.w = NegSIMD(q.w); + return ret; +} + +FORCEINLINE const FourQuaternions MaskedAssign(const fltx4 &mask, const FourQuaternions &a, const FourQuaternions &b) +{ + FourQuaternions ret; + ret.x = MaskedAssign(mask,a.x,b.x); + ret.y = MaskedAssign(mask,a.y,b.y); + ret.z = MaskedAssign(mask,a.z,b.z); + ret.w = MaskedAssign(mask,a.w,b.w); + return ret; +} + + +FORCEINLINE FourQuaternions QuaternionAlign( const FourQuaternions &p, const FourQuaternions &q ) +{ + // decide if one of the quaternions is backwards + fltx4 cmp = CmpLtSIMD( Dot(p,q), Four_Zeros ); + return MaskedAssign( cmp, Neg(q), q ); +} + + +FORCEINLINE const FourQuaternions QuaternionNormalize( const FourQuaternions &q ) +{ + fltx4 radius = Dot( q, q ); + fltx4 mask = CmpEqSIMD( radius, Four_Zeros ); // all ones iff radius = 0 + fltx4 invRadius = ReciprocalSqrtSIMD( radius ); + + FourQuaternions ret = MaskedAssign(mask, q, Mul(q, invRadius)); + return ret; +} + + + + + +/// this = this * q; +FORCEINLINE FourQuaternions FourQuaternions::Mul( FourQuaternions const &q ) const +{ + // W = w1w2 - x1x2 - y1y2 - z1z2 + FourQuaternions ret; + fltx4 signMask = LoadAlignedSIMD( (float *) g_SIMD_signmask ); + // as we do the multiplication, also do a dot product, so we know whether + // one of the quats is backwards and if we therefore have to negate at the end + fltx4 dotProduct = MulSIMD( w, q.w ); + + ret.w = MulSIMD( w, q.w ); // W = w1w2 + ret.x = MulSIMD( w, q.x ); // X = w1x2 + ret.y = MulSIMD( w, q.y ); // Y = w1y2 + ret.z = MulSIMD( w, q.z ); // Z = w1z2 + + dotProduct = MaddSIMD( x, q.x, dotProduct ); + ret.w = MsubSIMD( x, q.x, ret.w ); // W = w1w2 - x1x2 + ret.x = MaddSIMD( x, q.w, ret.x ); // X = w1x2 + x1w2 + ret.y = MsubSIMD( x, q.z, ret.y ); // Y = w1y2 - x1z2 + ret.z = MaddSIMD( x, q.y, ret.z ); // Z = w1z2 + x1y2 + + dotProduct = MaddSIMD( y, q.y, dotProduct ); + ret.w = MsubSIMD( y, q.y, ret.w ); // W = w1w2 - x1x2 - y1y2 + ret.x = MaddSIMD( y, q.z, ret.x ); // X = w1x2 + x1w2 + y1z2 + ret.y = MaddSIMD( y, q.w, ret.y ); // Y = w1y2 - x1z2 + y1w2 + ret.z = MsubSIMD( y, q.x, ret.z ); // Z = w1z2 + x1y2 - y1x2 + + dotProduct = MaddSIMD( z, q.z, dotProduct ); + ret.w = MsubSIMD( z, q.z, ret.w ); // W = w1w2 - x1x2 - y1y2 - z1z2 + ret.x = MsubSIMD( z, q.y, ret.x ); // X = w1x2 + x1w2 + y1z2 - z1y2 + ret.y = MaddSIMD( z, q.x, ret.y ); // Y = w1y2 - x1z2 + y1w2 + z1x2 + ret.z = MaddSIMD( z, q.w, ret.z ); // Z = w1z2 + x1y2 - y1x2 + z1w2 + + fltx4 Zero = Four_Zeros; + fltx4 control = CmpLtSIMD( dotProduct, Four_Zeros ); + signMask = MaskedAssign(control, signMask, Zero); // negate quats where q1.q2 < 0 + ret.w = XorSIMD( signMask, ret.w ); + ret.x = XorSIMD( signMask, ret.x ); + ret.y = XorSIMD( signMask, ret.y ); + ret.z = XorSIMD( signMask, ret.z ); + + return ret; +} + +/* + +void QuaternionScale( const Quaternion &p, float t, Quaternion &q ) +{ + Assert( s_bMathlibInitialized ); + + + float r; + + // FIXME: nick, this isn't overly sensitive to accuracy, and it may be faster to + // use the cos part (w) of the quaternion (sin(omega)*N,cos(omega)) to figure the new scale. + float sinom = sqrt( DotProduct( &p.x, &p.x ) ); + sinom = min( sinom, 1.f ); + + float sinsom = sin( asin( sinom ) * t ); + + t = sinsom / (sinom + FLT_EPSILON); + VectorScale( &p.x, t, &q.x ); + + // rescale rotation + r = 1.0f - sinsom * sinsom; + + // Assert( r >= 0 ); + if (r < 0.0f) + r = 0.0f; + r = sqrt( r ); + + // keep sign of rotation + if (p.w < 0) + q.w = -r; + else + q.w = r; + + Assert( q.IsValid() ); + + return; +} + +*/ + +FORCEINLINE FourQuaternions FourQuaternions::ScaleAngle( const fltx4 &scale ) const +{ + FourQuaternions ret; + static const fltx4 OneMinusEpsilon = {1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f }; + static const fltx4 tiny = { 0.00001f, 0.00001f, 0.00001f, 0.00001f }; + const fltx4 Zero = Four_Zeros; + fltx4 signMask = LoadAlignedSIMD( (float *) g_SIMD_signmask ); + // work out if there are any tiny scales or angles, which are unstable + fltx4 tinyAngles = CmpGtSIMD(w,OneMinusEpsilon); + fltx4 negativeRotations = CmpLtSIMD(w, Zero); // if any w's are <0, we will need to negate later down + + // figure out the theta + fltx4 angles = ArcCosSIMD(w); + + // test also if w > -1 + fltx4 negativeWs = XorSIMD(signMask, w); + tinyAngles = OrSIMD( CmpGtSIMD(negativeWs, OneMinusEpsilon ), tinyAngles ); + + // meanwhile start working on computing the dot product of the + // vector component, and trust in the scheduler to interleave them + fltx4 vLenSq = MulSIMD( x, x ); + vLenSq = MaddSIMD( y, y, vLenSq ); + vLenSq = MaddSIMD( z, z, vLenSq ); + + // scale the angles + angles = MulSIMD( angles, scale ); + + // clear out the sign mask where w>=0 + signMask = MaskedAssign( negativeRotations, signMask, Zero); + + // work out the new w component and vector length + fltx4 vLenRecip = ReciprocalSqrtSIMD(vLenSq); // interleave with Cos to hide latencies + fltx4 sine; + SinCosSIMD( sine, ret.w, angles ); + ret.x = MulSIMD( x, vLenRecip ); // renormalize so the vector length + w = 1 + ret.y = MulSIMD( y, vLenRecip ); // renormalize so the vector length + w = 1 + ret.z = MulSIMD( z, vLenRecip ); // renormalize so the vector length + w = 1 + ret.x = MulSIMD( ret.x, sine ); + ret.y = MulSIMD( ret.y, sine ); + ret.z = MulSIMD( ret.z, sine ); + + // negate where necessary + ret.x = XorSIMD(ret.x, signMask); + ret.y = XorSIMD(ret.y, signMask); + ret.z = XorSIMD(ret.z, signMask); + ret.w = XorSIMD(ret.w, signMask); + + // finally, toss results from where cos(theta) is close to 1 -- these are non rotations. + ret.x = MaskedAssign(tinyAngles, x, ret.x); + ret.y = MaskedAssign(tinyAngles, y, ret.y); + ret.z = MaskedAssign(tinyAngles, z, ret.z); + ret.w = MaskedAssign(tinyAngles, w, ret.w); + + return ret; +} + +//----------------------------------------------------------------------------- +// Purpose: return = this * ( s * q ) +// In other words, for a quaternion representing a rotation of angle theta, return +// one of angle s*theta +// s is four floats in a fltx4 -- one for each quaternion +//----------------------------------------------------------------------------- + +FORCEINLINE FourQuaternions FourQuaternions::MulAc( const fltx4 &s, const FourQuaternions &q ) const +{ + /* + void QuaternionMA( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt ) + { + Quaternion p1, q1; + + QuaternionScale( q, s, q1 ); + QuaternionMult( p, q1, p1 ); + QuaternionNormalize( p1 ); + qt[0] = p1[0]; + qt[1] = p1[1]; + qt[2] = p1[2]; + qt[3] = p1[3]; + } + */ + + return Mul(q.ScaleAngle(s)); +} + + +FORCEINLINE FourQuaternions FourQuaternions::ScaleMul( const fltx4 &s, const FourQuaternions &q ) const +{ + return ScaleAngle(s).Mul(q); +} + + +FORCEINLINE FourQuaternions FourQuaternions::Slerp( const FourQuaternions &originalto, const fltx4 &t ) +{ + FourQuaternions ret; + static const fltx4 OneMinusEpsilon = {1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f }; + + // align if necessary. + + // actually, before we even do that, start by computing the dot product of + // the quaternions. it has lots of dependent ops and we can sneak it into + // the pipeline bubbles as we figure out alignment. Of course we don't know + // yet if we need to realign, so compute them both -- there's plenty of + // space in the bubbles. They're roomy, those bubbles. + fltx4 cosineOmega; +#if 0 // Maybe I don't need to do alignment seperately, using the xb360 technique... + FourQuaternions to; + { + fltx4 diffs[4], sums[4], originalToNeg[4]; + fltx4 dotIfAligned, dotIfNotAligned; + + // compute negations of the TO quaternion. + originalToNeg[0] = NegSIMD(originalto.x); + originalToNeg[1] = NegSIMD(originalto.y); + originalToNeg[2] = NegSIMD(originalto.z); + originalToNeg[3] = NegSIMD(originalto.w); + + dotIfAligned = MulSIMD(x, originalto.x); + dotIfNotAligned = MulSIMD(x, originalToNeg[0]); + + diffs[0] = SubSIMD(x, originalto.x); + diffs[1] = SubSIMD(y, originalto.y); + diffs[2] = SubSIMD(z, originalto.z); + diffs[3] = SubSIMD(w, originalto.w); + + sums[0] = AddSIMD(x, originalto.x); + sums[1] = AddSIMD(y, originalto.y); + sums[2] = AddSIMD(z, originalto.z); + sums[3] = AddSIMD(w, originalto.w); + + dotIfAligned = MaddSIMD(y, originalto.y, dotIfAligned); + dotIfNotAligned = MaddSIMD(y, originalToNeg[1], dotIfNotAligned); + + fltx4 diffsDot, sumsDot; + + diffsDot = MulSIMD(diffs[0], diffs[0]); // x^2 + sumsDot = MulSIMD(sums[0], sums[0] ); // x^2 + // do some work on the dot products while letting the multiplies cook + dotIfAligned = MaddSIMD(z, originalto.z, dotIfAligned); + dotIfNotAligned = MaddSIMD(z, originalToNeg[2], dotIfNotAligned); + + diffsDot = MaddSIMD(diffs[1], diffs[1], diffsDot); // x^2 + y^2 + sumsDot = MaddSIMD(sums[1], sums[1], sumsDot ); + diffsDot = MaddSIMD(diffs[2], diffs[2], diffsDot); // x^2 + y^2 + z^2 + sumsDot = MaddSIMD(sums[2], sums[2], sumsDot ); + diffsDot = MaddSIMD(diffs[3], diffs[3], diffsDot); // x^2 + y^2 + z^2 + w^2 + sumsDot = MaddSIMD(sums[3], sums[3], sumsDot ); + // do some work on the dot products while letting the multiplies cook + dotIfAligned = MaddSIMD(w, originalto.w, dotIfAligned); + dotIfNotAligned = MaddSIMD(w, originalToNeg[3], dotIfNotAligned); + + // are the differences greater than the sums? + // if so, we need to negate that quaternion + fltx4 mask = CmpGtSIMD(diffsDot, sumsDot); // 1 for diffs>0 and 0 elsewhere + to.x = MaskedAssign(mask, originalToNeg[0], originalto.x); + to.y = MaskedAssign(mask, originalToNeg[1], originalto.y); + to.z = MaskedAssign(mask, originalToNeg[2], originalto.z); + to.w = MaskedAssign(mask, originalToNeg[3], originalto.w); + + cosineOmega = MaskedAssign(mask, dotIfNotAligned, dotIfAligned); + } + + // right, now to is aligned to be the short way round, and we computed + // the dot product while we were figuring all that out. +#else + const FourQuaternions &to = originalto; + cosineOmega = MulSIMD(x, to.x); + cosineOmega = MaddSIMD(y, to.y, cosineOmega); + cosineOmega = MaddSIMD(z, to.z, cosineOmega); + cosineOmega = MaddSIMD(w, to.w, cosineOmega); +#endif + + fltx4 Zero = Four_Zeros; + fltx4 cosOmegaLessThanZero = CmpLtSIMD(cosineOmega, Zero); + // fltx4 shouldNegate = MaskedAssign(cosOmegaLessThanZero, Four_NegativeOnes , Four_Ones ); + fltx4 signMask = LoadAlignedSIMD( (float *) g_SIMD_signmask ); // contains a one in the sign bit -- xor against a number to negate it + fltx4 sinOmega = Four_Ones; + + // negate cosineOmega where necessary + cosineOmega = MaskedAssign( cosOmegaLessThanZero, XorSIMD(cosineOmega, signMask), cosineOmega ); + fltx4 oneMinusT = SubSIMD(Four_Ones,t); + fltx4 bCosOmegaLessThanOne = CmpLtSIMD(cosineOmega, OneMinusEpsilon); // we'll use this to mask out null slerps + + // figure out the sin component of the diff quaternion. + // since sin^2(t) + cos^2(t) = 1... + sinOmega = MsubSIMD( cosineOmega, cosineOmega, sinOmega ); // = 1 - cos^2(t) = sin^2(t) + fltx4 invSinOmega = ReciprocalSqrtSIMD( sinOmega ); // 1/sin(t) + sinOmega = MulSIMD( sinOmega, invSinOmega ); // = sin^2(t) / sin(t) = sin(t) + + // use the arctangent technique to work out omega from tan^-1(sin/cos) + fltx4 omega = ArcTan2SIMD(sinOmega, cosineOmega); + + // alpha = sin(omega * (1-T))/sin(omega) + // beta = sin(omega * T)/sin(omega) + fltx4 alpha = MulSIMD(omega, oneMinusT); // w(1-T) + fltx4 beta = MulSIMD(omega, t); // w(T) + signMask = MaskedAssign(cosOmegaLessThanZero, signMask, Zero); + + alpha = SinSIMD(alpha); // sin(w(1-T)) + beta = SinSIMD(beta); // sin(wT) + + alpha = MulSIMD(alpha, invSinOmega); + beta = MulSIMD(beta, invSinOmega); + + // depending on whether the dot product was less than zero, negate beta, or not + beta = XorSIMD(beta, signMask); + + // mask out singularities (where omega = 1) + alpha = MaskedAssign( bCosOmegaLessThanOne, alpha, oneMinusT ); + beta = MaskedAssign( bCosOmegaLessThanOne, beta , t ); + + ret.x = MulSIMD(x, alpha); + ret.y = MulSIMD(y, alpha); + ret.z = MulSIMD(z, alpha); + ret.w = MulSIMD(w, alpha); + + ret.x = MaddSIMD(to.x, beta, ret.x); + ret.y = MaddSIMD(to.y, beta, ret.y); + ret.z = MaddSIMD(to.z, beta, ret.z); + ret.w = MaddSIMD(to.w, beta, ret.w); + + return ret; +} + + + +FORCEINLINE FourQuaternions FourQuaternions::SlerpNoAlign( const FourQuaternions &originalto, const fltx4 &t ) +{ + FourQuaternions ret; + static const fltx4 OneMinusEpsilon = {1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f, 1.0f - 0.000001f }; + + // align if necessary. + + // actually, before we even do that, start by computing the dot product of + // the quaternions. it has lots of dependent ops and we can sneak it into + // the pipeline bubbles as we figure out alignment. Of course we don't know + // yet if we need to realign, so compute them both -- there's plenty of + // space in the bubbles. They're roomy, those bubbles. + fltx4 cosineOmega; + + const FourQuaternions &to = originalto; + cosineOmega = MulSIMD(x, to.x); + cosineOmega = MaddSIMD(y, to.y, cosineOmega); + cosineOmega = MaddSIMD(z, to.z, cosineOmega); + cosineOmega = MaddSIMD(w, to.w, cosineOmega); + + fltx4 sinOmega = Four_Ones; + + fltx4 oneMinusT = SubSIMD(Four_Ones,t); + fltx4 bCosOmegaLessThanOne = CmpLtSIMD(cosineOmega, OneMinusEpsilon); // we'll use this to mask out null slerps + + // figure out the sin component of the diff quaternion. + // since sin^2(t) + cos^2(t) = 1... + sinOmega = MsubSIMD( cosineOmega, cosineOmega, sinOmega ); // = 1 - cos^2(t) = sin^2(t) + fltx4 invSinOmega = ReciprocalSqrtSIMD( sinOmega ); // 1/sin(t) + sinOmega = MulSIMD( sinOmega, invSinOmega ); // = sin^2(t) / sin(t) = sin(t) + + // use the arctangent technique to work out omega from tan^-1(sin/cos) + fltx4 omega = ArcTan2SIMD(sinOmega, cosineOmega); + + // alpha = sin(omega * (1-T))/sin(omega) + // beta = sin(omega * T)/sin(omega) + fltx4 alpha = MulSIMD(omega, oneMinusT); // w(1-T) + fltx4 beta = MulSIMD(omega, t); // w(T) + alpha = SinSIMD(alpha); // sin(w(1-T)) + beta = SinSIMD(beta); // sin(wT) + alpha = MulSIMD(alpha, invSinOmega); + beta = MulSIMD(beta, invSinOmega); + + // mask out singularities (where omega = 1) + alpha = MaskedAssign( bCosOmegaLessThanOne, alpha, oneMinusT ); + beta = MaskedAssign( bCosOmegaLessThanOne, beta , t ); + + ret.x = MulSIMD(x, alpha); + ret.y = MulSIMD(y, alpha); + ret.z = MulSIMD(z, alpha); + ret.w = MulSIMD(w, alpha); + + ret.x = MaddSIMD(to.x, beta, ret.x); + ret.y = MaddSIMD(to.y, beta, ret.y); + ret.z = MaddSIMD(to.z, beta, ret.z); + ret.w = MaddSIMD(to.w, beta, ret.w); + + return ret; +} + + +#endif // SSEQUATMATH_H + diff --git a/public/mathlib/vector.h b/public/mathlib/vector.h new file mode 100644 index 0000000..800da10 --- /dev/null +++ b/public/mathlib/vector.h @@ -0,0 +1,2501 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef VECTOR_H +#define VECTOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// For vec_t, put this somewhere else? +#include "tier0/basetypes.h" + +// For rand(). We really need a library! +#include + +#ifndef _X360 +// For MMX intrinsics +#include +#endif + +#ifndef ALIGN16_POST +#define ALIGN16_POST +#endif + +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "tier0/threadtools.h" +#include "mathlib/vector2d.h" +#include "mathlib/math_pfns.h" +#include "tier0/memalloc.h" + +// Uncomment this to add extra Asserts to check for NANs, uninitialized vecs, etc. +//#define VECTOR_PARANOIA 1 + +// Uncomment this to make sure we don't do anything slow with our vectors +//#define VECTOR_NO_SLOW_OPERATIONS 1 + + +// Used to make certain code easier to read. +#define X_INDEX 0 +#define Y_INDEX 1 +#define Z_INDEX 2 + + +#ifdef VECTOR_PARANOIA +#define CHECK_VALID( _v) Assert( (_v).IsValid() ) +#else +#ifdef GNUC +#define CHECK_VALID( _v) +#else +#define CHECK_VALID( _v) 0 +#endif +#endif + +#define VecToString(v) (static_cast(CFmtStr("(%f, %f, %f)", (v).x, (v).y, (v).z))) // ** Note: this generates a temporary, don't hold reference! + +class VectorByValue; + +//========================================================= +// 3D Vector +//========================================================= +class Vector +{ +public: + // Members + vec_t x, y, z; + + // Construction/destruction: + Vector(void); + Vector(vec_t X, vec_t Y, vec_t Z); + + // Initialization + void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f); + // TODO (Ilya): Should there be an init that takes a single float for consistency? + + // Got any nasty NAN's? + bool IsValid() const; + void Invalidate(); + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + // Base address... + vec_t* Base(); + vec_t const* Base() const; + + // Cast to Vector2D... + Vector2D& AsVector2D(); + const Vector2D& AsVector2D() const; + + // Initialization methods + void Random( vec_t minVal, vec_t maxVal ); + inline void Zero(); ///< zero out a vector + + // equality + bool operator==(const Vector& v) const; + bool operator!=(const Vector& v) const; + + // arithmetic operations + FORCEINLINE Vector& operator+=(const Vector &v); + FORCEINLINE Vector& operator-=(const Vector &v); + FORCEINLINE Vector& operator*=(const Vector &v); + FORCEINLINE Vector& operator*=(float s); + FORCEINLINE Vector& operator/=(const Vector &v); + FORCEINLINE Vector& operator/=(float s); + FORCEINLINE Vector& operator+=(float fl) ; ///< broadcast add + FORCEINLINE Vector& operator-=(float fl) ; ///< broadcast sub + +// negate the vector components + void Negate(); + + // Get the vector's magnitude. + inline vec_t Length() const; + + // Get the vector's magnitude squared. + FORCEINLINE vec_t LengthSqr(void) const + { + CHECK_VALID(*this); + return (x*x + y*y + z*z); + } + + // Get one over the vector's length + // via fast hardware approximation + inline vec_t LengthRecipFast(void) const + { + return FastRSqrtFast( LengthSqr() ); + } + + // return true if this vector is (0,0,0) within tolerance + bool IsZero( float tolerance = 0.01f ) const + { + return (x > -tolerance && x < tolerance && + y > -tolerance && y < tolerance && + z > -tolerance && z < tolerance); + } + + + // return true if this vector is exactly (0,0,0) -- only fast if vector is coming from memory, not registers + inline bool IsZeroFast( ) const RESTRICT + { + COMPILE_TIME_ASSERT( sizeof(vec_t) == sizeof(int) ); + return ( *reinterpret_cast(&x) == 0 && + *reinterpret_cast(&y) == 0 && + *reinterpret_cast(&z) == 0 ); + } + + vec_t NormalizeInPlace(); + Vector Normalized() const; + bool IsLengthGreaterThan( float val ) const; + bool IsLengthLessThan( float val ) const; + + // check if a vector is within the box defined by two other vectors + FORCEINLINE bool WithinAABox( Vector const &boxmin, Vector const &boxmax); + + // Get the distance from this vector to the other one. + vec_t DistTo(const Vector &vOther) const; + + // Get the distance from this vector to the other one squared. + // NJS: note, VC wasn't inlining it correctly in several deeply nested inlines due to being an 'out of line' inline. + // may be able to tidy this up after switching to VC7 + FORCEINLINE vec_t DistToSqr(const Vector &vOther) const + { + Vector delta; + + delta.x = x - vOther.x; + delta.y = y - vOther.y; + delta.z = z - vOther.z; + + return delta.LengthSqr(); + } + + // Copy + void CopyToArray(float* rgfl) const; + + // Multiply, add, and assign to this (ie: *this = a + b * scalar). This + // is about 12% faster than the actual vector equation (because it's done per-component + // rather than per-vector). + void MulAdd(const Vector& a, const Vector& b, float scalar); + + // Dot product. + vec_t Dot(const Vector& vOther) const; + + // assignment + Vector& operator=(const Vector &vOther); + + // returns 0, 1, 2 corresponding to the component with the largest absolute value + inline int LargestComponent() const; + + // 2d + vec_t Length2D(void) const; + vec_t Length2DSqr(void) const; + + /// get the component of this vector parallel to some other given vector + inline Vector ProjectOnto( const Vector& onto ); + + operator VectorByValue &() { return *((VectorByValue *)(this)); } + operator const VectorByValue &() const { return *((const VectorByValue *)(this)); } + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // copy constructors +// Vector(const Vector &vOther); + + // arithmetic operations + Vector operator-(void) const; + + Vector operator+(const Vector& v) const; + Vector operator-(const Vector& v) const; + Vector operator*(const Vector& v) const; + Vector operator/(const Vector& v) const; + Vector operator*(float fl) const; + Vector operator/(float fl) const; + + // Cross product between two vectors. + Vector Cross(const Vector &vOther) const; + + // Returns a vector with the min or max in X, Y, and Z. + Vector Min(const Vector &vOther) const; + Vector Max(const Vector &vOther) const; + +#else + +private: + // No copy constructors allowed if we're in optimal mode + Vector(const Vector& vOther); +#endif +}; + + + +#define USE_M64S ( ( !defined( _X360 ) ) ) + + + +//========================================================= +// 4D Short Vector (aligned on 8-byte boundary) +//========================================================= +class ALIGN8 ShortVector +{ +public: + + short x, y, z, w; + + // Initialization + void Init(short ix = 0, short iy = 0, short iz = 0, short iw = 0 ); + + +#if USE_M64S + __m64 &AsM64() { return *(__m64*)&x; } + const __m64 &AsM64() const { return *(const __m64*)&x; } +#endif + + // Setter + void Set( const ShortVector& vOther ); + void Set( const short ix, const short iy, const short iz, const short iw ); + + // array access... + short operator[](int i) const; + short& operator[](int i); + + // Base address... + short* Base(); + short const* Base() const; + + // equality + bool operator==(const ShortVector& v) const; + bool operator!=(const ShortVector& v) const; + + // Arithmetic operations + FORCEINLINE ShortVector& operator+=(const ShortVector &v); + FORCEINLINE ShortVector& operator-=(const ShortVector &v); + FORCEINLINE ShortVector& operator*=(const ShortVector &v); + FORCEINLINE ShortVector& operator*=(float s); + FORCEINLINE ShortVector& operator/=(const ShortVector &v); + FORCEINLINE ShortVector& operator/=(float s); + FORCEINLINE ShortVector operator*(float fl) const; + +private: + + // No copy constructors allowed if we're in optimal mode +// ShortVector(ShortVector const& vOther); + + // No assignment operators either... +// ShortVector& operator=( ShortVector const& src ); + +} ALIGN8_POST; + + + + + + +//========================================================= +// 4D Integer Vector +//========================================================= +class IntVector4D +{ +public: + + int x, y, z, w; + + // Initialization + void Init(int ix = 0, int iy = 0, int iz = 0, int iw = 0 ); + +#if USE_M64S + __m64 &AsM64() { return *(__m64*)&x; } + const __m64 &AsM64() const { return *(const __m64*)&x; } +#endif + + // Setter + void Set( const IntVector4D& vOther ); + void Set( const int ix, const int iy, const int iz, const int iw ); + + // array access... + int operator[](int i) const; + int& operator[](int i); + + // Base address... + int* Base(); + int const* Base() const; + + // equality + bool operator==(const IntVector4D& v) const; + bool operator!=(const IntVector4D& v) const; + + // Arithmetic operations + FORCEINLINE IntVector4D& operator+=(const IntVector4D &v); + FORCEINLINE IntVector4D& operator-=(const IntVector4D &v); + FORCEINLINE IntVector4D& operator*=(const IntVector4D &v); + FORCEINLINE IntVector4D& operator*=(float s); + FORCEINLINE IntVector4D& operator/=(const IntVector4D &v); + FORCEINLINE IntVector4D& operator/=(float s); + FORCEINLINE IntVector4D operator*(float fl) const; + +private: + + // No copy constructors allowed if we're in optimal mode + // IntVector4D(IntVector4D const& vOther); + + // No assignment operators either... + // IntVector4D& operator=( IntVector4D const& src ); + +}; + + + +//----------------------------------------------------------------------------- +// Allows us to specifically pass the vector by value when we need to +//----------------------------------------------------------------------------- +class VectorByValue : public Vector +{ +public: + // Construction/destruction: + VectorByValue(void) : Vector() {} + VectorByValue(vec_t X, vec_t Y, vec_t Z) : Vector( X, Y, Z ) {} + VectorByValue(const VectorByValue& vOther) { *this = vOther; } +}; + + +//----------------------------------------------------------------------------- +// Utility to simplify table construction. No constructor means can use +// traditional C-style initialization +//----------------------------------------------------------------------------- +class TableVector +{ +public: + vec_t x, y, z; + + operator Vector &() { return *((Vector *)(this)); } + operator const Vector &() const { return *((const Vector *)(this)); } + + // array access... + inline vec_t& operator[](int i) + { + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; + } + + inline vec_t operator[](int i) const + { + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; + } +}; + + +//----------------------------------------------------------------------------- +// Here's where we add all those lovely SSE optimized routines +//----------------------------------------------------------------------------- + +class ALIGN16 VectorAligned : public Vector +{ +public: + inline VectorAligned(void) {}; + inline VectorAligned(vec_t X, vec_t Y, vec_t Z) + { + Init(X,Y,Z); + } + +#ifdef VECTOR_NO_SLOW_OPERATIONS + +private: + // No copy constructors allowed if we're in optimal mode + VectorAligned(const VectorAligned& vOther); + VectorAligned(const Vector &vOther); + +#else +public: + explicit VectorAligned(const Vector &vOther) + { + Init(vOther.x, vOther.y, vOther.z); + } + + VectorAligned& operator=(const Vector &vOther) + { + Init(vOther.x, vOther.y, vOther.z); + return *this; + } + + VectorAligned& operator=(const VectorAligned &vOther) + { + // we know we're aligned, so use simd + // we can't use the convenient abstract interface coz it gets declared later +#ifdef _X360 + XMStoreVector4A(Base(), XMLoadVector4A(vOther.Base())); +#elif _WIN32 + _mm_store_ps(Base(), _mm_load_ps( vOther.Base() )); +#else + Init(vOther.x, vOther.y, vOther.z); +#endif + return *this; + } + + +#endif + float w; // this space is used anyway + + void* operator new[] ( size_t nSize) + { + return MemAlloc_AllocAligned(nSize, 16); + } + + void* operator new[] ( size_t nSize, const char *pFileName, int nLine) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + + void* operator new[] ( size_t nSize, int /*nBlockUse*/, const char *pFileName, int nLine) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + + void operator delete[] ( void* p) + { + MemAlloc_FreeAligned(p); + } + + void operator delete[] ( void* p, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + void operator delete[] ( void* p, int /*nBlockUse*/, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + // please don't allocate a single quaternion... + void* operator new ( size_t nSize ) + { + return MemAlloc_AllocAligned(nSize, 16); + } + void* operator new ( size_t nSize, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + void* operator new ( size_t nSize, int /*nBlockUse*/, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + void operator delete ( void* p) + { + MemAlloc_FreeAligned(p); + } + + void operator delete ( void* p, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + void operator delete ( void* p, int /*nBlockUse*/, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } +} ALIGN16_POST; + +//----------------------------------------------------------------------------- +// Vector related operations +//----------------------------------------------------------------------------- + +// Vector clear +FORCEINLINE void VectorClear( Vector& a ); + +// Copy +FORCEINLINE void VectorCopy( const Vector& src, Vector& dst ); + +// Vector arithmetic +FORCEINLINE void VectorAdd( const Vector& a, const Vector& b, Vector& result ); +FORCEINLINE void VectorSubtract( const Vector& a, const Vector& b, Vector& result ); +FORCEINLINE void VectorMultiply( const Vector& a, vec_t b, Vector& result ); +FORCEINLINE void VectorMultiply( const Vector& a, const Vector& b, Vector& result ); +FORCEINLINE void VectorDivide( const Vector& a, vec_t b, Vector& result ); +FORCEINLINE void VectorDivide( const Vector& a, const Vector& b, Vector& result ); +inline void VectorScale ( const Vector& in, vec_t scale, Vector& result ); +inline void VectorMA( const Vector& start, float scale, const Vector& direction, Vector& dest ); + +// Vector equality with tolerance +bool VectorsAreEqual( const Vector& src1, const Vector& src2, float tolerance = 0.0f ); + +#define VectorExpand(v) (v).x, (v).y, (v).z + + +// Normalization +// FIXME: Can't use quite yet +//vec_t VectorNormalize( Vector& v ); + +// Length +inline vec_t VectorLength( const Vector& v ); + +// Dot Product +FORCEINLINE vec_t DotProduct(const Vector& a, const Vector& b); + +// Cross product +void CrossProduct(const Vector& a, const Vector& b, Vector& result ); + +// Store the min or max of each of x, y, and z into the result. +void VectorMin( const Vector &a, const Vector &b, Vector &result ); +void VectorMax( const Vector &a, const Vector &b, Vector &result ); + +// Linearly interpolate between two vectors +void VectorLerp(const Vector& src1, const Vector& src2, vec_t t, Vector& dest ); +Vector VectorLerp(const Vector& src1, const Vector& src2, vec_t t ); + +FORCEINLINE Vector ReplicateToVector( float x ) +{ + return Vector( x, x, x ); +} + +// check if a point is in the field of a view of an object. +FORCEINLINE bool PointWithinViewAngle( Vector const &vecSrcPosition, + Vector const &vecTargetPosition, + Vector const &vecLookDirection, float flCosHalfFOV ) +{ + Vector vecDelta = vecTargetPosition - vecSrcPosition; + float cosDiff = DotProduct( vecLookDirection, vecDelta ); + + if ( flCosHalfFOV <= 0 ) // >180 + { + // signs are different, answer is implicit + if ( cosDiff > 0 ) + return true; + + // a/sqrt(b) > c == a^2 < b * c ^2 + // IFF left and right sides are <= 0 + float flLen2 = vecDelta.LengthSqr(); + return ( cosDiff * cosDiff <= flLen2 * flCosHalfFOV * flCosHalfFOV ); + } + else // flCosHalfFOV > 0 + { + // signs are different, answer is implicit + if ( cosDiff < 0 ) + return false; + + // a/sqrt(b) > c == a^2 > b * c ^2 + // IFF left and right sides are >= 0 + float flLen2 = vecDelta.LengthSqr(); + return ( cosDiff * cosDiff >= flLen2 * flCosHalfFOV * flCosHalfFOV ); + } +} + + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +// Cross product +Vector CrossProduct( const Vector& a, const Vector& b ); + +// Random vector creation +Vector RandomVector( vec_t minVal, vec_t maxVal ); + +#endif + +float RandomVectorInUnitSphere( Vector *pVector ); +float RandomVectorInUnitCircle( Vector2D *pVector ); + + +//----------------------------------------------------------------------------- +// +// Inlined Vector methods +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- +inline Vector::Vector(void) +{ +#ifdef _DEBUG +#ifdef VECTOR_PARANOIA + // Initialize to NAN to catch errors + x = y = z = VEC_T_NAN; +#endif +#endif +} + +inline Vector::Vector(vec_t X, vec_t Y, vec_t Z) +{ + x = X; y = Y; z = Z; + CHECK_VALID(*this); +} + +//inline Vector::Vector(const float *pFloat) +//{ +// Assert( pFloat ); +// x = pFloat[0]; y = pFloat[1]; z = pFloat[2]; +// CHECK_VALID(*this); +//} + +#if 0 +//----------------------------------------------------------------------------- +// copy constructor +//----------------------------------------------------------------------------- + +inline Vector::Vector(const Vector &vOther) +{ + CHECK_VALID(vOther); + x = vOther.x; y = vOther.y; z = vOther.z; +} +#endif + +//----------------------------------------------------------------------------- +// initialization +//----------------------------------------------------------------------------- + +inline void Vector::Init( vec_t ix, vec_t iy, vec_t iz ) +{ + x = ix; y = iy; z = iz; + CHECK_VALID(*this); +} + +inline void Vector::Random( vec_t minVal, vec_t maxVal ) +{ + x = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + y = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + z = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + CHECK_VALID(*this); +} + +// This should really be a single opcode on the PowerPC (move r0 onto the vec reg) +inline void Vector::Zero() +{ + x = y = z = 0.0f; +} + +inline void VectorClear( Vector& a ) +{ + a.x = a.y = a.z = 0.0f; +} + +//----------------------------------------------------------------------------- +// assignment +//----------------------------------------------------------------------------- + +inline Vector& Vector::operator=(const Vector &vOther) +{ + CHECK_VALID(vOther); + x=vOther.x; y=vOther.y; z=vOther.z; + return *this; +} + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline vec_t& Vector::operator[](int i) +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + +inline vec_t Vector::operator[](int i) const +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- +inline vec_t* Vector::Base() +{ + return (vec_t*)this; +} + +inline vec_t const* Vector::Base() const +{ + return (vec_t const*)this; +} + +//----------------------------------------------------------------------------- +// Cast to Vector2D... +//----------------------------------------------------------------------------- + +inline Vector2D& Vector::AsVector2D() +{ + return *(Vector2D*)this; +} + +inline const Vector2D& Vector::AsVector2D() const +{ + return *(const Vector2D*)this; +} + +//----------------------------------------------------------------------------- +// IsValid? +//----------------------------------------------------------------------------- + +inline bool Vector::IsValid() const +{ + return IsFinite(x) && IsFinite(y) && IsFinite(z); +} + +//----------------------------------------------------------------------------- +// Invalidate +//----------------------------------------------------------------------------- + +inline void Vector::Invalidate() +{ +//#ifdef _DEBUG +//#ifdef VECTOR_PARANOIA + x = y = z = VEC_T_NAN; +//#endif +//#endif +} + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- + +inline bool Vector::operator==( const Vector& src ) const +{ + CHECK_VALID(src); + CHECK_VALID(*this); + return (src.x == x) && (src.y == y) && (src.z == z); +} + +inline bool Vector::operator!=( const Vector& src ) const +{ + CHECK_VALID(src); + CHECK_VALID(*this); + return (src.x != x) || (src.y != y) || (src.z != z); +} + + +//----------------------------------------------------------------------------- +// Copy +//----------------------------------------------------------------------------- + +FORCEINLINE void VectorCopy( const Vector& src, Vector& dst ) +{ + CHECK_VALID(src); + dst.x = src.x; + dst.y = src.y; + dst.z = src.z; +} + +inline void Vector::CopyToArray(float* rgfl) const +{ + Assert( rgfl ); + CHECK_VALID(*this); + rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; +} + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- +// #pragma message("TODO: these should be SSE") + +inline void Vector::Negate() +{ + CHECK_VALID(*this); + x = -x; y = -y; z = -z; +} + +FORCEINLINE Vector& Vector::operator+=(const Vector& v) +{ + CHECK_VALID(*this); + CHECK_VALID(v); + x+=v.x; y+=v.y; z += v.z; + return *this; +} + +FORCEINLINE Vector& Vector::operator-=(const Vector& v) +{ + CHECK_VALID(*this); + CHECK_VALID(v); + x-=v.x; y-=v.y; z -= v.z; + return *this; +} + +FORCEINLINE Vector& Vector::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + CHECK_VALID(*this); + return *this; +} + +FORCEINLINE Vector& Vector::operator*=(const Vector& v) +{ + CHECK_VALID(v); + x *= v.x; + y *= v.y; + z *= v.z; + CHECK_VALID(*this); + return *this; +} + +// this ought to be an opcode. +FORCEINLINE Vector& Vector::operator+=(float fl) +{ + x += fl; + y += fl; + z += fl; + CHECK_VALID(*this); + return *this; +} + +FORCEINLINE Vector& Vector::operator-=(float fl) +{ + x -= fl; + y -= fl; + z -= fl; + CHECK_VALID(*this); + return *this; +} + + + +FORCEINLINE Vector& Vector::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + CHECK_VALID(*this); + return *this; +} + +FORCEINLINE Vector& Vector::operator/=(const Vector& v) +{ + CHECK_VALID(v); + Assert( v.x != 0.0f && v.y != 0.0f && v.z != 0.0f ); + x /= v.x; + y /= v.y; + z /= v.z; + CHECK_VALID(*this); + return *this; +} + + +// get the component of this vector parallel to some other given vector +inline Vector Vector::ProjectOnto( const Vector& onto ) +{ + return onto * ( this->Dot(onto) / ( onto.LengthSqr() ) ); +} + + +//----------------------------------------------------------------------------- +// +// Inlined Short Vector methods +// +//----------------------------------------------------------------------------- + + +inline void ShortVector::Init( short ix, short iy, short iz, short iw ) +{ + x = ix; y = iy; z = iz; w = iw; +} + +FORCEINLINE void ShortVector::Set( const ShortVector& vOther ) +{ + x = vOther.x; + y = vOther.y; + z = vOther.z; + w = vOther.w; +} + +FORCEINLINE void ShortVector::Set( const short ix, const short iy, const short iz, const short iw ) +{ + x = ix; + y = iy; + z = iz; + w = iw; +} + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline short ShortVector::operator[](int i) const +{ + Assert( (i >= 0) && (i < 4) ); + return ((short*)this)[i]; +} + +inline short& ShortVector::operator[](int i) +{ + Assert( (i >= 0) && (i < 4) ); + return ((short*)this)[i]; +} + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- +inline short* ShortVector::Base() +{ + return (short*)this; +} + +inline short const* ShortVector::Base() const +{ + return (short const*)this; +} + + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- + +inline bool ShortVector::operator==( const ShortVector& src ) const +{ + return (src.x == x) && (src.y == y) && (src.z == z) && (src.w == w); +} + +inline bool ShortVector::operator!=( const ShortVector& src ) const +{ + return (src.x != x) || (src.y != y) || (src.z != z) || (src.w != w); +} + + + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- + +FORCEINLINE ShortVector& ShortVector::operator+=(const ShortVector& v) +{ + x+=v.x; y+=v.y; z += v.z; w += v.w; + return *this; +} + +FORCEINLINE ShortVector& ShortVector::operator-=(const ShortVector& v) +{ + x-=v.x; y-=v.y; z -= v.z; w -= v.w; + return *this; +} + +FORCEINLINE ShortVector& ShortVector::operator*=(float fl) +{ + x = (short)(x * fl); + y = (short)(y * fl); + z = (short)(z * fl); + w = (short)(w * fl); + return *this; +} + +FORCEINLINE ShortVector& ShortVector::operator*=(const ShortVector& v) +{ + x = (short)(x * v.x); + y = (short)(y * v.y); + z = (short)(z * v.z); + w = (short)(w * v.w); + return *this; +} + +FORCEINLINE ShortVector& ShortVector::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x = (short)(x * oofl); + y = (short)(y * oofl); + z = (short)(z * oofl); + w = (short)(w * oofl); + return *this; +} + +FORCEINLINE ShortVector& ShortVector::operator/=(const ShortVector& v) +{ + Assert( v.x != 0 && v.y != 0 && v.z != 0 && v.w != 0 ); + x = (short)(x / v.x); + y = (short)(y / v.y); + z = (short)(z / v.z); + w = (short)(w / v.w); + return *this; +} + +FORCEINLINE void ShortVectorMultiply( const ShortVector& src, float fl, ShortVector& res ) +{ + Assert( IsFinite(fl) ); + res.x = (short)(src.x * fl); + res.y = (short)(src.y * fl); + res.z = (short)(src.z * fl); + res.w = (short)(src.w * fl); +} + +FORCEINLINE ShortVector ShortVector::operator*(float fl) const +{ + ShortVector res; + ShortVectorMultiply( *this, fl, res ); + return res; +} + + + + + + +//----------------------------------------------------------------------------- +// +// Inlined Integer Vector methods +// +//----------------------------------------------------------------------------- + + +inline void IntVector4D::Init( int ix, int iy, int iz, int iw ) +{ + x = ix; y = iy; z = iz; w = iw; +} + +FORCEINLINE void IntVector4D::Set( const IntVector4D& vOther ) +{ + x = vOther.x; + y = vOther.y; + z = vOther.z; + w = vOther.w; +} + +FORCEINLINE void IntVector4D::Set( const int ix, const int iy, const int iz, const int iw ) +{ + x = ix; + y = iy; + z = iz; + w = iw; +} + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline int IntVector4D::operator[](int i) const +{ + Assert( (i >= 0) && (i < 4) ); + return ((int*)this)[i]; +} + +inline int& IntVector4D::operator[](int i) +{ + Assert( (i >= 0) && (i < 4) ); + return ((int*)this)[i]; +} + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- +inline int* IntVector4D::Base() +{ + return (int*)this; +} + +inline int const* IntVector4D::Base() const +{ + return (int const*)this; +} + + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- + +inline bool IntVector4D::operator==( const IntVector4D& src ) const +{ + return (src.x == x) && (src.y == y) && (src.z == z) && (src.w == w); +} + +inline bool IntVector4D::operator!=( const IntVector4D& src ) const +{ + return (src.x != x) || (src.y != y) || (src.z != z) || (src.w != w); +} + + + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- + +FORCEINLINE IntVector4D& IntVector4D::operator+=(const IntVector4D& v) +{ + x+=v.x; y+=v.y; z += v.z; w += v.w; + return *this; +} + +FORCEINLINE IntVector4D& IntVector4D::operator-=(const IntVector4D& v) +{ + x-=v.x; y-=v.y; z -= v.z; w -= v.w; + return *this; +} + +FORCEINLINE IntVector4D& IntVector4D::operator*=(float fl) +{ + x = (int)(x * fl); + y = (int)(y * fl); + z = (int)(z * fl); + w = (int)(w * fl); + return *this; +} + +FORCEINLINE IntVector4D& IntVector4D::operator*=(const IntVector4D& v) +{ + x = (int)(x * v.x); + y = (int)(y * v.y); + z = (int)(z * v.z); + w = (int)(w * v.w); + return *this; +} + +FORCEINLINE IntVector4D& IntVector4D::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x = (int)(x * oofl); + y = (int)(y * oofl); + z = (int)(z * oofl); + w = (int)(w * oofl); + return *this; +} + +FORCEINLINE IntVector4D& IntVector4D::operator/=(const IntVector4D& v) +{ + Assert( v.x != 0 && v.y != 0 && v.z != 0 && v.w != 0 ); + x = (int)(x / v.x); + y = (int)(y / v.y); + z = (int)(z / v.z); + w = (int)(w / v.w); + return *this; +} + +FORCEINLINE void IntVector4DMultiply( const IntVector4D& src, float fl, IntVector4D& res ) +{ + Assert( IsFinite(fl) ); + res.x = (int)(src.x * fl); + res.y = (int)(src.y * fl); + res.z = (int)(src.z * fl); + res.w = (int)(src.w * fl); +} + +FORCEINLINE IntVector4D IntVector4D::operator*(float fl) const +{ + IntVector4D res; + IntVector4DMultiply( *this, fl, res ); + return res; +} + + + +// ======================= + + +FORCEINLINE void VectorAdd( const Vector& a, const Vector& b, Vector& c ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; +} + +FORCEINLINE void VectorSubtract( const Vector& a, const Vector& b, Vector& c ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; +} + +FORCEINLINE void VectorMultiply( const Vector& a, vec_t b, Vector& c ) +{ + CHECK_VALID(a); + Assert( IsFinite(b) ); + c.x = a.x * b; + c.y = a.y * b; + c.z = a.z * b; +} + +FORCEINLINE void VectorMultiply( const Vector& a, const Vector& b, Vector& c ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; +} + +// for backwards compatability +inline void VectorScale ( const Vector& in, vec_t scale, Vector& result ) +{ + VectorMultiply( in, scale, result ); +} + + +FORCEINLINE void VectorDivide( const Vector& a, vec_t b, Vector& c ) +{ + CHECK_VALID(a); + Assert( b != 0.0f ); + vec_t oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; + c.z = a.z * oob; +} + +FORCEINLINE void VectorDivide( const Vector& a, const Vector& b, Vector& c ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + Assert( (b.x != 0.0f) && (b.y != 0.0f) && (b.z != 0.0f) ); + c.x = a.x / b.x; + c.y = a.y / b.y; + c.z = a.z / b.z; +} + +// FIXME: Remove +// For backwards compatability +inline void Vector::MulAdd(const Vector& a, const Vector& b, float scalar) +{ + CHECK_VALID(a); + CHECK_VALID(b); + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; + z = a.z + b.z * scalar; +} + +inline void VectorLerp(const Vector& src1, const Vector& src2, vec_t t, Vector& dest ) +{ + CHECK_VALID(src1); + CHECK_VALID(src2); + dest.x = src1.x + (src2.x - src1.x) * t; + dest.y = src1.y + (src2.y - src1.y) * t; + dest.z = src1.z + (src2.z - src1.z) * t; +} + +inline Vector VectorLerp(const Vector& src1, const Vector& src2, vec_t t ) +{ + Vector result; + VectorLerp( src1, src2, t, result ); + return result; +} + +//----------------------------------------------------------------------------- +// Temporary storage for vector results so const Vector& results can be returned +//----------------------------------------------------------------------------- +inline Vector &AllocTempVector() +{ + static Vector s_vecTemp[128]; + static CInterlockedInt s_nIndex; + + int nIndex; + for (;;) + { + int nOldIndex = s_nIndex; + nIndex = ( (nOldIndex + 0x10001) & 0x7F ); + + if ( s_nIndex.AssignIf( nOldIndex, nIndex ) ) + { + break; + } + ThreadPause(); + } + return s_vecTemp[nIndex & 0xffff]; +} + + + +//----------------------------------------------------------------------------- +// dot, cross +//----------------------------------------------------------------------------- +FORCEINLINE vec_t DotProduct(const Vector& a, const Vector& b) +{ + CHECK_VALID(a); + CHECK_VALID(b); + return( a.x*b.x + a.y*b.y + a.z*b.z ); +} + +// for backwards compatability +inline vec_t Vector::Dot( const Vector& vOther ) const +{ + CHECK_VALID(vOther); + return DotProduct( *this, vOther ); +} + +inline int Vector::LargestComponent() const +{ + float flAbsx = fabs(x); + float flAbsy = fabs(y); + float flAbsz = fabs(z); + if ( flAbsx > flAbsy ) + { + if ( flAbsx > flAbsz ) + return X_INDEX; + return Z_INDEX; + } + if ( flAbsy > flAbsz ) + return Y_INDEX; + return Z_INDEX; +} + +inline void CrossProduct(const Vector& a, const Vector& b, Vector& result ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + Assert( &a != &result ); + Assert( &b != &result ); + result.x = a.y*b.z - a.z*b.y; + result.y = a.z*b.x - a.x*b.z; + result.z = a.x*b.y - a.y*b.x; +} + +inline vec_t DotProductAbs( const Vector &v0, const Vector &v1 ) +{ + CHECK_VALID(v0); + CHECK_VALID(v1); + return FloatMakePositive(v0.x*v1.x) + FloatMakePositive(v0.y*v1.y) + FloatMakePositive(v0.z*v1.z); +} + +inline vec_t DotProductAbs( const Vector &v0, const float *v1 ) +{ + return FloatMakePositive(v0.x * v1[0]) + FloatMakePositive(v0.y * v1[1]) + FloatMakePositive(v0.z * v1[2]); +} + +//----------------------------------------------------------------------------- +// length +//----------------------------------------------------------------------------- + +inline vec_t VectorLength( const Vector& v ) +{ + CHECK_VALID(v); + return (vec_t)FastSqrt(v.x*v.x + v.y*v.y + v.z*v.z); +} + + +inline vec_t Vector::Length(void) const +{ + CHECK_VALID(*this); + return VectorLength( *this ); +} + + +//----------------------------------------------------------------------------- +// Normalization +//----------------------------------------------------------------------------- + +/* +// FIXME: Can't use until we're un-macroed in mathlib.h +inline vec_t VectorNormalize( Vector& v ) +{ + Assert( v.IsValid() ); + vec_t l = v.Length(); + if (l != 0.0f) + { + v /= l; + } + else + { + // FIXME: + // Just copying the existing implemenation; shouldn't res.z == 0? + v.x = v.y = 0.0f; v.z = 1.0f; + } + return l; +} +*/ + + +// check a point against a box +bool Vector::WithinAABox( Vector const &boxmin, Vector const &boxmax) +{ + return ( + ( x >= boxmin.x ) && ( x <= boxmax.x) && + ( y >= boxmin.y ) && ( y <= boxmax.y) && + ( z >= boxmin.z ) && ( z <= boxmax.z) + ); +} + +//----------------------------------------------------------------------------- +// Get the distance from this vector to the other one +//----------------------------------------------------------------------------- +inline vec_t Vector::DistTo(const Vector &vOther) const +{ + Vector delta; + VectorSubtract( *this, vOther, delta ); + return delta.Length(); +} + + +//----------------------------------------------------------------------------- +// Vector equality with tolerance +//----------------------------------------------------------------------------- +inline bool VectorsAreEqual( const Vector& src1, const Vector& src2, float tolerance ) +{ + if (FloatMakePositive(src1.x - src2.x) > tolerance) + return false; + if (FloatMakePositive(src1.y - src2.y) > tolerance) + return false; + return (FloatMakePositive(src1.z - src2.z) <= tolerance); +} + + +//----------------------------------------------------------------------------- +// Computes the closest point to vecTarget no farther than flMaxDist from vecStart +//----------------------------------------------------------------------------- +inline void ComputeClosestPoint( const Vector& vecStart, float flMaxDist, const Vector& vecTarget, Vector *pResult ) +{ + Vector vecDelta; + VectorSubtract( vecTarget, vecStart, vecDelta ); + float flDistSqr = vecDelta.LengthSqr(); + if ( flDistSqr <= flMaxDist * flMaxDist ) + { + *pResult = vecTarget; + } + else + { + vecDelta /= FastSqrt( flDistSqr ); + VectorMA( vecStart, flMaxDist, vecDelta, *pResult ); + } +} + + +//----------------------------------------------------------------------------- +// Takes the absolute value of a vector +//----------------------------------------------------------------------------- +inline void VectorAbs( const Vector& src, Vector& dst ) +{ + dst.x = FloatMakePositive(src.x); + dst.y = FloatMakePositive(src.y); + dst.z = FloatMakePositive(src.z); +} + + +//----------------------------------------------------------------------------- +// +// Slow methods +// +//----------------------------------------------------------------------------- + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +//----------------------------------------------------------------------------- +// Returns a vector with the min or max in X, Y, and Z. +//----------------------------------------------------------------------------- +inline Vector Vector::Min(const Vector &vOther) const +{ + return Vector(x < vOther.x ? x : vOther.x, + y < vOther.y ? y : vOther.y, + z < vOther.z ? z : vOther.z); +} + +inline Vector Vector::Max(const Vector &vOther) const +{ + return Vector(x > vOther.x ? x : vOther.x, + y > vOther.y ? y : vOther.y, + z > vOther.z ? z : vOther.z); +} + + +//----------------------------------------------------------------------------- +// arithmetic operations +//----------------------------------------------------------------------------- + +inline Vector Vector::operator-(void) const +{ + return Vector(-x,-y,-z); +} + +inline Vector Vector::operator+(const Vector& v) const +{ + Vector res; + VectorAdd( *this, v, res ); + return res; +} + +inline Vector Vector::operator-(const Vector& v) const +{ + Vector res; + VectorSubtract( *this, v, res ); + return res; +} + +inline Vector Vector::operator*(float fl) const +{ + Vector res; + VectorMultiply( *this, fl, res ); + return res; +} + +inline Vector Vector::operator*(const Vector& v) const +{ + Vector res; + VectorMultiply( *this, v, res ); + return res; +} + +inline Vector Vector::operator/(float fl) const +{ + Vector res; + VectorDivide( *this, fl, res ); + return res; +} + +inline Vector Vector::operator/(const Vector& v) const +{ + Vector res; + VectorDivide( *this, v, res ); + return res; +} + +inline Vector operator*(float fl, const Vector& v) +{ + return v * fl; +} + +//----------------------------------------------------------------------------- +// cross product +//----------------------------------------------------------------------------- + +inline Vector Vector::Cross(const Vector& vOther) const +{ + Vector res; + CrossProduct( *this, vOther, res ); + return res; +} + +//----------------------------------------------------------------------------- +// 2D +//----------------------------------------------------------------------------- + +inline vec_t Vector::Length2D(void) const +{ + return (vec_t)FastSqrt(x*x + y*y); +} + +inline vec_t Vector::Length2DSqr(void) const +{ + return (x*x + y*y); +} + +inline Vector CrossProduct(const Vector& a, const Vector& b) +{ + return Vector( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); +} + +inline void VectorMin( const Vector &a, const Vector &b, Vector &result ) +{ + result.x = fpmin(a.x, b.x); + result.y = fpmin(a.y, b.y); + result.z = fpmin(a.z, b.z); +} + +inline void VectorMax( const Vector &a, const Vector &b, Vector &result ) +{ + result.x = fpmax(a.x, b.x); + result.y = fpmax(a.y, b.y); + result.z = fpmax(a.z, b.z); +} + +// and when you want to return the vector rather than cause a LHS with it... +inline Vector VectorMin( const Vector &a, const Vector &b ) +{ + return Vector( fpmin(a.x, b.x), fpmin(a.y, b.y), fpmin(a.z, b.z) ); +} + +inline Vector VectorMax( const Vector &a, const Vector &b ) +{ + return Vector( fpmax(a.x, b.x), fpmax(a.y, b.y), fpmax(a.z, b.z) ); +} + +inline float ComputeVolume( const Vector &vecMins, const Vector &vecMaxs ) +{ + Vector vecDelta; + VectorSubtract( vecMaxs, vecMins, vecDelta ); + return DotProduct( vecDelta, vecDelta ); +} + +// Get a random vector. +inline Vector RandomVector( float minVal, float maxVal ) +{ + Vector random; + random.Random( minVal, maxVal ); + return random; +} + +#endif //slow + +//----------------------------------------------------------------------------- +// Helper debugging stuff.... +//----------------------------------------------------------------------------- + +inline bool operator==( float const* f, const Vector& v ) +{ + // AIIIEEEE!!!! + Assert(0); + return false; +} + +inline bool operator==( const Vector& v, float const* f ) +{ + // AIIIEEEE!!!! + Assert(0); + return false; +} + +inline bool operator!=( float const* f, const Vector& v ) +{ + // AIIIEEEE!!!! + Assert(0); + return false; +} + +inline bool operator!=( const Vector& v, float const* f ) +{ + // AIIIEEEE!!!! + Assert(0); + return false; +} + + +// return a vector perpendicular to another, with smooth variation. The difference between this and +// something like VectorVectors is that there are now discontinuities. _unlike_ VectorVectors, +// you won't get an "u +void VectorPerpendicularToVector( Vector const &in, Vector *pvecOut ); + +//----------------------------------------------------------------------------- +// AngularImpulse +//----------------------------------------------------------------------------- +// AngularImpulse are exponetial maps (an axis scaled by a "twist" angle in degrees) +typedef Vector AngularImpulse; + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline AngularImpulse RandomAngularImpulse( float minVal, float maxVal ) +{ + AngularImpulse angImp; + angImp.Random( minVal, maxVal ); + return angImp; +} + +#endif + + +//----------------------------------------------------------------------------- +// Quaternion +//----------------------------------------------------------------------------- + +class RadianEuler; + +class Quaternion // same data-layout as engine's vec4_t, +{ // which is a vec_t[4] +public: + inline Quaternion(void) { + + // Initialize to NAN to catch errors +#ifdef _DEBUG +#ifdef VECTOR_PARANOIA + x = y = z = w = VEC_T_NAN; +#endif +#endif + } + inline Quaternion(vec_t ix, vec_t iy, vec_t iz, vec_t iw) : x(ix), y(iy), z(iz), w(iw) { } + inline Quaternion(RadianEuler const &angle); // evil auto type promotion!!! + + inline void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f, vec_t iw=0.0f) { x = ix; y = iy; z = iz; w = iw; } + + bool IsValid() const; + void Invalidate(); + + bool operator==( const Quaternion &src ) const; + bool operator!=( const Quaternion &src ) const; + + vec_t* Base() { return (vec_t*)this; } + const vec_t* Base() const { return (vec_t*)this; } + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + vec_t x, y, z, w; +}; + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline vec_t& Quaternion::operator[](int i) +{ + Assert( (i >= 0) && (i < 4) ); + return ((vec_t*)this)[i]; +} + +inline vec_t Quaternion::operator[](int i) const +{ + Assert( (i >= 0) && (i < 4) ); + return ((vec_t*)this)[i]; +} + + +//----------------------------------------------------------------------------- +// Equality test +//----------------------------------------------------------------------------- +inline bool Quaternion::operator==( const Quaternion &src ) const +{ + return ( x == src.x ) && ( y == src.y ) && ( z == src.z ) && ( w == src.w ); +} + +inline bool Quaternion::operator!=( const Quaternion &src ) const +{ + return !operator==( src ); +} + + +//----------------------------------------------------------------------------- +// Quaternion equality with tolerance +//----------------------------------------------------------------------------- +inline bool QuaternionsAreEqual( const Quaternion& src1, const Quaternion& src2, float tolerance ) +{ + if (FloatMakePositive(src1.x - src2.x) > tolerance) + return false; + if (FloatMakePositive(src1.y - src2.y) > tolerance) + return false; + if (FloatMakePositive(src1.z - src2.z) > tolerance) + return false; + return (FloatMakePositive(src1.w - src2.w) <= tolerance); +} + + +//----------------------------------------------------------------------------- +// Here's where we add all those lovely SSE optimized routines +//----------------------------------------------------------------------------- +class ALIGN16 QuaternionAligned : public Quaternion +{ +public: + inline QuaternionAligned(void) {}; + inline QuaternionAligned(vec_t X, vec_t Y, vec_t Z, vec_t W) + { + Init(X,Y,Z,W); + } + + operator Quaternion * () { return this; } + operator const Quaternion * () { return this; } + +#ifdef VECTOR_NO_SLOW_OPERATIONS + +private: + // No copy constructors allowed if we're in optimal mode + QuaternionAligned(const QuaternionAligned& vOther); + QuaternionAligned(const Quaternion &vOther); + +#else +public: + explicit QuaternionAligned(const Quaternion &vOther) + { + Init(vOther.x, vOther.y, vOther.z, vOther.w); + } + + QuaternionAligned& operator=(const Quaternion &vOther) + { + Init(vOther.x, vOther.y, vOther.z, vOther.w); + return *this; + } + + QuaternionAligned& operator=(const QuaternionAligned &vOther) + { + // we know we're aligned, so use simd + // we can't use the convenient abstract interface coz it gets declared later +#ifdef _X360 + XMStoreVector4A(Base(), XMLoadVector4A(vOther.Base())); +#elif _WIN32 + _mm_store_ps(Base(), _mm_load_ps( vOther.Base() )); +#else + Init(vOther.x, vOther.y, vOther.z, vOther.w); +#endif + return *this; + } + +#endif + + void* operator new[] ( size_t nSize) + { + return MemAlloc_AllocAligned(nSize, 16); + } + + void* operator new[] ( size_t nSize, const char *pFileName, int nLine) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + + void* operator new[] ( size_t nSize, int /*nBlockUse*/, const char *pFileName, int nLine) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + + void operator delete[] ( void* p) + { + MemAlloc_FreeAligned(p); + } + + void operator delete[] ( void* p, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + void operator delete[] ( void* p, int /*nBlockUse*/, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + // please don't allocate a single quaternion... + void* operator new ( size_t nSize ) + { + return MemAlloc_AllocAligned(nSize, 16); + } + void* operator new ( size_t nSize, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + void* operator new ( size_t nSize, int /*nBlockUse*/, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine(nSize, 16, pFileName, nLine); + } + void operator delete ( void* p) + { + MemAlloc_FreeAligned(p); + } + + void operator delete ( void* p, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } + + void operator delete ( void* p, int /*nBlockUse*/, const char *pFileName, int nLine) + { + MemAlloc_FreeAligned(p, pFileName, nLine); + } +} ALIGN16_POST; + + +//----------------------------------------------------------------------------- +// Radian Euler angle aligned to axis (NOT ROLL/PITCH/YAW) +//----------------------------------------------------------------------------- +class QAngle; +class RadianEuler +{ +public: + inline RadianEuler(void) { } + inline RadianEuler(vec_t X, vec_t Y, vec_t Z) { x = X; y = Y; z = Z; } + inline RadianEuler(Quaternion const &q); // evil auto type promotion!!! + inline RadianEuler(QAngle const &angles); // evil auto type promotion!!! + + // Initialization + inline void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f) { x = ix; y = iy; z = iz; } + + // conversion to qangle + QAngle ToQAngle( void ) const; + bool IsValid() const; + void Invalidate(); + + inline vec_t *Base() { return &x; } + inline const vec_t *Base() const { return &x; } + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + vec_t x, y, z; +}; + + +extern void AngleQuaternion( RadianEuler const &angles, Quaternion &qt ); +extern void QuaternionAngles( Quaternion const &q, RadianEuler &angles ); +inline Quaternion::Quaternion(RadianEuler const &angle) +{ + AngleQuaternion( angle, *this ); +} + +inline bool Quaternion::IsValid() const +{ + return IsFinite(x) && IsFinite(y) && IsFinite(z) && IsFinite(w); +} + +inline void Quaternion::Invalidate() +{ +//#ifdef _DEBUG +//#ifdef VECTOR_PARANOIA + x = y = z = w = VEC_T_NAN; +//#endif +//#endif +} + +inline RadianEuler::RadianEuler(Quaternion const &q) +{ + QuaternionAngles( q, *this ); +} + +inline void VectorCopy( RadianEuler const& src, RadianEuler &dst ) +{ + CHECK_VALID(src); + dst.x = src.x; + dst.y = src.y; + dst.z = src.z; +} + +inline void VectorScale( RadianEuler const& src, float b, RadianEuler &dst ) +{ + CHECK_VALID(src); + Assert( IsFinite(b) ); + dst.x = src.x * b; + dst.y = src.y * b; + dst.z = src.z * b; +} + +inline bool RadianEuler::IsValid() const +{ + return IsFinite(x) && IsFinite(y) && IsFinite(z); +} + +inline void RadianEuler::Invalidate() +{ +//#ifdef _DEBUG +//#ifdef VECTOR_PARANOIA + x = y = z = VEC_T_NAN; +//#endif +//#endif +} + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline vec_t& RadianEuler::operator[](int i) +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + +inline vec_t RadianEuler::operator[](int i) const +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + + +//----------------------------------------------------------------------------- +// Degree Euler QAngle pitch, yaw, roll +//----------------------------------------------------------------------------- +class QAngleByValue; + +class QAngle +{ +public: + // Members + vec_t x, y, z; + + // Construction/destruction + QAngle(void); + QAngle(vec_t X, vec_t Y, vec_t Z); +// QAngle(RadianEuler const &angles); // evil auto type promotion!!! + + // Allow pass-by-value + operator QAngleByValue &() { return *((QAngleByValue *)(this)); } + operator const QAngleByValue &() const { return *((const QAngleByValue *)(this)); } + + // Initialization + void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f); + void Random( vec_t minVal, vec_t maxVal ); + + // Got any nasty NAN's? + bool IsValid() const; + void Invalidate(); + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + // Base address... + vec_t* Base(); + vec_t const* Base() const; + + // equality + bool operator==(const QAngle& v) const; + bool operator!=(const QAngle& v) const; + + // arithmetic operations + QAngle& operator+=(const QAngle &v); + QAngle& operator-=(const QAngle &v); + QAngle& operator*=(float s); + QAngle& operator/=(float s); + + // Get the vector's magnitude. + vec_t Length() const; + vec_t LengthSqr() const; + + // negate the QAngle components + //void Negate(); + + // No assignment operators either... + QAngle& operator=( const QAngle& src ); + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // copy constructors + + // arithmetic operations + QAngle operator-(void) const; + + QAngle operator+(const QAngle& v) const; + QAngle operator-(const QAngle& v) const; + QAngle operator*(float fl) const; + QAngle operator/(float fl) const; +#else + +private: + // No copy constructors allowed if we're in optimal mode + QAngle(const QAngle& vOther); + +#endif +}; + +//----------------------------------------------------------------------------- +// Allows us to specifically pass the vector by value when we need to +//----------------------------------------------------------------------------- +class QAngleByValue : public QAngle +{ +public: + // Construction/destruction: + QAngleByValue(void) : QAngle() {} + QAngleByValue(vec_t X, vec_t Y, vec_t Z) : QAngle( X, Y, Z ) {} + QAngleByValue(const QAngleByValue& vOther) { *this = vOther; } +}; + + +inline void VectorAdd( const QAngle& a, const QAngle& b, QAngle& result ) +{ + CHECK_VALID(a); + CHECK_VALID(b); + result.x = a.x + b.x; + result.y = a.y + b.y; + result.z = a.z + b.z; +} + +inline void VectorMA( const QAngle &start, float scale, const QAngle &direction, QAngle &dest ) +{ + CHECK_VALID(start); + CHECK_VALID(direction); + dest.x = start.x + scale * direction.x; + dest.y = start.y + scale * direction.y; + dest.z = start.z + scale * direction.z; +} + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- +inline QAngle::QAngle(void) +{ +#ifdef _DEBUG +#ifdef VECTOR_PARANOIA + // Initialize to NAN to catch errors + x = y = z = VEC_T_NAN; +#endif +#endif +} + +inline QAngle::QAngle(vec_t X, vec_t Y, vec_t Z) +{ + x = X; y = Y; z = Z; + CHECK_VALID(*this); +} + + +//----------------------------------------------------------------------------- +// initialization +//----------------------------------------------------------------------------- +inline void QAngle::Init( vec_t ix, vec_t iy, vec_t iz ) +{ + x = ix; y = iy; z = iz; + CHECK_VALID(*this); +} + +inline void QAngle::Random( vec_t minVal, vec_t maxVal ) +{ + x = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + y = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + z = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + CHECK_VALID(*this); +} + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline QAngle RandomAngle( float minVal, float maxVal ) +{ + Vector random; + random.Random( minVal, maxVal ); + QAngle ret( random.x, random.y, random.z ); + return ret; +} + +#endif + + +inline RadianEuler::RadianEuler(QAngle const &angles) +{ + Init( + angles.z * 3.14159265358979323846f / 180.f, + angles.x * 3.14159265358979323846f / 180.f, + angles.y * 3.14159265358979323846f / 180.f ); +} + + + + +inline QAngle RadianEuler::ToQAngle( void) const +{ + return QAngle( + y * 180.f / 3.14159265358979323846f, + z * 180.f / 3.14159265358979323846f, + x * 180.f / 3.14159265358979323846f ); +} + + +//----------------------------------------------------------------------------- +// assignment +//----------------------------------------------------------------------------- +inline QAngle& QAngle::operator=(const QAngle &vOther) +{ + CHECK_VALID(vOther); + x=vOther.x; y=vOther.y; z=vOther.z; + return *this; +} + + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- +inline vec_t& QAngle::operator[](int i) +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + +inline vec_t QAngle::operator[](int i) const +{ + Assert( (i >= 0) && (i < 3) ); + return ((vec_t*)this)[i]; +} + + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- +inline vec_t* QAngle::Base() +{ + return (vec_t*)this; +} + +inline vec_t const* QAngle::Base() const +{ + return (vec_t const*)this; +} + + +//----------------------------------------------------------------------------- +// IsValid? +//----------------------------------------------------------------------------- +inline bool QAngle::IsValid() const +{ + return IsFinite(x) && IsFinite(y) && IsFinite(z); +} + +//----------------------------------------------------------------------------- +// Invalidate +//----------------------------------------------------------------------------- + +inline void QAngle::Invalidate() +{ +//#ifdef _DEBUG +//#ifdef VECTOR_PARANOIA + x = y = z = VEC_T_NAN; +//#endif +//#endif +} + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- +inline bool QAngle::operator==( const QAngle& src ) const +{ + CHECK_VALID(src); + CHECK_VALID(*this); + return (src.x == x) && (src.y == y) && (src.z == z); +} + +inline bool QAngle::operator!=( const QAngle& src ) const +{ + CHECK_VALID(src); + CHECK_VALID(*this); + return (src.x != x) || (src.y != y) || (src.z != z); +} + + +//----------------------------------------------------------------------------- +// Copy +//----------------------------------------------------------------------------- +inline void VectorCopy( const QAngle& src, QAngle& dst ) +{ + CHECK_VALID(src); + dst.x = src.x; + dst.y = src.y; + dst.z = src.z; +} + + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- +inline QAngle& QAngle::operator+=(const QAngle& v) +{ + CHECK_VALID(*this); + CHECK_VALID(v); + x+=v.x; y+=v.y; z += v.z; + return *this; +} + +inline QAngle& QAngle::operator-=(const QAngle& v) +{ + CHECK_VALID(*this); + CHECK_VALID(v); + x-=v.x; y-=v.y; z -= v.z; + return *this; +} + +inline QAngle& QAngle::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + CHECK_VALID(*this); + return *this; +} + +inline QAngle& QAngle::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + CHECK_VALID(*this); + return *this; +} + + +//----------------------------------------------------------------------------- +// length +//----------------------------------------------------------------------------- +inline vec_t QAngle::Length( ) const +{ + CHECK_VALID(*this); + return (vec_t)FastSqrt( LengthSqr( ) ); +} + + +inline vec_t QAngle::LengthSqr( ) const +{ + CHECK_VALID(*this); + return x * x + y * y + z * z; +} + + +//----------------------------------------------------------------------------- +// Vector equality with tolerance +//----------------------------------------------------------------------------- +inline bool QAnglesAreEqual( const QAngle& src1, const QAngle& src2, float tolerance = 0.0f ) +{ + if (FloatMakePositive(src1.x - src2.x) > tolerance) + return false; + if (FloatMakePositive(src1.y - src2.y) > tolerance) + return false; + return (FloatMakePositive(src1.z - src2.z) <= tolerance); +} + + +//----------------------------------------------------------------------------- +// arithmetic operations (SLOW!!) +//----------------------------------------------------------------------------- +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline QAngle QAngle::operator-(void) const +{ + QAngle ret(-x,-y,-z); + return ret; +} + +inline QAngle QAngle::operator+(const QAngle& v) const +{ + QAngle res; + res.x = x + v.x; + res.y = y + v.y; + res.z = z + v.z; + return res; +} + +inline QAngle QAngle::operator-(const QAngle& v) const +{ + QAngle res; + res.x = x - v.x; + res.y = y - v.y; + res.z = z - v.z; + return res; +} + +inline QAngle QAngle::operator*(float fl) const +{ + QAngle res; + res.x = x * fl; + res.y = y * fl; + res.z = z * fl; + return res; +} + +inline QAngle QAngle::operator/(float fl) const +{ + QAngle res; + res.x = x / fl; + res.y = y / fl; + res.z = z / fl; + return res; +} + +inline QAngle operator*(float fl, const QAngle& v) +{ + QAngle ret( v * fl ); + return ret; +} + +#endif // VECTOR_NO_SLOW_OPERATIONS + + +//----------------------------------------------------------------------------- +// NOTE: These are not completely correct. The representations are not equivalent +// unless the QAngle represents a rotational impulse along a coordinate axis (x,y,z) +inline void QAngleToAngularImpulse( const QAngle &angles, AngularImpulse &impulse ) +{ + impulse.x = angles.z; + impulse.y = angles.x; + impulse.z = angles.y; +} + +inline void AngularImpulseToQAngle( const AngularImpulse &impulse, QAngle &angles ) +{ + angles.x = impulse.y; + angles.y = impulse.z; + angles.z = impulse.x; +} + +#if !defined( _X360 ) + +FORCEINLINE vec_t InvRSquared( const float* v ) +{ + return 1.0 / MAX( 1.0, v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); +} + +FORCEINLINE vec_t InvRSquared( const Vector &v ) +{ + return InvRSquared( v.Base() ); +} + +#else + +// call directly +FORCEINLINE float _VMX_InvRSquared( const Vector &v ) +{ + XMVECTOR xmV = XMVector3ReciprocalLength( XMLoadVector3( v.Base() ) ); + xmV = XMVector3Dot( xmV, xmV ); + return xmV.x; +} + +#define InvRSquared(x) _VMX_InvRSquared(x) + +#endif // _X360 + +#if !defined( _X360 ) + +// FIXME: Change this back to a #define once we get rid of the vec_t version +float VectorNormalize( Vector& v ); + +// FIXME: Obsolete version of VectorNormalize, once we remove all the friggin float*s +FORCEINLINE float VectorNormalize( float * v ) +{ + return VectorNormalize(*(reinterpret_cast(v))); +} + +#else + +// call directly +FORCEINLINE float _VMX_VectorNormalize( Vector &vec ) +{ + float mag = XMVector3Length( XMLoadVector3( vec.Base() ) ).x; + float den = 1.f / (mag + FLT_EPSILON ); + vec.x *= den; + vec.y *= den; + vec.z *= den; + return mag; +} +// FIXME: Change this back to a #define once we get rid of the vec_t version +FORCEINLINE float VectorNormalize( Vector& v ) +{ + return _VMX_VectorNormalize( v ); +} +// FIXME: Obsolete version of VectorNormalize, once we remove all the friggin float*s +FORCEINLINE float VectorNormalize( float *pV ) +{ + return _VMX_VectorNormalize(*(reinterpret_cast(pV))); +} + +#endif // _X360 + +#if !defined( _X360 ) +FORCEINLINE void VectorNormalizeFast (Vector& vec) +{ + float ool = FastRSqrt( FLT_EPSILON + vec.x * vec.x + vec.y * vec.y + vec.z * vec.z ); + + vec.x *= ool; + vec.y *= ool; + vec.z *= ool; +} +#else + +// call directly +FORCEINLINE void VectorNormalizeFast( Vector &vec ) +{ + XMVECTOR xmV = XMVector3LengthEst( XMLoadVector3( vec.Base() ) ); + float den = 1.f / (xmV.x + FLT_EPSILON); + vec.x *= den; + vec.y *= den; + vec.z *= den; +} + +#endif // _X360 + +inline vec_t Vector::NormalizeInPlace() +{ + return VectorNormalize( *this ); +} + +inline Vector Vector::Normalized() const +{ + Vector norm = *this; + VectorNormalize( norm ); + return norm; +} + +inline bool Vector::IsLengthGreaterThan( float val ) const +{ + return LengthSqr() > val*val; +} + +inline bool Vector::IsLengthLessThan( float val ) const +{ + return LengthSqr() < val*val; +} + +#endif + diff --git a/public/mathlib/vector2d.h b/public/mathlib/vector2d.h new file mode 100644 index 0000000..4eef4b2 --- /dev/null +++ b/public/mathlib/vector2d.h @@ -0,0 +1,670 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef VECTOR2D_H +#define VECTOR2D_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// For vec_t, put this somewhere else? +#include "tier0/basetypes.h" + +// For rand(). We really need a library! +#include + +#include "tier0/dbg.h" +#include "mathlib/math_pfns.h" + +//========================================================= +// 2D Vector2D +//========================================================= + +class Vector2D +{ +public: + // Members + vec_t x, y; + + // Construction/destruction + Vector2D(void); + Vector2D(vec_t X, vec_t Y); + Vector2D(const float *pFloat); + + // Initialization + void Init(vec_t ix=0.0f, vec_t iy=0.0f); + + // Got any nasty NAN's? + bool IsValid() const; + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + // Base address... + vec_t* Base(); + vec_t const* Base() const; + + // Initialization methods + void Random( float minVal, float maxVal ); + + // equality + bool operator==(const Vector2D& v) const; + bool operator!=(const Vector2D& v) const; + + // arithmetic operations + Vector2D& operator+=(const Vector2D &v); + Vector2D& operator-=(const Vector2D &v); + Vector2D& operator*=(const Vector2D &v); + Vector2D& operator*=(float s); + Vector2D& operator/=(const Vector2D &v); + Vector2D& operator/=(float s); + + // negate the Vector2D components + void Negate(); + + // Get the Vector2D's magnitude. + vec_t Length() const; + + // Get the Vector2D's magnitude squared. + vec_t LengthSqr(void) const; + + // return true if this vector is (0,0) within tolerance + bool IsZero( float tolerance = 0.01f ) const + { + return (x > -tolerance && x < tolerance && + y > -tolerance && y < tolerance); + } + + // Normalize in place and return the old length. + vec_t NormalizeInPlace(); + + // Compare length. + bool IsLengthGreaterThan( float val ) const; + bool IsLengthLessThan( float val ) const; + + // Get the distance from this Vector2D to the other one. + vec_t DistTo(const Vector2D &vOther) const; + + // Get the distance from this Vector2D to the other one squared. + vec_t DistToSqr(const Vector2D &vOther) const; + + // Copy + void CopyToArray(float* rgfl) const; + + // Multiply, add, and assign to this (ie: *this = a + b * scalar). This + // is about 12% faster than the actual Vector2D equation (because it's done per-component + // rather than per-Vector2D). + void MulAdd(const Vector2D& a, const Vector2D& b, float scalar); + + // Dot product. + vec_t Dot(const Vector2D& vOther) const; + + // assignment + Vector2D& operator=(const Vector2D &vOther); + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // copy constructors + Vector2D(const Vector2D &vOther); + + // arithmetic operations + Vector2D operator-(void) const; + + Vector2D operator+(const Vector2D& v) const; + Vector2D operator-(const Vector2D& v) const; + Vector2D operator*(const Vector2D& v) const; + Vector2D operator/(const Vector2D& v) const; + Vector2D operator*(float fl) const; + Vector2D operator/(float fl) const; + + // Cross product between two vectors. + Vector2D Cross(const Vector2D &vOther) const; + + // Returns a Vector2D with the min or max in X, Y, and Z. + Vector2D Min(const Vector2D &vOther) const; + Vector2D Max(const Vector2D &vOther) const; + +#else + +private: + // No copy constructors allowed if we're in optimal mode + Vector2D(const Vector2D& vOther); +#endif +}; + +//----------------------------------------------------------------------------- + +const Vector2D vec2_origin(0,0); +const Vector2D vec2_invalid( FLT_MAX, FLT_MAX ); + +//----------------------------------------------------------------------------- +// Vector2D related operations +//----------------------------------------------------------------------------- + +// Vector2D clear +void Vector2DClear( Vector2D& a ); + +// Copy +void Vector2DCopy( const Vector2D& src, Vector2D& dst ); + +// Vector2D arithmetic +void Vector2DAdd( const Vector2D& a, const Vector2D& b, Vector2D& result ); +void Vector2DSubtract( const Vector2D& a, const Vector2D& b, Vector2D& result ); +void Vector2DMultiply( const Vector2D& a, vec_t b, Vector2D& result ); +void Vector2DMultiply( const Vector2D& a, const Vector2D& b, Vector2D& result ); +void Vector2DDivide( const Vector2D& a, vec_t b, Vector2D& result ); +void Vector2DDivide( const Vector2D& a, const Vector2D& b, Vector2D& result ); +void Vector2DMA( const Vector2D& start, float s, const Vector2D& dir, Vector2D& result ); + +// Store the min or max of each of x, y, and z into the result. +void Vector2DMin( const Vector2D &a, const Vector2D &b, Vector2D &result ); +void Vector2DMax( const Vector2D &a, const Vector2D &b, Vector2D &result ); + +#define Vector2DExpand( v ) (v).x, (v).y + +// Normalization +vec_t Vector2DNormalize( Vector2D& v ); + +// Length +vec_t Vector2DLength( const Vector2D& v ); + +// Dot Product +vec_t DotProduct2D(const Vector2D& a, const Vector2D& b); + +// Linearly interpolate between two vectors +void Vector2DLerp(const Vector2D& src1, const Vector2D& src2, vec_t t, Vector2D& dest ); + + +//----------------------------------------------------------------------------- +// +// Inlined Vector2D methods +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- + +inline Vector2D::Vector2D(void) +{ +#ifdef _DEBUG + // Initialize to NAN to catch errors + x = y = VEC_T_NAN; +#endif +} + +inline Vector2D::Vector2D(vec_t X, vec_t Y) +{ + x = X; y = Y; + Assert( IsValid() ); +} + +inline Vector2D::Vector2D(const float *pFloat) +{ + Assert( pFloat ); + x = pFloat[0]; y = pFloat[1]; + Assert( IsValid() ); +} + + +//----------------------------------------------------------------------------- +// copy constructor +//----------------------------------------------------------------------------- + +inline Vector2D::Vector2D(const Vector2D &vOther) +{ + Assert( vOther.IsValid() ); + x = vOther.x; y = vOther.y; +} + +//----------------------------------------------------------------------------- +// initialization +//----------------------------------------------------------------------------- + +inline void Vector2D::Init( vec_t ix, vec_t iy ) +{ + x = ix; y = iy; + Assert( IsValid() ); +} + +inline void Vector2D::Random( float minVal, float maxVal ) +{ + x = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); + y = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal); +} + +inline void Vector2DClear( Vector2D& a ) +{ + a.x = a.y = 0.0f; +} + +//----------------------------------------------------------------------------- +// assignment +//----------------------------------------------------------------------------- + +inline Vector2D& Vector2D::operator=(const Vector2D &vOther) +{ + Assert( vOther.IsValid() ); + x=vOther.x; y=vOther.y; + return *this; +} + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- + +inline vec_t& Vector2D::operator[](int i) +{ + Assert( (i >= 0) && (i < 2) ); + return ((vec_t*)this)[i]; +} + +inline vec_t Vector2D::operator[](int i) const +{ + Assert( (i >= 0) && (i < 2) ); + return ((vec_t*)this)[i]; +} + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- + +inline vec_t* Vector2D::Base() +{ + return (vec_t*)this; +} + +inline vec_t const* Vector2D::Base() const +{ + return (vec_t const*)this; +} + +//----------------------------------------------------------------------------- +// IsValid? +//----------------------------------------------------------------------------- + +inline bool Vector2D::IsValid() const +{ + return IsFinite(x) && IsFinite(y); +} + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- + +inline bool Vector2D::operator==( const Vector2D& src ) const +{ + Assert( src.IsValid() && IsValid() ); + return (src.x == x) && (src.y == y); +} + +inline bool Vector2D::operator!=( const Vector2D& src ) const +{ + Assert( src.IsValid() && IsValid() ); + return (src.x != x) || (src.y != y); +} + + +//----------------------------------------------------------------------------- +// Copy +//----------------------------------------------------------------------------- + +inline void Vector2DCopy( const Vector2D& src, Vector2D& dst ) +{ + Assert( src.IsValid() ); + dst.x = src.x; + dst.y = src.y; +} + +inline void Vector2D::CopyToArray(float* rgfl) const +{ + Assert( IsValid() ); + Assert( rgfl ); + rgfl[0] = x; rgfl[1] = y; +} + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- + +inline void Vector2D::Negate() +{ + Assert( IsValid() ); + x = -x; y = -y; +} + +inline Vector2D& Vector2D::operator+=(const Vector2D& v) +{ + Assert( IsValid() && v.IsValid() ); + x+=v.x; y+=v.y; + return *this; +} + +inline Vector2D& Vector2D::operator-=(const Vector2D& v) +{ + Assert( IsValid() && v.IsValid() ); + x-=v.x; y-=v.y; + return *this; +} + +inline Vector2D& Vector2D::operator*=(float fl) +{ + x *= fl; + y *= fl; + Assert( IsValid() ); + return *this; +} + +inline Vector2D& Vector2D::operator*=(const Vector2D& v) +{ + x *= v.x; + y *= v.y; + Assert( IsValid() ); + return *this; +} + +inline Vector2D& Vector2D::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + Assert( IsValid() ); + return *this; +} + +inline Vector2D& Vector2D::operator/=(const Vector2D& v) +{ + Assert( v.x != 0.0f && v.y != 0.0f ); + x /= v.x; + y /= v.y; + Assert( IsValid() ); + return *this; +} + +inline void Vector2DAdd( const Vector2D& a, const Vector2D& b, Vector2D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x + b.x; + c.y = a.y + b.y; +} + +inline void Vector2DSubtract( const Vector2D& a, const Vector2D& b, Vector2D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x - b.x; + c.y = a.y - b.y; +} + +inline void Vector2DMultiply( const Vector2D& a, vec_t b, Vector2D& c ) +{ + Assert( a.IsValid() && IsFinite(b) ); + c.x = a.x * b; + c.y = a.y * b; +} + +inline void Vector2DMultiply( const Vector2D& a, const Vector2D& b, Vector2D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x * b.x; + c.y = a.y * b.y; +} + + +inline void Vector2DDivide( const Vector2D& a, vec_t b, Vector2D& c ) +{ + Assert( a.IsValid() ); + Assert( b != 0.0f ); + vec_t oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; +} + +inline void Vector2DDivide( const Vector2D& a, const Vector2D& b, Vector2D& c ) +{ + Assert( a.IsValid() ); + Assert( (b.x != 0.0f) && (b.y != 0.0f) ); + c.x = a.x / b.x; + c.y = a.y / b.y; +} + +inline void Vector2DMA( const Vector2D& start, float s, const Vector2D& dir, Vector2D& result ) +{ + Assert( start.IsValid() && IsFinite(s) && dir.IsValid() ); + result.x = start.x + s*dir.x; + result.y = start.y + s*dir.y; +} + +// FIXME: Remove +// For backwards compatability +inline void Vector2D::MulAdd(const Vector2D& a, const Vector2D& b, float scalar) +{ + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; +} + +inline void Vector2DLerp(const Vector2D& src1, const Vector2D& src2, vec_t t, Vector2D& dest ) +{ + dest[0] = src1[0] + (src2[0] - src1[0]) * t; + dest[1] = src1[1] + (src2[1] - src1[1]) * t; +} + +//----------------------------------------------------------------------------- +// dot, cross +//----------------------------------------------------------------------------- +inline vec_t DotProduct2D(const Vector2D& a, const Vector2D& b) +{ + Assert( a.IsValid() && b.IsValid() ); + return( a.x*b.x + a.y*b.y ); +} + +// for backwards compatability +inline vec_t Vector2D::Dot( const Vector2D& vOther ) const +{ + return DotProduct2D( *this, vOther ); +} + + +//----------------------------------------------------------------------------- +// length +//----------------------------------------------------------------------------- +inline vec_t Vector2DLength( const Vector2D& v ) +{ + Assert( v.IsValid() ); + return (vec_t)FastSqrt(v.x*v.x + v.y*v.y); +} + +inline vec_t Vector2D::LengthSqr(void) const +{ + Assert( IsValid() ); + return (x*x + y*y); +} + +inline vec_t Vector2D::NormalizeInPlace() +{ + return Vector2DNormalize( *this ); +} + +inline bool Vector2D::IsLengthGreaterThan( float val ) const +{ + return LengthSqr() > val*val; +} + +inline bool Vector2D::IsLengthLessThan( float val ) const +{ + return LengthSqr() < val*val; +} + +inline vec_t Vector2D::Length(void) const +{ + return Vector2DLength( *this ); +} + + +inline void Vector2DMin( const Vector2D &a, const Vector2D &b, Vector2D &result ) +{ + result.x = (a.x < b.x) ? a.x : b.x; + result.y = (a.y < b.y) ? a.y : b.y; +} + + +inline void Vector2DMax( const Vector2D &a, const Vector2D &b, Vector2D &result ) +{ + result.x = (a.x > b.x) ? a.x : b.x; + result.y = (a.y > b.y) ? a.y : b.y; +} + + +//----------------------------------------------------------------------------- +// Normalization +//----------------------------------------------------------------------------- +inline vec_t Vector2DNormalize( Vector2D& v ) +{ + Assert( v.IsValid() ); + vec_t l = v.Length(); + if (l != 0.0f) + { + v /= l; + } + else + { + v.x = v.y = 0.0f; + } + return l; +} + + +//----------------------------------------------------------------------------- +// Get the distance from this Vector2D to the other one +//----------------------------------------------------------------------------- +inline vec_t Vector2D::DistTo(const Vector2D &vOther) const +{ + Vector2D delta; + Vector2DSubtract( *this, vOther, delta ); + return delta.Length(); +} + +inline vec_t Vector2D::DistToSqr(const Vector2D &vOther) const +{ + Vector2D delta; + Vector2DSubtract( *this, vOther, delta ); + return delta.LengthSqr(); +} + + +//----------------------------------------------------------------------------- +// Computes the closest point to vecTarget no farther than flMaxDist from vecStart +//----------------------------------------------------------------------------- +inline void ComputeClosestPoint2D( const Vector2D& vecStart, float flMaxDist, const Vector2D& vecTarget, Vector2D *pResult ) +{ + Vector2D vecDelta; + Vector2DSubtract( vecTarget, vecStart, vecDelta ); + float flDistSqr = vecDelta.LengthSqr(); + if ( flDistSqr <= flMaxDist * flMaxDist ) + { + *pResult = vecTarget; + } + else + { + vecDelta /= FastSqrt( flDistSqr ); + Vector2DMA( vecStart, flMaxDist, vecDelta, *pResult ); + } +} + + + +//----------------------------------------------------------------------------- +// +// Slow methods +// +//----------------------------------------------------------------------------- + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +//----------------------------------------------------------------------------- +// Returns a Vector2D with the min or max in X, Y, and Z. +//----------------------------------------------------------------------------- + +inline Vector2D Vector2D::Min(const Vector2D &vOther) const +{ + return Vector2D(x < vOther.x ? x : vOther.x, + y < vOther.y ? y : vOther.y); +} + +inline Vector2D Vector2D::Max(const Vector2D &vOther) const +{ + return Vector2D(x > vOther.x ? x : vOther.x, + y > vOther.y ? y : vOther.y); +} + + +//----------------------------------------------------------------------------- +// arithmetic operations +//----------------------------------------------------------------------------- + +inline Vector2D Vector2D::operator-(void) const +{ + return Vector2D(-x,-y); +} + +inline Vector2D Vector2D::operator+(const Vector2D& v) const +{ + Vector2D res; + Vector2DAdd( *this, v, res ); + return res; +} + +inline Vector2D Vector2D::operator-(const Vector2D& v) const +{ + Vector2D res; + Vector2DSubtract( *this, v, res ); + return res; +} + +inline Vector2D Vector2D::operator*(float fl) const +{ + Vector2D res; + Vector2DMultiply( *this, fl, res ); + return res; +} + +inline Vector2D Vector2D::operator*(const Vector2D& v) const +{ + Vector2D res; + Vector2DMultiply( *this, v, res ); + return res; +} + +inline Vector2D Vector2D::operator/(float fl) const +{ + Vector2D res; + Vector2DDivide( *this, fl, res ); + return res; +} + +inline Vector2D Vector2D::operator/(const Vector2D& v) const +{ + Vector2D res; + Vector2DDivide( *this, v, res ); + return res; +} + +inline Vector2D operator*(float fl, const Vector2D& v) +{ + return v * fl; +} + +#endif //slow + +#endif // VECTOR2D_H + diff --git a/public/mathlib/vector4d.h b/public/mathlib/vector4d.h new file mode 100644 index 0000000..ec3f6d9 --- /dev/null +++ b/public/mathlib/vector4d.h @@ -0,0 +1,747 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef VECTOR4D_H +#define VECTOR4D_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include // for rand(). we really need a library! +#include +#if !defined( _X360 ) +#include // for sse +#endif +#include "tier0/basetypes.h" // For vec_t, put this somewhere else? +#include "tier0/dbg.h" +#include "mathlib/math_pfns.h" +#include "mathlib/vector.h" + +// forward declarations +class Vector; +class Vector2D; + +//========================================================= +// 4D Vector4D +//========================================================= + +class Vector4D +{ +public: + // Members + vec_t x, y, z, w; + + // Construction/destruction + Vector4D(void); + Vector4D(vec_t X, vec_t Y, vec_t Z, vec_t W); + Vector4D(const float *pFloat); + + // Initialization + void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f, vec_t iw=0.0f); + void Init( const Vector& src, vec_t iw=0.0f ); + + // Got any nasty NAN's? + bool IsValid() const; + + // array access... + vec_t operator[](int i) const; + vec_t& operator[](int i); + + // Base address... + inline vec_t* Base(); + inline vec_t const* Base() const; + + // Cast to Vector and Vector2D... + Vector& AsVector3D(); + Vector const& AsVector3D() const; + + Vector2D& AsVector2D(); + Vector2D const& AsVector2D() const; + + // Initialization methods + void Random( vec_t minVal, vec_t maxVal ); + + // equality + bool operator==(const Vector4D& v) const; + bool operator!=(const Vector4D& v) const; + + // arithmetic operations + Vector4D& operator+=(const Vector4D &v); + Vector4D& operator-=(const Vector4D &v); + Vector4D& operator*=(const Vector4D &v); + Vector4D& operator*=(float s); + Vector4D& operator/=(const Vector4D &v); + Vector4D& operator/=(float s); + + Vector4D operator-( void ) const; + Vector4D operator*( float fl ) const; + Vector4D operator/( float fl ) const; + Vector4D operator*( const Vector4D& v ) const; + Vector4D operator+( const Vector4D& v ) const; + Vector4D operator-( const Vector4D& v ) const; + + // negate the Vector4D components + void Negate(); + + // Get the Vector4D's magnitude. + vec_t Length() const; + + // Get the Vector4D's magnitude squared. + vec_t LengthSqr(void) const; + + // return true if this vector is (0,0,0,0) within tolerance + bool IsZero( float tolerance = 0.01f ) const + { + return (x > -tolerance && x < tolerance && + y > -tolerance && y < tolerance && + z > -tolerance && z < tolerance && + w > -tolerance && w < tolerance); + } + + // Get the distance from this Vector4D to the other one. + vec_t DistTo(const Vector4D &vOther) const; + + // Get the distance from this Vector4D to the other one squared. + vec_t DistToSqr(const Vector4D &vOther) const; + + // Copy + void CopyToArray(float* rgfl) const; + + // Multiply, add, and assign to this (ie: *this = a + b * scalar). This + // is about 12% faster than the actual Vector4D equation (because it's done per-component + // rather than per-Vector4D). + void MulAdd(Vector4D const& a, Vector4D const& b, float scalar); + + // Dot product. + vec_t Dot(Vector4D const& vOther) const; + + // No copy constructors allowed if we're in optimal mode +#ifdef VECTOR_NO_SLOW_OPERATIONS +private: +#else +public: +#endif + Vector4D(Vector4D const& vOther); + + // No assignment operators either... + Vector4D& operator=( Vector4D const& src ); +}; + +const Vector4D vec4_origin( 0.0f, 0.0f, 0.0f, 0.0f ); +const Vector4D vec4_invalid( FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX ); + +//----------------------------------------------------------------------------- +// SSE optimized routines +//----------------------------------------------------------------------------- + +class ALIGN16 Vector4DAligned : public Vector4D +{ +public: + Vector4DAligned(void) {} + Vector4DAligned( vec_t X, vec_t Y, vec_t Z, vec_t W ); + + inline void Set( vec_t X, vec_t Y, vec_t Z, vec_t W ); + inline void InitZero( void ); + + inline __m128 &AsM128() { return *(__m128*)&x; } + inline const __m128 &AsM128() const { return *(const __m128*)&x; } + +private: + // No copy constructors allowed if we're in optimal mode + Vector4DAligned( Vector4DAligned const& vOther ); + + // No assignment operators either... + Vector4DAligned& operator=( Vector4DAligned const& src ); +} ALIGN16_POST; + +//----------------------------------------------------------------------------- +// Vector4D related operations +//----------------------------------------------------------------------------- + +// Vector4D clear +void Vector4DClear( Vector4D& a ); + +// Copy +void Vector4DCopy( Vector4D const& src, Vector4D& dst ); + +// Vector4D arithmetic +void Vector4DAdd( Vector4D const& a, Vector4D const& b, Vector4D& result ); +void Vector4DSubtract( Vector4D const& a, Vector4D const& b, Vector4D& result ); +void Vector4DMultiply( Vector4D const& a, vec_t b, Vector4D& result ); +void Vector4DMultiply( Vector4D const& a, Vector4D const& b, Vector4D& result ); +void Vector4DDivide( Vector4D const& a, vec_t b, Vector4D& result ); +void Vector4DDivide( Vector4D const& a, Vector4D const& b, Vector4D& result ); +void Vector4DMA( Vector4D const& start, float s, Vector4D const& dir, Vector4D& result ); + +// Vector4DAligned arithmetic +void Vector4DMultiplyAligned( Vector4DAligned const& a, vec_t b, Vector4DAligned& result ); + + +#define Vector4DExpand( v ) (v).x, (v).y, (v).z, (v).w + +// Normalization +vec_t Vector4DNormalize( Vector4D& v ); + +// Length +vec_t Vector4DLength( Vector4D const& v ); + +// Dot Product +vec_t DotProduct4D(Vector4D const& a, Vector4D const& b); + +// Linearly interpolate between two vectors +void Vector4DLerp(Vector4D const& src1, Vector4D const& src2, vec_t t, Vector4D& dest ); + + +//----------------------------------------------------------------------------- +// +// Inlined Vector4D methods +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructors +//----------------------------------------------------------------------------- + +inline Vector4D::Vector4D(void) +{ +#ifdef _DEBUG + // Initialize to NAN to catch errors + x = y = z = w = VEC_T_NAN; +#endif +} + +inline Vector4D::Vector4D(vec_t X, vec_t Y, vec_t Z, vec_t W ) +{ + x = X; y = Y; z = Z; w = W; + Assert( IsValid() ); +} + +inline Vector4D::Vector4D(const float *pFloat) +{ + Assert( pFloat ); + x = pFloat[0]; y = pFloat[1]; z = pFloat[2]; w = pFloat[3]; + Assert( IsValid() ); +} + + +//----------------------------------------------------------------------------- +// copy constructor +//----------------------------------------------------------------------------- + +inline Vector4D::Vector4D(const Vector4D &vOther) +{ + Assert( vOther.IsValid() ); + x = vOther.x; y = vOther.y; z = vOther.z; w = vOther.w; +} + +//----------------------------------------------------------------------------- +// initialization +//----------------------------------------------------------------------------- +inline void Vector4D::Init( vec_t ix, vec_t iy, vec_t iz, vec_t iw ) +{ + x = ix; y = iy; z = iz; w = iw; + Assert( IsValid() ); +} + +inline void Vector4D::Init( const Vector& src, vec_t iw ) +{ + x = src.x; y = src.y; z = src.z; w = iw; + Assert( IsValid() ); +} + + +inline void Vector4D::Random( vec_t minVal, vec_t maxVal ) +{ + x = minVal + ((vec_t)rand() / RAND_MAX) * (maxVal - minVal); + y = minVal + ((vec_t)rand() / RAND_MAX) * (maxVal - minVal); + z = minVal + ((vec_t)rand() / RAND_MAX) * (maxVal - minVal); + w = minVal + ((vec_t)rand() / RAND_MAX) * (maxVal - minVal); +} + +inline void Vector4DClear( Vector4D& a ) +{ + a.x = a.y = a.z = a.w = 0.0f; +} + +//----------------------------------------------------------------------------- +// assignment +//----------------------------------------------------------------------------- + +inline Vector4D& Vector4D::operator=(const Vector4D &vOther) +{ + Assert( vOther.IsValid() ); + x=vOther.x; y=vOther.y; z=vOther.z; w=vOther.w; + return *this; +} + +//----------------------------------------------------------------------------- +// Array access +//----------------------------------------------------------------------------- + +inline vec_t& Vector4D::operator[](int i) +{ + Assert( (i >= 0) && (i < 4) ); + return ((vec_t*)this)[i]; +} + +inline vec_t Vector4D::operator[](int i) const +{ + Assert( (i >= 0) && (i < 4) ); + return ((vec_t*)this)[i]; +} + +//----------------------------------------------------------------------------- +// Cast to Vector and Vector2D... +//----------------------------------------------------------------------------- + +inline Vector& Vector4D::AsVector3D() +{ + return *(Vector*)this; +} + +inline Vector const& Vector4D::AsVector3D() const +{ + return *(Vector const*)this; +} + +inline Vector2D& Vector4D::AsVector2D() +{ + return *(Vector2D*)this; +} + +inline Vector2D const& Vector4D::AsVector2D() const +{ + return *(Vector2D const*)this; +} + +//----------------------------------------------------------------------------- +// Base address... +//----------------------------------------------------------------------------- + +inline vec_t* Vector4D::Base() +{ + return (vec_t*)this; +} + +inline vec_t const* Vector4D::Base() const +{ + return (vec_t const*)this; +} + +//----------------------------------------------------------------------------- +// IsValid? +//----------------------------------------------------------------------------- + +inline bool Vector4D::IsValid() const +{ + return IsFinite(x) && IsFinite(y) && IsFinite(z) && IsFinite(w); +} + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- + +inline bool Vector4D::operator==( Vector4D const& src ) const +{ + Assert( src.IsValid() && IsValid() ); + return (src.x == x) && (src.y == y) && (src.z == z) && (src.w == w); +} + +inline bool Vector4D::operator!=( Vector4D const& src ) const +{ + Assert( src.IsValid() && IsValid() ); + return (src.x != x) || (src.y != y) || (src.z != z) || (src.w != w); +} + + +//----------------------------------------------------------------------------- +// Copy +//----------------------------------------------------------------------------- + +inline void Vector4DCopy( Vector4D const& src, Vector4D& dst ) +{ + Assert( src.IsValid() ); + dst.x = src.x; + dst.y = src.y; + dst.z = src.z; + dst.w = src.w; +} + +inline void Vector4D::CopyToArray(float* rgfl) const +{ + Assert( IsValid() ); + Assert( rgfl ); + rgfl[0] = x; rgfl[1] = y; rgfl[2] = z; rgfl[3] = w; +} + +//----------------------------------------------------------------------------- +// standard math operations +//----------------------------------------------------------------------------- + +inline void Vector4D::Negate() +{ + Assert( IsValid() ); + x = -x; y = -y; z = -z; w = -w; +} + +inline Vector4D& Vector4D::operator+=(const Vector4D& v) +{ + Assert( IsValid() && v.IsValid() ); + x+=v.x; y+=v.y; z += v.z; w += v.w; + return *this; +} + +inline Vector4D& Vector4D::operator-=(const Vector4D& v) +{ + Assert( IsValid() && v.IsValid() ); + x-=v.x; y-=v.y; z -= v.z; w -= v.w; + return *this; +} + +inline Vector4D& Vector4D::operator*=(float fl) +{ + x *= fl; + y *= fl; + z *= fl; + w *= fl; + Assert( IsValid() ); + return *this; +} + +inline Vector4D& Vector4D::operator*=(Vector4D const& v) +{ + x *= v.x; + y *= v.y; + z *= v.z; + w *= v.w; + Assert( IsValid() ); + return *this; +} + +inline Vector4D Vector4D::operator-(void) const +{ + return Vector4D(-x,-y,-z,-w); +} + +inline Vector4D Vector4D::operator+(const Vector4D& v) const +{ + Vector4D res; + Vector4DAdd( *this, v, res ); + return res; +} + +inline Vector4D Vector4D::operator-(const Vector4D& v) const +{ + Vector4D res; + Vector4DSubtract( *this, v, res ); + return res; +} + + +inline Vector4D Vector4D::operator*(float fl) const +{ + Vector4D res; + Vector4DMultiply( *this, fl, res ); + return res; +} + +inline Vector4D Vector4D::operator*(const Vector4D& v) const +{ + Vector4D res; + Vector4DMultiply( *this, v, res ); + return res; +} + +inline Vector4D Vector4D::operator/(float fl) const +{ + Vector4D res; + Vector4DDivide( *this, fl, res ); + return res; +} + +inline Vector4D operator*( float fl, const Vector4D& v ) +{ + return v * fl; +} + +inline Vector4D& Vector4D::operator/=(float fl) +{ + Assert( fl != 0.0f ); + float oofl = 1.0f / fl; + x *= oofl; + y *= oofl; + z *= oofl; + w *= oofl; + Assert( IsValid() ); + return *this; +} + +inline Vector4D& Vector4D::operator/=(Vector4D const& v) +{ + Assert( v.x != 0.0f && v.y != 0.0f && v.z != 0.0f && v.w != 0.0f ); + x /= v.x; + y /= v.y; + z /= v.z; + w /= v.w; + Assert( IsValid() ); + return *this; +} + +inline void Vector4DAdd( Vector4D const& a, Vector4D const& b, Vector4D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; + c.w = a.w + b.w; +} + +inline void Vector4DSubtract( Vector4D const& a, Vector4D const& b, Vector4D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; + c.w = a.w - b.w; +} + +inline void Vector4DMultiply( Vector4D const& a, vec_t b, Vector4D& c ) +{ + Assert( a.IsValid() && IsFinite(b) ); + c.x = a.x * b; + c.y = a.y * b; + c.z = a.z * b; + c.w = a.w * b; +} + +inline void Vector4DMultiply( Vector4D const& a, Vector4D const& b, Vector4D& c ) +{ + Assert( a.IsValid() && b.IsValid() ); + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; + c.w = a.w * b.w; +} + +inline void Vector4DDivide( Vector4D const& a, vec_t b, Vector4D& c ) +{ + Assert( a.IsValid() ); + Assert( b != 0.0f ); + vec_t oob = 1.0f / b; + c.x = a.x * oob; + c.y = a.y * oob; + c.z = a.z * oob; + c.w = a.w * oob; +} + +inline void Vector4DDivide( Vector4D const& a, Vector4D const& b, Vector4D& c ) +{ + Assert( a.IsValid() ); + Assert( (b.x != 0.0f) && (b.y != 0.0f) && (b.z != 0.0f) && (b.w != 0.0f) ); + c.x = a.x / b.x; + c.y = a.y / b.y; + c.z = a.z / b.z; + c.w = a.w / b.w; +} + +inline void Vector4DMA( Vector4D const& start, float s, Vector4D const& dir, Vector4D& result ) +{ + Assert( start.IsValid() && IsFinite(s) && dir.IsValid() ); + result.x = start.x + s*dir.x; + result.y = start.y + s*dir.y; + result.z = start.z + s*dir.z; + result.w = start.w + s*dir.w; +} + +// FIXME: Remove +// For backwards compatability +inline void Vector4D::MulAdd(Vector4D const& a, Vector4D const& b, float scalar) +{ + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; + z = a.z + b.z * scalar; + w = a.w + b.w * scalar; +} + +inline void Vector4DLerp(const Vector4D& src1, const Vector4D& src2, vec_t t, Vector4D& dest ) +{ + dest[0] = src1[0] + (src2[0] - src1[0]) * t; + dest[1] = src1[1] + (src2[1] - src1[1]) * t; + dest[2] = src1[2] + (src2[2] - src1[2]) * t; + dest[3] = src1[3] + (src2[3] - src1[3]) * t; +} + +//----------------------------------------------------------------------------- +// dot, cross +//----------------------------------------------------------------------------- + +inline vec_t DotProduct4D(const Vector4D& a, const Vector4D& b) +{ + Assert( a.IsValid() && b.IsValid() ); + return( a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w ); +} + +// for backwards compatability +inline vec_t Vector4D::Dot( Vector4D const& vOther ) const +{ + return DotProduct4D( *this, vOther ); +} + + +//----------------------------------------------------------------------------- +// length +//----------------------------------------------------------------------------- + +inline vec_t Vector4DLength( Vector4D const& v ) +{ + Assert( v.IsValid() ); + return (vec_t)FastSqrt(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); +} + +inline vec_t Vector4D::LengthSqr(void) const +{ + Assert( IsValid() ); + return (x*x + y*y + z*z + w*w); +} + +inline vec_t Vector4D::Length(void) const +{ + return Vector4DLength( *this ); +} + + +//----------------------------------------------------------------------------- +// Normalization +//----------------------------------------------------------------------------- + +// FIXME: Can't use until we're un-macroed in mathlib.h +inline vec_t Vector4DNormalize( Vector4D& v ) +{ + Assert( v.IsValid() ); + vec_t l = v.Length(); + if (l != 0.0f) + { + v /= l; + } + else + { + v.x = v.y = v.z = v.w = 0.0f; + } + return l; +} + +//----------------------------------------------------------------------------- +// Get the distance from this Vector4D to the other one +//----------------------------------------------------------------------------- + +inline vec_t Vector4D::DistTo(const Vector4D &vOther) const +{ + Vector4D delta; + Vector4DSubtract( *this, vOther, delta ); + return delta.Length(); +} + +inline vec_t Vector4D::DistToSqr(const Vector4D &vOther) const +{ + Vector4D delta; + Vector4DSubtract( *this, vOther, delta ); + return delta.LengthSqr(); +} + + +//----------------------------------------------------------------------------- +// Vector4DAligned routines +//----------------------------------------------------------------------------- + +inline Vector4DAligned::Vector4DAligned( vec_t X, vec_t Y, vec_t Z, vec_t W ) +{ + x = X; y = Y; z = Z; w = W; + Assert( IsValid() ); +} + +inline void Vector4DAligned::Set( vec_t X, vec_t Y, vec_t Z, vec_t W ) +{ + x = X; y = Y; z = Z; w = W; + Assert( IsValid() ); +} + +inline void Vector4DAligned::InitZero( void ) +{ +#if !defined( _X360 ) + this->AsM128() = _mm_set1_ps( 0.0f ); +#else + this->AsM128() = __vspltisw( 0 ); +#endif + Assert( IsValid() ); +} + +inline void Vector4DMultiplyAligned( Vector4DAligned const& a, Vector4DAligned const& b, Vector4DAligned& c ) +{ + Assert( a.IsValid() && b.IsValid() ); +#if !defined( _X360 ) + c.x = a.x * b.x; + c.y = a.y * b.y; + c.z = a.z * b.z; + c.w = a.w * b.w; +#else + c.AsM128() = __vmulfp( a.AsM128(), b.AsM128() ); +#endif +} + +inline void Vector4DWeightMAD( vec_t w, Vector4DAligned const& vInA, Vector4DAligned& vOutA, Vector4DAligned const& vInB, Vector4DAligned& vOutB ) +{ + Assert( vInA.IsValid() && vInB.IsValid() && IsFinite(w) ); + +#if !defined( _X360 ) + vOutA.x += vInA.x * w; + vOutA.y += vInA.y * w; + vOutA.z += vInA.z * w; + vOutA.w += vInA.w * w; + + vOutB.x += vInB.x * w; + vOutB.y += vInB.y * w; + vOutB.z += vInB.z * w; + vOutB.w += vInB.w * w; +#else + __vector4 temp; + + temp = __lvlx( &w, 0 ); + temp = __vspltw( temp, 0 ); + + vOutA.AsM128() = __vmaddfp( vInA.AsM128(), temp, vOutA.AsM128() ); + vOutB.AsM128() = __vmaddfp( vInB.AsM128(), temp, vOutB.AsM128() ); +#endif +} + +inline void Vector4DWeightMADSSE( vec_t w, Vector4DAligned const& vInA, Vector4DAligned& vOutA, Vector4DAligned const& vInB, Vector4DAligned& vOutB ) +{ + Assert( vInA.IsValid() && vInB.IsValid() && IsFinite(w) ); + +#if !defined( _X360 ) + // Replicate scalar float out to 4 components + __m128 packed = _mm_set1_ps( w ); + + // 4D SSE Vector MAD + vOutA.AsM128() = _mm_add_ps( vOutA.AsM128(), _mm_mul_ps( vInA.AsM128(), packed ) ); + vOutB.AsM128() = _mm_add_ps( vOutB.AsM128(), _mm_mul_ps( vInB.AsM128(), packed ) ); +#else + __vector4 temp; + + temp = __lvlx( &w, 0 ); + temp = __vspltw( temp, 0 ); + + vOutA.AsM128() = __vmaddfp( vInA.AsM128(), temp, vOutA.AsM128() ); + vOutB.AsM128() = __vmaddfp( vInB.AsM128(), temp, vOutB.AsM128() ); +#endif +} + +#endif // VECTOR4D_H + diff --git a/public/mathlib/vertexcolor.h b/public/mathlib/vertexcolor.h new file mode 100644 index 0000000..ea85b0a --- /dev/null +++ b/public/mathlib/vertexcolor.h @@ -0,0 +1,121 @@ +//==== Copyright © 1996-2008, Valve Corporation, All rights reserved. =======// +// +// Purpose: A color format which works on 360 + PC +// +//===========================================================================// + +#ifndef VERTEXCOLOR_H +#define VERTEXCOLOR_H + +#ifdef COMPILER_MSVC +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// The challenge here is to make a color struct that works both on the 360 +// and PC, since the 360 is big-endian vs the PC which is little endian. +//----------------------------------------------------------------------------- +struct VertexColor_t +{ + // NOTE: This constructor is explicitly here to disallow initializers + // with initializer lists i.e. + // VertexColor_t color = { 255, 0, 0, 255 }; + // which will totally fail on the 360. + VertexColor_t() {}; + VertexColor_t( const VertexColor_t &src ); + VertexColor_t( uint8 ir, uint8 ig, uint8 ib, uint8 ia ); + + // assign and copy by using the whole register rather than byte-by-byte copy. + // (No, the compiler is not smart enough to do this for you. /FAcs if you + // don't believe me.) + uint32 AsUint32() const; + uint32 *AsUint32Ptr(); + const uint32 *AsUint32Ptr() const; + + // assignment + VertexColor_t &operator=( const VertexColor_t &src ); + VertexColor_t &operator=( const color32 &src ); + + // comparison + bool operator==( const VertexColor_t &src ) const; + bool operator!=( const VertexColor_t &src ) const; + +#ifdef PLATFORM_X360 + // 360 is little endian + uint8 a, b, g, r; +#else + uint8 r, g, b, a; +#endif +}; + + +//----------------------------------------------------------------------------- +// Constructors +//----------------------------------------------------------------------------- +inline VertexColor_t::VertexColor_t( const VertexColor_t &src ) +{ + *AsUint32Ptr() = src.AsUint32(); +} + +inline VertexColor_t::VertexColor_t( uint8 ir, uint8 ig, uint8 ib, uint8 ia ) : r(ir), g(ig), b(ib), a(ia) +{ +} + + +//----------------------------------------------------------------------------- +// Cast to int +//----------------------------------------------------------------------------- +inline uint32 VertexColor_t::AsUint32() const +{ + return *reinterpret_cast( this ); +} + +inline uint32 *VertexColor_t::AsUint32Ptr() +{ + return reinterpret_cast( this ); +} + +inline const uint32 *VertexColor_t::AsUint32Ptr() const +{ + return reinterpret_cast( this ); +} + + +//----------------------------------------------------------------------------- +// assignment +//----------------------------------------------------------------------------- +inline VertexColor_t &VertexColor_t::operator=( const VertexColor_t &src ) +{ + *AsUint32Ptr() = src.AsUint32(); + return *this; +} + +inline VertexColor_t &VertexColor_t::operator=( const color32 &src ) +{ + r = src.r; + g = src.g; + b = src.b; + a = src.a; + return *this; +} + + +//----------------------------------------------------------------------------- +// comparison +//----------------------------------------------------------------------------- +inline bool VertexColor_t::operator==( const VertexColor_t &src ) const +{ + return AsUint32() == src.AsUint32(); +} + +inline bool VertexColor_t::operator!=( const VertexColor_t &src ) const +{ + return AsUint32() != src.AsUint32(); +} + + + +#endif // VERTEXCOLOR_H diff --git a/public/mathlib/vmatrix.h b/public/mathlib/vmatrix.h new file mode 100644 index 0000000..f9a881f --- /dev/null +++ b/public/mathlib/vmatrix.h @@ -0,0 +1,941 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// +// VMatrix always postmultiply vectors as in Ax = b. +// Given a set of basis vectors ((F)orward, (L)eft, (U)p), and a (T)ranslation, +// a matrix to transform a vector into that space looks like this: +// Fx Lx Ux Tx +// Fy Ly Uy Ty +// Fz Lz Uz Tz +// 0 0 0 1 + +// Note that concatenating matrices needs to multiply them in reverse order. +// ie: if I want to apply matrix A, B, then C, the equation needs to look like this: +// C * B * A * v +// ie: +// v = A * v; +// v = B * v; +// v = C * v; +//============================================================================= + +#ifndef VMATRIX_H +#define VMATRIX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "mathlib/vector.h" +#include "mathlib/vplane.h" +#include "mathlib/vector4d.h" +#include "mathlib/mathlib.h" + +struct cplane_t; + + +class VMatrix +{ +public: + + VMatrix(); + VMatrix( + vec_t m00, vec_t m01, vec_t m02, vec_t m03, + vec_t m10, vec_t m11, vec_t m12, vec_t m13, + vec_t m20, vec_t m21, vec_t m22, vec_t m23, + vec_t m30, vec_t m31, vec_t m32, vec_t m33 + ); + + // Creates a matrix where the X axis = forward + // the Y axis = left, and the Z axis = up + VMatrix( const Vector& forward, const Vector& left, const Vector& up ); + + // Construct from a 3x4 matrix + VMatrix( const matrix3x4_t& matrix3x4 ); + + // Set the values in the matrix. + void Init( + vec_t m00, vec_t m01, vec_t m02, vec_t m03, + vec_t m10, vec_t m11, vec_t m12, vec_t m13, + vec_t m20, vec_t m21, vec_t m22, vec_t m23, + vec_t m30, vec_t m31, vec_t m32, vec_t m33 + ); + + + // Initialize from a 3x4 + void Init( const matrix3x4_t& matrix3x4 ); + + // array access + inline float* operator[](int i) + { + return m[i]; + } + + inline const float* operator[](int i) const + { + return m[i]; + } + + // Get a pointer to m[0][0] + inline float *Base() + { + return &m[0][0]; + } + + inline const float *Base() const + { + return &m[0][0]; + } + + void SetLeft(const Vector &vLeft); + void SetUp(const Vector &vUp); + void SetForward(const Vector &vForward); + + void GetBasisVectors(Vector &vForward, Vector &vLeft, Vector &vUp) const; + void SetBasisVectors(const Vector &vForward, const Vector &vLeft, const Vector &vUp); + + // Get/set the translation. + Vector & GetTranslation( Vector &vTrans ) const; + void SetTranslation(const Vector &vTrans); + + void PreTranslate(const Vector &vTrans); + void PostTranslate(const Vector &vTrans); + + matrix3x4_t& As3x4(); + const matrix3x4_t& As3x4() const; + void CopyFrom3x4( const matrix3x4_t &m3x4 ); + void Set3x4( matrix3x4_t& matrix3x4 ) const; + + bool operator==( const VMatrix& src ) const; + bool operator!=( const VMatrix& src ) const { return !( *this == src ); } + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // Access the basis vectors. + Vector GetLeft() const; + Vector GetUp() const; + Vector GetForward() const; + Vector GetTranslation() const; +#endif + + +// Matrix->vector operations. +public: + // Multiply by a 3D vector (same as operator*). + void V3Mul(const Vector &vIn, Vector &vOut) const; + + // Multiply by a 4D vector. + void V4Mul(const Vector4D &vIn, Vector4D &vOut) const; + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // Applies the rotation (ignores translation in the matrix). (This just calls VMul3x3). + Vector ApplyRotation(const Vector &vVec) const; + + // Multiply by a vector (divides by w, assumes input w is 1). + Vector operator*(const Vector &vVec) const; + + // Multiply by the upper 3x3 part of the matrix (ie: only apply rotation). + Vector VMul3x3(const Vector &vVec) const; + + // Apply the inverse (transposed) rotation (only works on pure rotation matrix) + Vector VMul3x3Transpose(const Vector &vVec) const; + + // Multiply by the upper 3 rows. + Vector VMul4x3(const Vector &vVec) const; + + // Apply the inverse (transposed) transformation (only works on pure rotation/translation) + Vector VMul4x3Transpose(const Vector &vVec) const; +#endif + + +// Matrix->plane operations. +public: + // Transform the plane. The matrix can only contain translation and rotation. + void TransformPlane( const VPlane &inPlane, VPlane &outPlane ) const; + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // Just calls TransformPlane and returns the result. + VPlane operator*(const VPlane &thePlane) const; +#endif + +// Matrix->matrix operations. +public: + + VMatrix& operator=(const VMatrix &mOther); + + // Multiply two matrices (out = this * vm). + void MatrixMul( const VMatrix &vm, VMatrix &out ) const; + + // Add two matrices. + const VMatrix& operator+=(const VMatrix &other); + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // Just calls MatrixMul and returns the result. + VMatrix operator*(const VMatrix &mOther) const; + + // Add/Subtract two matrices. + VMatrix operator+(const VMatrix &other) const; + VMatrix operator-(const VMatrix &other) const; + + // Negation. + VMatrix operator-() const; + + // Return inverse matrix. Be careful because the results are undefined + // if the matrix doesn't have an inverse (ie: InverseGeneral returns false). + VMatrix operator~() const; +#endif + +// Matrix operations. +public: + // Set to identity. + void Identity(); + + bool IsIdentity() const; + + // Setup a matrix for origin and angles. + void SetupMatrixOrgAngles( const Vector &origin, const QAngle &vAngles ); + + // General inverse. This may fail so check the return! + bool InverseGeneral(VMatrix &vInverse) const; + + // Does a fast inverse, assuming the matrix only contains translation and rotation. + void InverseTR( VMatrix &mRet ) const; + + // Usually used for debug checks. Returns true if the upper 3x3 contains + // unit vectors and they are all orthogonal. + bool IsRotationMatrix() const; + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // This calls the other InverseTR and returns the result. + VMatrix InverseTR() const; + + // Get the scale of the matrix's basis vectors. + Vector GetScale() const; + + // (Fast) multiply by a scaling matrix setup from vScale. + VMatrix Scale(const Vector &vScale); + + // Normalize the basis vectors. + VMatrix NormalizeBasisVectors() const; + + // Transpose. + VMatrix Transpose() const; + + // Transpose upper-left 3x3. + VMatrix Transpose3x3() const; +#endif + +public: + // The matrix. + vec_t m[4][4]; +}; + + + +//----------------------------------------------------------------------------- +// Helper functions. +//----------------------------------------------------------------------------- + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +// Setup an identity matrix. +VMatrix SetupMatrixIdentity(); + +// Setup as a scaling matrix. +VMatrix SetupMatrixScale(const Vector &vScale); + +// Setup a translation matrix. +VMatrix SetupMatrixTranslation(const Vector &vTranslation); + +// Setup a matrix to reflect around the plane. +VMatrix SetupMatrixReflection(const VPlane &thePlane); + +// Setup a matrix to project from vOrigin onto thePlane. +VMatrix SetupMatrixProjection(const Vector &vOrigin, const VPlane &thePlane); + +// Setup a matrix to rotate the specified amount around the specified axis. +VMatrix SetupMatrixAxisRot(const Vector &vAxis, vec_t fDegrees); + +// Setup a matrix to rotate one axis onto another. Input vectors must be normalized. +VMatrix SetupMatrixAxisToAxisRot(const Vector &vFromAxis, const Vector &vToAxis); + +// Setup a matrix from euler angles. Just sets identity and calls MatrixAngles. +VMatrix SetupMatrixAngles(const QAngle &vAngles); + +// Setup a matrix for origin and angles. +VMatrix SetupMatrixOrgAngles(const Vector &origin, const QAngle &vAngles); + +#endif + +#define VMatToString(mat) (static_cast(CFmtStr("[ (%f, %f, %f), (%f, %f, %f), (%f, %f, %f), (%f, %f, %f) ]", mat.m[0][0], mat.m[0][1], mat.m[0][2], mat.m[0][3], mat.m[1][0], mat.m[1][1], mat.m[1][2], mat.m[1][3], mat.m[2][0], mat.m[2][1], mat.m[2][2], mat.m[2][3], mat.m[3][0], mat.m[3][1], mat.m[3][2], mat.m[3][3] ))) // ** Note: this generates a temporary, don't hold reference! + +//----------------------------------------------------------------------------- +// Returns the point at the intersection on the 3 planes. +// Returns false if it can't be solved (2 or more planes are parallel). +//----------------------------------------------------------------------------- +bool PlaneIntersection( const VPlane &vp1, const VPlane &vp2, const VPlane &vp3, Vector &vOut ); + + +//----------------------------------------------------------------------------- +// These methods are faster. Use them if you want faster code +//----------------------------------------------------------------------------- +void MatrixSetIdentity( VMatrix &dst ); +void MatrixTranspose( const VMatrix& src, VMatrix& dst ); +void MatrixCopy( const VMatrix& src, VMatrix& dst ); +void MatrixMultiply( const VMatrix& src1, const VMatrix& src2, VMatrix& dst ); + +// Accessors +void MatrixGetColumn( const VMatrix &src, int nCol, Vector *pColumn ); +void MatrixSetColumn( VMatrix &src, int nCol, const Vector &column ); +void MatrixGetRow( const VMatrix &src, int nCol, Vector *pColumn ); +void MatrixSetRow( VMatrix &src, int nCol, const Vector &column ); + +// Vector3DMultiply treats src2 as if it's a direction vector +void Vector3DMultiply( const VMatrix& src1, const Vector& src2, Vector& dst ); + +// Vector3DMultiplyPosition treats src2 as if it's a point (adds the translation) +inline void Vector3DMultiplyPosition( const VMatrix& src1, const VectorByValue src2, Vector& dst ); + +// Vector3DMultiplyPositionProjective treats src2 as if it's a point +// and does the perspective divide at the end +void Vector3DMultiplyPositionProjective( const VMatrix& src1, const Vector &src2, Vector& dst ); + +// Vector3DMultiplyPosition treats src2 as if it's a direction +// and does the perspective divide at the end +// NOTE: src1 had better be an inverse transpose to use this correctly +void Vector3DMultiplyProjective( const VMatrix& src1, const Vector &src2, Vector& dst ); + +void Vector4DMultiply( const VMatrix& src1, const Vector4D& src2, Vector4D& dst ); + +// Same as Vector4DMultiply except that src2 has an implicit W of 1 +void Vector4DMultiplyPosition( const VMatrix& src1, const Vector &src2, Vector4D& dst ); + +// Multiplies the vector by the transpose of the matrix +void Vector3DMultiplyTranspose( const VMatrix& src1, const Vector& src2, Vector& dst ); +void Vector4DMultiplyTranspose( const VMatrix& src1, const Vector4D& src2, Vector4D& dst ); + +// Transform a plane +void MatrixTransformPlane( const VMatrix &src, const cplane_t &inPlane, cplane_t &outPlane ); + +// Transform a plane that has an axis-aligned normal +void MatrixTransformAxisAlignedPlane( const VMatrix &src, int nDim, float flSign, float flDist, cplane_t &outPlane ); + +void MatrixBuildTranslation( VMatrix& dst, float x, float y, float z ); +void MatrixBuildTranslation( VMatrix& dst, const Vector &translation ); + +inline void MatrixTranslate( VMatrix& dst, const Vector &translation ) +{ + VMatrix matTranslation, temp; + MatrixBuildTranslation( matTranslation, translation ); + MatrixMultiply( dst, matTranslation, temp ); + dst = temp; +} + + +void MatrixBuildRotationAboutAxis( VMatrix& dst, const Vector& vAxisOfRot, float angleDegrees ); +void MatrixBuildRotateZ( VMatrix& dst, float angleDegrees ); + +inline void MatrixRotate( VMatrix& dst, const Vector& vAxisOfRot, float angleDegrees ) +{ + VMatrix rotation, temp; + MatrixBuildRotationAboutAxis( rotation, vAxisOfRot, angleDegrees ); + MatrixMultiply( dst, rotation, temp ); + dst = temp; +} + +// Builds a rotation matrix that rotates one direction vector into another +void MatrixBuildRotation( VMatrix &dst, const Vector& initialDirection, const Vector& finalDirection ); + +// Builds a scale matrix +void MatrixBuildScale( VMatrix &dst, float x, float y, float z ); +void MatrixBuildScale( VMatrix &dst, const Vector& scale ); + +// Build a perspective matrix. +// zNear and zFar are assumed to be positive. +// You end up looking down positive Z, X is to the right, Y is up. +// X range: [0..1] +// Y range: [0..1] +// Z range: [0..1] +void MatrixBuildPerspective( VMatrix &dst, float fovX, float fovY, float zNear, float zFar ); + +//----------------------------------------------------------------------------- +// Given a projection matrix, take the extremes of the space in transformed into world space and +// get a bounding box. +//----------------------------------------------------------------------------- +void CalculateAABBFromProjectionMatrix( const VMatrix &worldToVolume, Vector *pMins, Vector *pMaxs ); + +//----------------------------------------------------------------------------- +// Given a projection matrix, take the extremes of the space in transformed into world space and +// get a bounding sphere. +//----------------------------------------------------------------------------- +void CalculateSphereFromProjectionMatrix( const VMatrix &worldToVolume, Vector *pCenter, float *pflRadius ); + +//----------------------------------------------------------------------------- +// Given an inverse projection matrix, take the extremes of the space in transformed into world space and +// get a bounding box. +//----------------------------------------------------------------------------- +void CalculateAABBFromProjectionMatrixInverse( const VMatrix &volumeToWorld, Vector *pMins, Vector *pMaxs ); + +//----------------------------------------------------------------------------- +// Given an inverse projection matrix, take the extremes of the space in transformed into world space and +// get a bounding sphere. +//----------------------------------------------------------------------------- +void CalculateSphereFromProjectionMatrixInverse( const VMatrix &volumeToWorld, Vector *pCenter, float *pflRadius ); + +//----------------------------------------------------------------------------- +// Calculate frustum planes given a clip->world space transform. +//----------------------------------------------------------------------------- +void FrustumPlanesFromMatrix( const VMatrix &clipToWorld, Frustum_t &frustum ); + +//----------------------------------------------------------------------------- +// Setup a matrix from euler angles. +//----------------------------------------------------------------------------- +void MatrixFromAngles( const QAngle& vAngles, VMatrix& dst ); + +//----------------------------------------------------------------------------- +// Creates euler angles from a matrix +//----------------------------------------------------------------------------- +void MatrixToAngles( const VMatrix& src, QAngle& vAngles ); + +//----------------------------------------------------------------------------- +// Does a fast inverse, assuming the matrix only contains translation and rotation. +//----------------------------------------------------------------------------- +void MatrixInverseTR( const VMatrix& src, VMatrix &dst ); + +//----------------------------------------------------------------------------- +// Inverts any matrix at all +//----------------------------------------------------------------------------- +bool MatrixInverseGeneral(const VMatrix& src, VMatrix& dst); + +//----------------------------------------------------------------------------- +// Computes the inverse transpose +//----------------------------------------------------------------------------- +void MatrixInverseTranspose( const VMatrix& src, VMatrix& dst ); + + + +//----------------------------------------------------------------------------- +// VMatrix inlines. +//----------------------------------------------------------------------------- +inline VMatrix::VMatrix() +{ +} + +inline VMatrix::VMatrix( + vec_t m00, vec_t m01, vec_t m02, vec_t m03, + vec_t m10, vec_t m11, vec_t m12, vec_t m13, + vec_t m20, vec_t m21, vec_t m22, vec_t m23, + vec_t m30, vec_t m31, vec_t m32, vec_t m33) +{ + Init( + m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33 + ); +} + + +inline VMatrix::VMatrix( const matrix3x4_t& matrix3x4 ) +{ + Init( matrix3x4 ); +} + + +//----------------------------------------------------------------------------- +// Creates a matrix where the X axis = forward +// the Y axis = left, and the Z axis = up +//----------------------------------------------------------------------------- +inline VMatrix::VMatrix( const Vector& xAxis, const Vector& yAxis, const Vector& zAxis ) +{ + Init( + xAxis.x, yAxis.x, zAxis.x, 0.0f, + xAxis.y, yAxis.y, zAxis.y, 0.0f, + xAxis.z, yAxis.z, zAxis.z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); +} + + +inline void VMatrix::Init( + vec_t m00, vec_t m01, vec_t m02, vec_t m03, + vec_t m10, vec_t m11, vec_t m12, vec_t m13, + vec_t m20, vec_t m21, vec_t m22, vec_t m23, + vec_t m30, vec_t m31, vec_t m32, vec_t m33 + ) +{ + m[0][0] = m00; + m[0][1] = m01; + m[0][2] = m02; + m[0][3] = m03; + + m[1][0] = m10; + m[1][1] = m11; + m[1][2] = m12; + m[1][3] = m13; + + m[2][0] = m20; + m[2][1] = m21; + m[2][2] = m22; + m[2][3] = m23; + + m[3][0] = m30; + m[3][1] = m31; + m[3][2] = m32; + m[3][3] = m33; +} + + +//----------------------------------------------------------------------------- +// Initialize from a 3x4 +//----------------------------------------------------------------------------- +inline void VMatrix::Init( const matrix3x4_t& matrix3x4 ) +{ + memcpy(m, matrix3x4.Base(), sizeof( matrix3x4_t ) ); + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; +} + + +//----------------------------------------------------------------------------- +// Methods related to the basis vectors of the matrix +//----------------------------------------------------------------------------- + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline Vector VMatrix::GetForward() const +{ + return Vector(m[0][0], m[1][0], m[2][0]); +} + +inline Vector VMatrix::GetLeft() const +{ + return Vector(m[0][1], m[1][1], m[2][1]); +} + +inline Vector VMatrix::GetUp() const +{ + return Vector(m[0][2], m[1][2], m[2][2]); +} + +#endif + +inline void VMatrix::SetForward(const Vector &vForward) +{ + m[0][0] = vForward.x; + m[1][0] = vForward.y; + m[2][0] = vForward.z; +} + +inline void VMatrix::SetLeft(const Vector &vLeft) +{ + m[0][1] = vLeft.x; + m[1][1] = vLeft.y; + m[2][1] = vLeft.z; +} + +inline void VMatrix::SetUp(const Vector &vUp) +{ + m[0][2] = vUp.x; + m[1][2] = vUp.y; + m[2][2] = vUp.z; +} + +inline void VMatrix::GetBasisVectors(Vector &vForward, Vector &vLeft, Vector &vUp) const +{ + vForward.Init( m[0][0], m[1][0], m[2][0] ); + vLeft.Init( m[0][1], m[1][1], m[2][1] ); + vUp.Init( m[0][2], m[1][2], m[2][2] ); +} + +inline void VMatrix::SetBasisVectors(const Vector &vForward, const Vector &vLeft, const Vector &vUp) +{ + SetForward(vForward); + SetLeft(vLeft); + SetUp(vUp); +} + + +//----------------------------------------------------------------------------- +// Methods related to the translation component of the matrix +//----------------------------------------------------------------------------- +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline Vector VMatrix::GetTranslation() const +{ + return Vector(m[0][3], m[1][3], m[2][3]); +} + +#endif + +inline Vector& VMatrix::GetTranslation( Vector &vTrans ) const +{ + vTrans.x = m[0][3]; + vTrans.y = m[1][3]; + vTrans.z = m[2][3]; + return vTrans; +} + +inline void VMatrix::SetTranslation(const Vector &vTrans) +{ + m[0][3] = vTrans.x; + m[1][3] = vTrans.y; + m[2][3] = vTrans.z; +} + + +//----------------------------------------------------------------------------- +// appply translation to this matrix in the input space +//----------------------------------------------------------------------------- +inline void VMatrix::PreTranslate(const Vector &vTrans) +{ + Vector tmp; + Vector3DMultiplyPosition( *this, vTrans, tmp ); + m[0][3] = tmp.x; + m[1][3] = tmp.y; + m[2][3] = tmp.z; +} + + +//----------------------------------------------------------------------------- +// appply translation to this matrix in the output space +//----------------------------------------------------------------------------- +inline void VMatrix::PostTranslate(const Vector &vTrans) +{ + m[0][3] += vTrans.x; + m[1][3] += vTrans.y; + m[2][3] += vTrans.z; +} + +inline const matrix3x4_t& VMatrix::As3x4() const +{ + return *((const matrix3x4_t*)this); +} + +inline matrix3x4_t& VMatrix::As3x4() +{ + return *((matrix3x4_t*)this); +} + +inline void VMatrix::CopyFrom3x4( const matrix3x4_t &m3x4 ) +{ + memcpy( m, m3x4.Base(), sizeof( matrix3x4_t ) ); + m[3][0] = m[3][1] = m[3][2] = 0; + m[3][3] = 1; +} + +inline void VMatrix::Set3x4( matrix3x4_t& matrix3x4 ) const +{ + memcpy(matrix3x4.Base(), m, sizeof( matrix3x4_t ) ); +} + + +//----------------------------------------------------------------------------- +// Matrix math operations +//----------------------------------------------------------------------------- +inline const VMatrix& VMatrix::operator+=(const VMatrix &other) +{ + for(int i=0; i < 4; i++) + { + for(int j=0; j < 4; j++) + { + m[i][j] += other.m[i][j]; + } + } + + return *this; +} + + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline VMatrix VMatrix::operator+(const VMatrix &other) const +{ + VMatrix ret; + for(int i=0; i < 16; i++) + { + ((float*)ret.m)[i] = ((float*)m)[i] + ((float*)other.m)[i]; + } + return ret; +} + +inline VMatrix VMatrix::operator-(const VMatrix &other) const +{ + VMatrix ret; + + for(int i=0; i < 4; i++) + { + for(int j=0; j < 4; j++) + { + ret.m[i][j] = m[i][j] - other.m[i][j]; + } + } + + return ret; +} + +inline VMatrix VMatrix::operator-() const +{ + VMatrix ret; + for( int i=0; i < 16; i++ ) + { + ((float*)ret.m)[i] = -((float*)m)[i]; + } + return ret; +} + +#endif // VECTOR_NO_SLOW_OPERATIONS + + +//----------------------------------------------------------------------------- +// Vector transformation +//----------------------------------------------------------------------------- + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline Vector VMatrix::operator*(const Vector &vVec) const +{ + Vector vRet; + vRet.x = m[0][0]*vVec.x + m[0][1]*vVec.y + m[0][2]*vVec.z + m[0][3]; + vRet.y = m[1][0]*vVec.x + m[1][1]*vVec.y + m[1][2]*vVec.z + m[1][3]; + vRet.z = m[2][0]*vVec.x + m[2][1]*vVec.y + m[2][2]*vVec.z + m[2][3]; + + return vRet; +} + +inline Vector VMatrix::VMul4x3(const Vector &vVec) const +{ + Vector vResult; + Vector3DMultiplyPosition( *this, vVec, vResult ); + return vResult; +} + + +inline Vector VMatrix::VMul4x3Transpose(const Vector &vVec) const +{ + Vector tmp = vVec; + tmp.x -= m[0][3]; + tmp.y -= m[1][3]; + tmp.z -= m[2][3]; + + return Vector( + m[0][0]*tmp.x + m[1][0]*tmp.y + m[2][0]*tmp.z, + m[0][1]*tmp.x + m[1][1]*tmp.y + m[2][1]*tmp.z, + m[0][2]*tmp.x + m[1][2]*tmp.y + m[2][2]*tmp.z + ); +} + +inline Vector VMatrix::VMul3x3(const Vector &vVec) const +{ + return Vector( + m[0][0]*vVec.x + m[0][1]*vVec.y + m[0][2]*vVec.z, + m[1][0]*vVec.x + m[1][1]*vVec.y + m[1][2]*vVec.z, + m[2][0]*vVec.x + m[2][1]*vVec.y + m[2][2]*vVec.z + ); +} + +inline Vector VMatrix::VMul3x3Transpose(const Vector &vVec) const +{ + return Vector( + m[0][0]*vVec.x + m[1][0]*vVec.y + m[2][0]*vVec.z, + m[0][1]*vVec.x + m[1][1]*vVec.y + m[2][1]*vVec.z, + m[0][2]*vVec.x + m[1][2]*vVec.y + m[2][2]*vVec.z + ); +} + +#endif // VECTOR_NO_SLOW_OPERATIONS + + +inline void VMatrix::V3Mul(const Vector &vIn, Vector &vOut) const +{ + vec_t rw; + + rw = 1.0f / (m[3][0]*vIn.x + m[3][1]*vIn.y + m[3][2]*vIn.z + m[3][3]); + vOut.x = (m[0][0]*vIn.x + m[0][1]*vIn.y + m[0][2]*vIn.z + m[0][3]) * rw; + vOut.y = (m[1][0]*vIn.x + m[1][1]*vIn.y + m[1][2]*vIn.z + m[1][3]) * rw; + vOut.z = (m[2][0]*vIn.x + m[2][1]*vIn.y + m[2][2]*vIn.z + m[2][3]) * rw; +} + +inline void VMatrix::V4Mul(const Vector4D &vIn, Vector4D &vOut) const +{ + vOut[0] = m[0][0]*vIn[0] + m[0][1]*vIn[1] + m[0][2]*vIn[2] + m[0][3]*vIn[3]; + vOut[1] = m[1][0]*vIn[0] + m[1][1]*vIn[1] + m[1][2]*vIn[2] + m[1][3]*vIn[3]; + vOut[2] = m[2][0]*vIn[0] + m[2][1]*vIn[1] + m[2][2]*vIn[2] + m[2][3]*vIn[3]; + vOut[3] = m[3][0]*vIn[0] + m[3][1]*vIn[1] + m[3][2]*vIn[2] + m[3][3]*vIn[3]; +} + + +//----------------------------------------------------------------------------- +// Plane transformation +//----------------------------------------------------------------------------- +inline void VMatrix::TransformPlane( const VPlane &inPlane, VPlane &outPlane ) const +{ + Vector vTrans; + Vector3DMultiply( *this, inPlane.m_Normal, outPlane.m_Normal ); + outPlane.m_Dist = inPlane.m_Dist * DotProduct( outPlane.m_Normal, outPlane.m_Normal ); + outPlane.m_Dist += DotProduct( outPlane.m_Normal, GetTranslation( vTrans ) ); +} + + +//----------------------------------------------------------------------------- +// Other random stuff +//----------------------------------------------------------------------------- +inline void VMatrix::Identity() +{ + MatrixSetIdentity( *this ); +} + + +inline bool VMatrix::IsIdentity() const +{ + return + m[0][0] == 1.0f && m[0][1] == 0.0f && m[0][2] == 0.0f && m[0][3] == 0.0f && + m[1][0] == 0.0f && m[1][1] == 1.0f && m[1][2] == 0.0f && m[1][3] == 0.0f && + m[2][0] == 0.0f && m[2][1] == 0.0f && m[2][2] == 1.0f && m[2][3] == 0.0f && + m[3][0] == 0.0f && m[3][1] == 0.0f && m[3][2] == 0.0f && m[3][3] == 1.0f; +} + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline Vector VMatrix::ApplyRotation(const Vector &vVec) const +{ + return VMul3x3(vVec); +} + +inline VMatrix VMatrix::operator~() const +{ + VMatrix mRet; + InverseGeneral(mRet); + return mRet; +} + +#endif + + +//----------------------------------------------------------------------------- +// Accessors +//----------------------------------------------------------------------------- +inline void MatrixGetColumn( const VMatrix &src, int nCol, Vector *pColumn ) +{ + Assert( (nCol >= 0) && (nCol <= 3) ); + + pColumn->x = src[0][nCol]; + pColumn->y = src[1][nCol]; + pColumn->z = src[2][nCol]; +} + +inline void MatrixSetColumn( VMatrix &src, int nCol, const Vector &column ) +{ + Assert( (nCol >= 0) && (nCol <= 3) ); + + src.m[0][nCol] = column.x; + src.m[1][nCol] = column.y; + src.m[2][nCol] = column.z; +} + +inline void MatrixGetRow( const VMatrix &src, int nRow, Vector *pRow ) +{ + Assert( (nRow >= 0) && (nRow <= 3) ); + *pRow = *(Vector*)src[nRow]; +} + +inline void MatrixSetRow( VMatrix &dst, int nRow, const Vector &row ) +{ + Assert( (nRow >= 0) && (nRow <= 3) ); + *(Vector*)dst[nRow] = row; +} + + +//----------------------------------------------------------------------------- +// Vector3DMultiplyPosition treats src2 as if it's a point (adds the translation) +//----------------------------------------------------------------------------- +// NJS: src2 is passed in as a full vector rather than a reference to prevent the need +// for 2 branches and a potential copy in the body. (ie, handling the case when the src2 +// reference is the same as the dst reference ). +inline void Vector3DMultiplyPosition( const VMatrix& src1, const VectorByValue src2, Vector& dst ) +{ + dst[0] = src1[0][0] * src2.x + src1[0][1] * src2.y + src1[0][2] * src2.z + src1[0][3]; + dst[1] = src1[1][0] * src2.x + src1[1][1] * src2.y + src1[1][2] * src2.z + src1[1][3]; + dst[2] = src1[2][0] * src2.x + src1[2][1] * src2.y + src1[2][2] * src2.z + src1[2][3]; +} + + +//----------------------------------------------------------------------------- +// Transform a plane that has an axis-aligned normal +//----------------------------------------------------------------------------- +inline void MatrixTransformAxisAlignedPlane( const VMatrix &src, int nDim, float flSign, float flDist, cplane_t &outPlane ) +{ + // See MatrixTransformPlane in the .cpp file for an explanation of the algorithm. + MatrixGetColumn( src, nDim, &outPlane.normal ); + outPlane.normal *= flSign; + outPlane.dist = flDist * DotProduct( outPlane.normal, outPlane.normal ); + + // NOTE: Writing this out by hand because it doesn't inline (inline depth isn't large enough) + // This should read outPlane.dist += DotProduct( outPlane.normal, src.GetTranslation ); + outPlane.dist += outPlane.normal.x * src.m[0][3] + outPlane.normal.y * src.m[1][3] + outPlane.normal.z * src.m[2][3]; +} + + +//----------------------------------------------------------------------------- +// Matrix equality test +//----------------------------------------------------------------------------- +inline bool MatricesAreEqual( const VMatrix &src1, const VMatrix &src2, float flTolerance ) +{ + for ( int i = 0; i < 3; ++i ) + { + for ( int j = 0; j < 3; ++j ) + { + if ( fabs( src1[i][j] - src2[i][j] ) > flTolerance ) + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void MatrixBuildOrtho( VMatrix& dst, double left, double top, double right, double bottom, double zNear, double zFar ); +void MatrixBuildPerspectiveX( VMatrix& dst, double flFovX, double flAspect, double flZNear, double flZFar ); +void MatrixBuildPerspectiveOffCenterX( VMatrix& dst, double flFovX, double flAspect, double flZNear, double flZFar, double bottom, double top, double left, double right ); + +inline void MatrixOrtho( VMatrix& dst, double left, double top, double right, double bottom, double zNear, double zFar ) +{ + VMatrix mat; + MatrixBuildOrtho( mat, left, top, right, bottom, zNear, zFar ); + + VMatrix temp; + MatrixMultiply( dst, mat, temp ); + dst = temp; +} + +inline void MatrixPerspectiveX( VMatrix& dst, double flFovX, double flAspect, double flZNear, double flZFar ) +{ + VMatrix mat; + MatrixBuildPerspectiveX( mat, flFovX, flAspect, flZNear, flZFar ); + + VMatrix temp; + MatrixMultiply( dst, mat, temp ); + dst = temp; +} + +inline void MatrixPerspectiveOffCenterX( VMatrix& dst, double flFovX, double flAspect, double flZNear, double flZFar, double bottom, double top, double left, double right ) +{ + VMatrix mat; + MatrixBuildPerspectiveOffCenterX( mat, flFovX, flAspect, flZNear, flZFar, bottom, top, left, right ); + + VMatrix temp; + MatrixMultiply( dst, mat, temp ); + dst = temp; +} + +#endif + + diff --git a/public/mathlib/vplane.h b/public/mathlib/vplane.h new file mode 100644 index 0000000..16cf8c2 --- /dev/null +++ b/public/mathlib/vplane.h @@ -0,0 +1,182 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef VPLANE_H +#define VPLANE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" + +typedef int SideType; + +// Used to represent sides of things like planes. +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +#define VP_EPSILON 0.01f + + +class VPlane +{ +public: + VPlane(); + VPlane(const Vector &vNormal, vec_t dist); + + void Init(const Vector &vNormal, vec_t dist); + + // Return the distance from the point to the plane. + vec_t DistTo(const Vector &vVec) const; + + // Copy. + VPlane& operator=(const VPlane &thePlane); + + // Returns SIDE_ON, SIDE_FRONT, or SIDE_BACK. + // The epsilon for SIDE_ON can be passed in. + SideType GetPointSide(const Vector &vPoint, vec_t sideEpsilon=VP_EPSILON) const; + + // Returns SIDE_FRONT or SIDE_BACK. + SideType GetPointSideExact(const Vector &vPoint) const; + + // Classify the box with respect to the plane. + // Returns SIDE_ON, SIDE_FRONT, or SIDE_BACK + SideType BoxOnPlaneSide(const Vector &vMin, const Vector &vMax) const; + +#ifndef VECTOR_NO_SLOW_OPERATIONS + // Flip the plane. + VPlane Flip(); + + // Get a point on the plane (normal*dist). + Vector GetPointOnPlane() const; + + // Snap the specified point to the plane (along the plane's normal). + Vector SnapPointToPlane(const Vector &vPoint) const; +#endif + +public: + Vector m_Normal; + vec_t m_Dist; + +#ifdef VECTOR_NO_SLOW_OPERATIONS +private: + // No copy constructors allowed if we're in optimal mode + VPlane(const VPlane& vOther); +#endif +}; + + +//----------------------------------------------------------------------------- +// Inlines. +//----------------------------------------------------------------------------- +inline VPlane::VPlane() +{ +} + +inline VPlane::VPlane(const Vector &vNormal, vec_t dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline void VPlane::Init(const Vector &vNormal, vec_t dist) +{ + m_Normal = vNormal; + m_Dist = dist; +} + +inline vec_t VPlane::DistTo(const Vector &vVec) const +{ + return vVec.Dot(m_Normal) - m_Dist; +} + +inline VPlane& VPlane::operator=(const VPlane &thePlane) +{ + m_Normal = thePlane.m_Normal; + m_Dist = thePlane.m_Dist; + return *this; +} + +#ifndef VECTOR_NO_SLOW_OPERATIONS + +inline VPlane VPlane::Flip() +{ + return VPlane(-m_Normal, -m_Dist); +} + +inline Vector VPlane::GetPointOnPlane() const +{ + return m_Normal * m_Dist; +} + +inline Vector VPlane::SnapPointToPlane(const Vector &vPoint) const +{ + return vPoint - m_Normal * DistTo(vPoint); +} + +#endif + +inline SideType VPlane::GetPointSide(const Vector &vPoint, vec_t sideEpsilon) const +{ + vec_t fDist; + + fDist = DistTo(vPoint); + if(fDist >= sideEpsilon) + return SIDE_FRONT; + else if(fDist <= -sideEpsilon) + return SIDE_BACK; + else + return SIDE_ON; +} + +inline SideType VPlane::GetPointSideExact(const Vector &vPoint) const +{ + return DistTo(vPoint) > 0.0f ? SIDE_FRONT : SIDE_BACK; +} + + +// BUGBUG: This should either simply use the implementation in mathlib or cease to exist. +// mathlib implementation is much more efficient. Check to see that VPlane isn't used in +// performance critical code. +inline SideType VPlane::BoxOnPlaneSide(const Vector &vMin, const Vector &vMax) const +{ + int i, firstSide, side; + TableVector vPoints[8] = + { + { vMin.x, vMin.y, vMin.z }, + { vMin.x, vMin.y, vMax.z }, + { vMin.x, vMax.y, vMax.z }, + { vMin.x, vMax.y, vMin.z }, + + { vMax.x, vMin.y, vMin.z }, + { vMax.x, vMin.y, vMax.z }, + { vMax.x, vMax.y, vMax.z }, + { vMax.x, vMax.y, vMin.z }, + }; + + firstSide = GetPointSideExact(vPoints[0]); + for(i=1; i < 8; i++) + { + side = GetPointSideExact(vPoints[i]); + + // Does the box cross the plane? + if(side != firstSide) + return SIDE_ON; + } + + // Ok, they're all on the same side, return that. + return firstSide; +} + + + + +#endif // VPLANE_H diff --git a/public/matsys_controls/QCGenerator.h b/public/matsys_controls/QCGenerator.h new file mode 100644 index 0000000..723793e --- /dev/null +++ b/public/matsys_controls/QCGenerator.h @@ -0,0 +1,155 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef QCGENERATOR_H +#define QCGENERATOR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/Frame.h" +#include "vgui_controls/Button.h" +#include "tier1/utlstring.h" +#include "vgui_controls/TextEntry.h" + +class CQCGenerator; + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Panel; +} + +class CBrowseButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CBrowseButton, vgui::Button ); + +public: + CBrowseButton( vgui::Panel *pParent ); + ~CBrowseButton(); + void InitBrowseInfo( int x, int y, char *pszName, const char *pszDir, char *pszFilter, char *pszField ); + +private: + char *pszStartingDirectory; + char *pszFileFilter; + char *pszTargetField; + + char **GetStartingDirectory(){ return &pszStartingDirectory; } + char **GetFileFilter(){ return &pszFileFilter; } + char **GetTargetField(){ return &pszTargetField; } + void SetCharVar( char **pVar, const char *pszNewText ); + void SetActionMessage(); +}; + +struct LODInfo +{ + char pszFilename[MAX_PATH]; + int iLOD; +}; + +struct QCInfo +{ + CQCGenerator *pQCGenerator; + + char pszSMDPath[MAX_PATH]; + char pszCollisionPath[MAX_PATH]; + char pszSurfaceProperty[MAX_PATH]; + char pszMaterialPath[MAX_PATH]; + char pszSceneName[MAX_PATH]; + + bool bStaticProp; + bool bMostlyOpaque; + bool bDisableCollision; + bool bReferenceAsPhys; + bool bConcave; + bool bAutomass; + bool bNoAnimation; + + CUtlVector LODs; + + float fScale; + float fMass; + void Init( CQCGenerator *pPanel ) + { + pQCGenerator = pPanel; + + Q_strcpy( pszSMDPath, "" ); + Q_strcpy( pszCollisionPath, "" ); + Q_strcpy( pszSurfaceProperty, "default" ); + bStaticProp = false; + bMostlyOpaque = false; + bDisableCollision = false; + bReferenceAsPhys = false; + bConcave = false; + bAutomass = false; + bNoAnimation = true; + + fScale = 1.0; + fMass = 10.0; + } + void SyncToControls(); + void SyncFromControls(); +}; + +//----------------------------------------------------------------------------- +// Purpose: Base class for generating QC files +//----------------------------------------------------------------------------- +class CQCGenerator : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CQCGenerator, vgui::EditablePanel ); + +public: + CQCGenerator( vgui::Panel *pParent, const char *pszPath, const char *pszScene ); + ~CQCGenerator(); + + // overridden frame functions +// virtual void Activate(); + + virtual void OnCommand( const char *command ); + + // Purpose: +// virtual void OnKeyCodeTyped( vgui::KeyCode code ); + + MESSAGE_FUNC( OnNewLODText, "TextNewLine" ); + MESSAGE_FUNC_PARAMS( OnBrowse, "browse", data ); + MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", data ); + MESSAGE_FUNC_PARAMS( OnDirectorySelected, "DirectorySelected", data ); + + bool GenerateQCFile(); +// void BrowseDirectory( KeyValues *data ); + void BrowseFile( KeyValues *data ); + + void DeleteLOD( ); + void EditLOD(); + virtual void OnKeyCodeTyped( vgui::KeyCode code); + void InitializeSMDPaths( const char *pszPath, const char *pszScene ); + +protected: + // Creates standard controls. Allows the derived class to + // add these controls to various splitter windows + void CreateStandardControls( vgui::Panel *pParent ); + +private: + + CBrowseButton *m_pCollisionBrowseButton; + char m_szTargetField[MAX_PATH]; + vgui::ListPanel *m_pLODPanel; + + vgui::TextEntry *m_pLODEdit; + + int m_nSelectedSequence; + int m_nSelectedColumn; + + QCInfo m_QCInfo_t; +}; + + + + +#endif // QCGENERATOR_H diff --git a/public/matsys_controls/assetpicker.h b/public/matsys_controls/assetpicker.h new file mode 100644 index 0000000..3b76b10 --- /dev/null +++ b/public/matsys_controls/assetpicker.h @@ -0,0 +1,51 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ASSETPICKER_H +#define ASSETPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "matsys_controls/baseassetpicker.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Panel; +} + + +//----------------------------------------------------------------------------- +// Purpose: Base class for choosing raw assets +//----------------------------------------------------------------------------- +class CAssetPicker : public CBaseAssetPicker +{ + DECLARE_CLASS_SIMPLE( CAssetPicker, CBaseAssetPicker ); + +public: + CAssetPicker( vgui::Panel *pParent, const char *pAssetType, + const char *pExt, const char *pSubDir, const char *pTextType ); +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for asset picker +//----------------------------------------------------------------------------- +class CAssetPickerFrame : public CBaseAssetPickerFrame +{ + DECLARE_CLASS_SIMPLE( CAssetPickerFrame, CBaseAssetPickerFrame ); + +public: + CAssetPickerFrame( vgui::Panel *pParent, const char *pTitle, + const char *pAssetType, const char *pExt, const char *pSubDir, const char *pTextType ); +}; + + +#endif // ASSETPICKER_H diff --git a/public/matsys_controls/baseassetpicker.h b/public/matsys_controls/baseassetpicker.h new file mode 100644 index 0000000..3878f4d --- /dev/null +++ b/public/matsys_controls/baseassetpicker.h @@ -0,0 +1,243 @@ +//===================== Copyright (c) Valve Corporation. All Rights Reserved. ====================== +// +// +// +//================================================================================================== + +#ifndef BASEASSETPICKER_H +#define BASEASSETPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/Frame.h" +#include "tier1/utlstring.h" +#include "tier1/utllinkedlist.h" +#include "filesystem.h" +#include "assetpickerdefs.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CAssetTreeView; +namespace vgui +{ + class Panel; +} +FORWARD_DECLARE_HANDLE( AssetList_t ); + +typedef unsigned short DirHandle_t; + +struct CachedAssetInfo_t +{ + CUtlString m_AssetName; + int m_nModIndex; + int m_nTimesUsed; +}; + +struct CacheModInfo_t +{ + CUtlString m_ModName; + CUtlString m_Path; +}; + +//----------------------------------------------------------------------------- +// Purpose: Base class for choosing raw assets +//----------------------------------------------------------------------------- +class CBaseAssetPicker : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CBaseAssetPicker, vgui::EditablePanel ); + +public: + CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType, + const char *pExt, const char *pSubDir, const char *pTextType, const char *pSearchPath = "GAME" ); + ~CBaseAssetPicker(); + + // overridden frame functions + virtual void OnTick(); + virtual bool HasUserConfigSettings(); + virtual void ApplyUserConfigSettings( KeyValues *pUserConfig ); + virtual void GetUserConfigSettings( KeyValues *pUserConfig ); + virtual void OnCommand( const char *pCommand ); + + // Purpose: + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + + // Returns the selected asset name + int GetSelectedAssetCount(); + const char *GetSelectedAsset( int nSelectionIndex = -1 ); + int GetSelectedAssetIndex( int nSelectionIndex ); + + // Is multiselect enabled? + bool IsMultiselectEnabled() const; + void SetAllowMultiselect( bool bAllowMultiselect ); + + // Sets the selected asset + void SetSelection( const char *pAssetName, bool bInitialSelection = false ); + void SetInitialSelection( const char *pAssetName ); + + void SetUsedAssetList( CUtlVector &usedAssets ); + + // Set/get the filter + void SetFilter( const char *pFilter ); + const char *GetFilter(); + + // Purpose: refreshes the file tree + void RefreshFileTree(); + + virtual void Activate(); + void CloseModal(); + + virtual void CustomizeSelectionMessage( KeyValues *pKeyValues ) {} + + // asset cache interface + virtual int GetAssetCount(); + virtual const char *GetAssetName( int nAssetIndex ); + virtual const CachedAssetInfo_t& GetCachedAsset( int nAssetIndex ); + virtual int GetCachedAssetCount(); + virtual bool IncrementalCacheAssets( float flTimeAllowed ); // return true if finished + virtual bool BeginCacheAssets( bool bForceRecache ); // return true if finished + virtual CUtlString GetSelectedAssetFullPath( int nIndex ); + + int ModCount() const; + const CacheModInfo_t& ModInfo( int nIndex ) const; + +protected: + // Creates standard controls. Allows the derived class to + // add these controls to various splitter windows + void CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect = false ); + void AutoLayoutStandardControls( ); + + // Allows the picker to browse multiple asset types + void AddExtension( const char *pExtension ); + + // Derived classes have this called when the previewed asset changes + virtual void OnSelectedAssetPicked( const char *pAssetName ) {} + + // Derived classes have this called when the next selected asset is selected by default + virtual void OnNextSelectionIsDefault() {} + + // Derived classes have this called when the filtered list changes + virtual void OnAssetListChanged( ) {} + + // Request focus of the filter box + void RequestFilterFocus(); + + // Rescan assets + void RescanAssets(); + bool DoIncrementalCache( ); + + // Is a particular asset visible? + bool IsAssetVisible( int nAssetIndex ); + + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", kv ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC_PARAMS( OnItemDeselected, "ItemDeselected", kv ); + MESSAGE_FUNC_PARAMS( OnCheckButtonChecked, "CheckButtonChecked", kv ); + MESSAGE_FUNC( OnFileSelected, "TreeViewItemSelected" ); + +private: + struct AssetInfo_t + { + int m_nAssetIndex; + int m_nItemId; + }; + + void BuildAssetNameList(); + void RefreshAssetList( ); + int GetSelectedAssetModIndex( ); + + // Recursively add all files matching the wildcard under this directory + void AddAssetToList( int nAssetIndex ); + + // Update column headers + void UpdateAssetColumnHeader( ); + + vgui::Splitter *m_pAssetSplitter; + CAssetTreeView* m_pFileTree; + vgui::CheckButton* m_pSubDirCheck; + vgui::TextEntry *m_pFilter; + vgui::ListPanel *m_pAssetBrowser; + vgui::TextEntry *m_pFullPath; + vgui::ComboBox *m_pModSelector; + vgui::Button *m_pRescanButton; + vgui::Button *m_pFindAssetButton; + KeyValues *m_pInsertHelper; + vgui::CheckButton *m_pOnlyUsedCheck; + + AssetList_t m_hAssetList; + CUtlString m_FolderFilter; + CUtlString m_Filter; + CUtlString m_SelectedAsset; + CUtlVector< AssetInfo_t > m_AssetList; + const char *m_pAssetType; + const char *m_pAssetTextType; + const char *m_pAssetExt; + const char *m_pAssetSubDir; + const char *m_pAssetSearchPath; + CUtlVector< const char * > m_ExtraAssetExt; + + bool m_bBuiltAssetList : 1; + bool m_bFirstAssetScan : 1; + bool m_bFinishedAssetListScan : 1; + bool m_bSubDirCheck : 1; + bool m_bOnlyUsedAssetsCheck : 1; + + int m_nCurrentModFilter; + int m_nMatchingAssets; + CUtlVector m_usedAssets; + + friend class CBaseAssetPickerFrame; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for asset picker +//----------------------------------------------------------------------------- +class CBaseAssetPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CBaseAssetPickerFrame, vgui::Frame ); + +public: + CBaseAssetPickerFrame( vgui::Panel *pParent ); + ~CBaseAssetPickerFrame(); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + + // Purpose: Activate the dialog + // The message "AssetSelected" will be sent if an asset is picked + // Pass in optional keyvalues to add to the message + void DoModal( KeyValues *pContextKeyValues = NULL ); + + // Sets the initial selected asset + void SetInitialSelection( const char *pAssetName ); + + // Set/get the filter + void SetFilter( const char *pFilter ); + const char *GetFilter(); + + void SetAllowMultiselect( bool bAllowMultiselect ); + +protected: + // Allows the derived class to create the picker + void SetAssetPicker( CBaseAssetPicker* pPicker ); + CBaseAssetPicker* GetAssetPicker() { return m_pPicker; } + + // Posts a message (passing the key values) + void PostMessageAndClose( KeyValues *pKeyValues ); + virtual void CloseModal(); + +private: + void CleanUpMessage(); + + CBaseAssetPicker *m_pPicker; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeyValues; +}; + + +#endif // BASEASSETPICKER_H diff --git a/public/matsys_controls/colorpickerpanel.h b/public/matsys_controls/colorpickerpanel.h new file mode 100644 index 0000000..1414112 --- /dev/null +++ b/public/matsys_controls/colorpickerpanel.h @@ -0,0 +1,162 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef COLORPICKERPANEL_H +#define COLORPICKERPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/editablepanel.h" +#include "vgui_controls/frame.h" +#include "vgui_controls/button.h" +#include "bitmap/imageformat.h" +#include "mathlib/vector.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CColorXYPreview; +class CColorZPreview; + +namespace vgui +{ + class RadioButton; + class TextEntry; + class IScheme; +} + + +//----------------------------------------------------------------------------- +// +// Color picker panel +// +//----------------------------------------------------------------------------- +class CColorPickerPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CColorPickerPanel, vgui::EditablePanel ); + +public: + // constructor + CColorPickerPanel( vgui::Panel *pParent, const char *pName ); + void SetInitialColor( Color initialColor ); + void GetCurrentColor( Color *pColor ); + void GetInitialColor( Color *pColor ); + + // Inherited from Panel + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnMousePressed( vgui::MouseCode code ); + +private: + MESSAGE_FUNC_PARAMS( OnRadioButtonChecked, "RadioButtonChecked", kv ); + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data ); + MESSAGE_FUNC_PARAMS( OnHSVSelected, "HSVSelected", data ); + MESSAGE_FUNC_PARAMS( OnColorSelected, "ColorSelected", data ); + + // Called when the color changes + void OnColorChanged( vgui::TextEntry *pChanged = NULL ); + + // Updates the preview colors + void UpdatePreviewColors(); + + CColorXYPreview *m_pColorXYPreview; + CColorZPreview *m_pColorZPreview; + vgui::RadioButton* m_pHueRadio; + vgui::RadioButton* m_pSaturationRadio; + vgui::RadioButton* m_pValueRadio; + vgui::RadioButton* m_pRedRadio; + vgui::RadioButton* m_pGreenRadio; + vgui::RadioButton* m_pBlueRadio; + vgui::TextEntry* m_pHueText; + vgui::TextEntry* m_pSaturationText; + vgui::TextEntry* m_pValueText; + vgui::TextEntry* m_pRedText; + vgui::TextEntry* m_pGreenText; + vgui::TextEntry* m_pBlueText; + vgui::Panel* m_pInitialColor; + vgui::Panel* m_pCurrentColor; + vgui::TextEntry* m_pAlphaText; + + RGB888_t m_InitialColor; + RGB888_t m_CurrentColor; + unsigned char m_InitialAlpha; + unsigned char m_CurrentAlpha; + Vector m_CurrentHSVColor; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for picker +//----------------------------------------------------------------------------- +class CColorPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CColorPickerFrame, vgui::Frame ); + +public: + CColorPickerFrame( vgui::Panel *pParent, const char *pTitle ); + ~CColorPickerFrame(); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + + // Purpose: Activate the dialog + // If a color is picked, the message 'ColorPickerPicked' is sent with the "color" field set to the color + // If cancel is hit, the message 'ColorPickerCancel' is sent + // If the color is changed in the preview, the message 'ColorPickerPreview' is sent with the "color" field set to the color + void DoModal( Color initialColor, KeyValues *pContextKeys = NULL ); + + // Gets the initial color + void GetInitialColor( Color *pColor ); + +private: + void CleanUpMessage(); + + CColorPickerPanel *m_pPicker; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeys; +}; + + +//----------------------------------------------------------------------------- +// Purpose: A button which brings up the color picker +//----------------------------------------------------------------------------- +class CColorPickerButton : public vgui::Button +{ + DECLARE_CLASS_SIMPLE( CColorPickerButton, vgui::Button ); + + /* + NOTE: Sends ColorPickerPicked message when a color is picked + color - picked color + Sends ColorPickerPreview message when a color is previewed + color - current preview color + Sends ColorPickerCancelled message when the cancel button was hit + startingColor - color before the picking occurred + */ + +public: + CColorPickerButton( vgui::Panel *pParent, const char *pName, vgui::Panel *pActionSignalTarget = NULL ); + ~CColorPickerButton(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void DoClick(); + + void SetColor( const Color& clr ); + void SetColor( int r, int g, int b, int a ); + +private: + MESSAGE_FUNC_PARAMS( OnPicked, "ColorPickerPicked", data ); + MESSAGE_FUNC_PARAMS( OnPreview, "ColorPickerPreview", data ); + MESSAGE_FUNC( OnCancelled, "ColorPickerCancel" ); + + void UpdateButtonColor(); + Color m_CurrentColor; +}; + +#endif // COLORPICKERPANEL_H \ No newline at end of file diff --git a/public/matsys_controls/curveeditorpanel.h b/public/matsys_controls/curveeditorpanel.h new file mode 100644 index 0000000..b911d5c --- /dev/null +++ b/public/matsys_controls/curveeditorpanel.h @@ -0,0 +1,68 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef CURVEEDITORPANEL_H +#define CURVEEDITORPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/Panel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct BGRA8888_t; + + +//----------------------------------------------------------------------------- +// +// Curve editor image panel +// +//----------------------------------------------------------------------------- +class CCurveEditorPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CCurveEditorPanel, vgui::Panel ); + +public: + // constructor + CCurveEditorPanel( vgui::Panel *pParent, const char *pName ); + ~CCurveEditorPanel(); + + virtual void Paint( void ); + virtual void PaintBackground( void ); + + virtual void OnCursorMoved( int x,int y ); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + +protected: + // Control points + values... + virtual int FindOrAddControlPoint( float flIn, float flTolerance, float flOut ) = 0; + virtual int FindControlPoint( float flIn, float flTolerance ) = 0; + virtual int ModifyControlPoint( int nPoint, float flIn, float flOut ) = 0; + virtual void RemoveControlPoint( int nPoint ) = 0; + virtual float GetValue( float flIn ) = 0; + virtual int ControlPointCount() = 0; + virtual void GetControlPoint( int nPoint, float *pIn, float *pOut ) = 0; + + // Converts screen location to normalized values and back + void ScreenToValue( int x, int y, float *pIn, float *pOut ); + void ValueToScreen( float flIn, float flOut, int *x, int *y ); + +private: + + int m_nSelectedPoint; + int m_nHighlightedPoint; // Used when not selecting +}; + + + +#endif // CURVEEDITORPANEL_H diff --git a/public/matsys_controls/gamefiletreeview.h b/public/matsys_controls/gamefiletreeview.h new file mode 100644 index 0000000..5e57e72 --- /dev/null +++ b/public/matsys_controls/gamefiletreeview.h @@ -0,0 +1,81 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef GAMEFILETREEVIEW_H +#define GAMEFILETREEVIEW_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utlstring.h" +#include "vgui_controls/treeview.h" +#include "vgui_controls/ImageList.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class IScheme; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles file view for game files +//----------------------------------------------------------------------------- +class CGameFileTreeView : public vgui::TreeView +{ + DECLARE_CLASS_SIMPLE( CGameFileTreeView, vgui::TreeView ); + +public: + CGameFileTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir, const char *pExtension = NULL ); + + // Inherited from base classes + virtual void GenerateChildrenOfNode( int itemIndex ); + virtual void GenerateContextMenu( int itemIndex, int x, int y ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + // Purpose: Refreshes the active file list + void RefreshFileList(); + + // Sets an item to be colored as if its a menu + void SetItemColorForDirectories( int itemID ); + + // Gets the number of root directories + int GetRootDirectoryCount(); + + // Gets the ith root directory + const char *GetRootDirectory( int nIndex ); + + // Selects the root folder + void SelectRoot(); + +private: + // Populate the root node (necessary since tree view can't have multiple roots) + void PopulateRootNode( int itemIndex ); + + // Populate the root node with directories + void AddDirectoriesOfNode( int itemIndex, const char *pFilePath ); + + // Populate the root node with directories + bool DoesDirectoryHaveSubdirectories( const char *pFilePath ); + + // Populate the root node with files + void AddFilesOfNode( int itemIndex, const char *pFilePath, const char *pExt ); + + CUtlString m_RootDir; + CUtlString m_Ext; + CUtlString m_RootFolderName; + vgui::ImageList m_Images; + bool m_bUseExt; // To differentiate "" from NULL in m_Ext +}; + + +#endif // GAMEFILETREEVIEW_H + diff --git a/public/matsys_controls/manipulator.h b/public/matsys_controls/manipulator.h new file mode 100644 index 0000000..e2bfab9 --- /dev/null +++ b/public/matsys_controls/manipulator.h @@ -0,0 +1,128 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef MANIPULATOR_H +#define MANIPULATOR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Panel.h" +#include "mathlib/Vector.h" + +//----------------------------------------------------------------------------- +// Manipulator interface +//----------------------------------------------------------------------------- +class IManipulator +{ +public: + virtual void OnBeginManipulation( void ) = 0; + virtual void OnAcceptManipulation( void ) = 0; + virtual void OnCancelManipulation( void ) = 0; + + virtual void OnTick( void ) = 0; + + virtual void OnCursorMoved( int x, int y ) = 0; + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ) = 0; + virtual void OnMouseReleased( vgui::MouseCode code, int x, int y ) = 0; + virtual void OnMouseWheeled( int delta ) = 0; + + virtual void SetViewportSize( int w, int h ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Base class helper for implementing manipulators +//----------------------------------------------------------------------------- +class CBaseManipulator : public IManipulator +{ +public: + CBaseManipulator() + { + m_nViewport[ 0 ] = m_nViewport[ 1 ] = 0; + } + + virtual void OnTick( void ) {}; + + virtual void OnBeginManipulation( void ) {} + virtual void OnAcceptManipulation( void ) {}; + virtual void OnCancelManipulation( void ) {}; + + virtual void OnCursorMoved( int x, int y ) {}; + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ) {}; + virtual void OnMouseReleased( vgui::MouseCode code, int x, int y ) {}; + virtual void OnMouseWheeled( int delta ) {}; + + virtual void SetViewportSize( int w, int h ) + { + m_nViewport[ 0 ] = w; + m_nViewport[ 1 ] = h; + } + +protected: + int m_nViewport[ 2 ]; +}; + + +//----------------------------------------------------------------------------- +// Base class for manipulators which operate on transforms +//----------------------------------------------------------------------------- +class CTransformManipulator : public CBaseManipulator +{ +public: + CTransformManipulator( matrix3x4_t *pTransform ); + + void SetTransform( matrix3x4_t *transform ); + matrix3x4_t *GetTransform(); + +protected: + matrix3x4_t *m_pTransform; +}; + + +//----------------------------------------------------------------------------- +// Standard maya-like transform manipulator +//----------------------------------------------------------------------------- +class CPotteryWheelManip : public CTransformManipulator +{ +public: + CPotteryWheelManip( matrix3x4_t *pTransform ); + + virtual void OnBeginManipulation( void ); + virtual void OnAcceptManipulation( void ); + virtual void OnCancelManipulation( void ); + + virtual void OnTick( void ); + + virtual void OnCursorMoved( int x, int y ); + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ); + virtual void OnMouseReleased( vgui::MouseCode code, int x, int y ); + virtual void OnMouseWheeled( int delta ); + + // Sets the zoom level + void SetZoom( float flZoom ); + +protected: + int m_lastx, m_lasty; + + float m_zoom; + float m_altitude, m_azimuth; + //vec3 m_lookat + + float m_prevZoom; + float m_prevAltitude, m_prevAzimuth; + + float m_flLastMouseTime; + float m_flLastTickTime; + float m_flSpin; + bool m_bSpin; + + void UpdateTransform( void ); + void UpdateZoom( float delta ); +}; + + +#endif // MANIPULATOR_H diff --git a/public/matsys_controls/matsyscontrols.h b/public/matsys_controls/matsyscontrols.h new file mode 100644 index 0000000..1f16187 --- /dev/null +++ b/public/matsys_controls/matsyscontrols.h @@ -0,0 +1,72 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MATSYSCONTROLS_H +#define MATSYSCONTROLS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterialSystem; +class IMaterialSystemHardwareConfig; +class IMDLCache; +class IMatSystemSurface; +class IStudioRender; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// handles the initialization of the vgui interfaces. +// NOTE: Calls into VGui_InitInterfacesList +// interfaces (listed below) are first attempted to be loaded from primaryProvider, then secondaryProvider +// moduleName should be the name of the module that this instance of the vgui_controls has been compiled into +//----------------------------------------------------------------------------- +bool VGui_InitMatSysInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ); + + +//----------------------------------------------------------------------------- +// set of accessor functions to matsys interfaces +// the appropriate header file for each is listed above the item +//----------------------------------------------------------------------------- + +// #include +IMaterialSystem *MaterialSystem(); + +// #include +IMDLCache *MDLCache(); + +// #include +IMatSystemSurface *MatSystemSurface(); + +// #include +IMaterialSystemHardwareConfig *MaterialSystemHardwareConfig(); + +} // end namespace vgui + + +//----------------------------------------------------------------------------- +// predeclare all the matsys control class names +//----------------------------------------------------------------------------- +class CMDLPanel; +class CMDLSequencePicker; +class CMDLPicker; +class CSequencePicker; +class CGameFileTreeView; + + +#endif // MATSYSCONTROLS_H diff --git a/public/matsys_controls/mdlpanel.h b/public/matsys_controls/mdlpanel.h new file mode 100644 index 0000000..ac47213 --- /dev/null +++ b/public/matsys_controls/mdlpanel.h @@ -0,0 +1,121 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef MDLPANEL_H +#define MDLPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/Panel.h" +#include "datacache/imdlcache.h" +#include "materialsystem/materialsystemutil.h" +#include "matsys_controls/potterywheelpanel.h" +#include "tier3/mdlutils.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class IScheme; +} + + +//----------------------------------------------------------------------------- +// MDL Viewer Panel +//----------------------------------------------------------------------------- +class CMDLPanel : public CPotteryWheelPanel +{ + DECLARE_CLASS_SIMPLE( CMDLPanel, CPotteryWheelPanel ); + +public: + // constructor, destructor + CMDLPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CMDLPanel(); + + // Overriden methods of vgui::Panel + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual void OnTick(); + + // Sets the current mdl + virtual void SetMDL( MDLHandle_t handle ); + virtual void SetMDL( const char *pMDLName ); + + // Sets the camera to look at the model + void LookAtMDL( ); + + // Sets the current sequence + void SetSequence( int nSequence ); + + // Set the pose parameters + void SetPoseParameters( const float *pPoseParameters, int nCount ); + + // Set the overlay sequence layers + void SetSequenceLayers( const MDLSquenceLayer_t *pSequenceLayers, int nCount ); + + void SetCollsionModel( bool bVisible ); + void SetGroundGrid( bool bVisible ); + void SetWireFrame( bool bVisible ); + void SetLockView( bool bLocked ); + void SetSkin( int nSkin ); + void SetLookAtCamera( bool bLookAtCamera ); + + // Bounds. + bool GetBoundingBox( Vector &vecBoundsMin, Vector &vecBoundsMax ); + bool GetBoundingSphere( Vector &vecCenter, float &flRadius ); + bool GetAttachment( const char *szAttachment, matrix3x4_t& matrixOut ); + bool GetAttachment( int iAttachmentNum, matrix3x4_t& matrixOut ); + + void SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ); + + // Attached models. + void SetMergeMDL( MDLHandle_t handle ); + void SetMergeMDL( const char *pMDLName ); + int GetMergeMDLIndex( MDLHandle_t handle ); + void ClearMergeMDLs( void ); + +protected: + + struct MDLData_t + { + CMDL m_MDL; + matrix3x4_t m_MDLToWorld; + }; + + MDLData_t m_RootMDL; + CUtlVector m_aMergeMDLs; + +private: + // paint it! + void OnPaint3D(); + void OnMouseDoublePressed( vgui::MouseCode code ); + + void DrawCollisionModel(); + void UpdateStudioRenderConfig( void ); + + CTextureReference m_DefaultEnvCubemap; + CTextureReference m_DefaultHDREnvCubemap; + + bool m_bDrawCollisionModel : 1; + bool m_bGroundGrid : 1; + bool m_bLockView : 1; + bool m_bWireFrame : 1; + bool m_bLookAtCamera : 1; + + float m_PoseParameters[ MAXSTUDIOPOSEPARAM ]; + + static const int MAX_SEQUENCE_LAYERS = 8; + int m_nNumSequenceLayers; + MDLSquenceLayer_t m_SequenceLayers[ MAX_SEQUENCE_LAYERS ]; +}; + + +#endif // MDLPANEL_H diff --git a/public/matsys_controls/mdlpicker.h b/public/matsys_controls/mdlpicker.h new file mode 100644 index 0000000..c79bc0d --- /dev/null +++ b/public/matsys_controls/mdlpicker.h @@ -0,0 +1,139 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef MDLPICKER_H +#define MDLPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlstring.h" +#include "vgui_controls/Frame.h" +#include "matsys_controls/baseassetpicker.h" +#include "datacache/imdlcache.h" + + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Splitter; +} + +class CMDLPanel; + + +//----------------------------------------------------------------------------- +// Purpose: Main app window +//----------------------------------------------------------------------------- +class CMDLPicker : public CBaseAssetPicker +{ + DECLARE_CLASS_SIMPLE( CMDLPicker, CBaseAssetPicker ); + +public: + + enum PageType_t + { + PAGE_NONE = 0, + PAGE_RENDER = 0x1, + PAGE_SEQUENCES = 0x2, + PAGE_ACTIVITIES = 0x4, + PAGE_SKINS = 0x8, + PAGE_INFO = 0x10, + PAGE_ALL = 0xFFFFFFFF, + }; + + CMDLPicker( vgui::Panel *pParent, int nFlags = PAGE_ALL ); + ~CMDLPicker(); + + // overridden frame functions + virtual void PerformLayout(); + virtual void OnCommand( const char *pCommand ); + + // Get current model + void GetSelectedMDLName( char *pBuffer, int nMaxLen ); + + // get current selected options page + int GetSelectedPage(); + + // Allows external apps to select a MDL + void SelectMDL( const char *pRelativePath ); + + // Set/Get Sequence + void SelectSequence( const char *pSequenceName ); + const char *GetSelectedSequenceName(); + + // Set/Get Activity + void SelectActivity( const char *pActivityName ); + const char *GetSelectedActivityName(); + + void SelectSkin( int nSkin ); + int GetSelectedSkin(); + +private: + MESSAGE_FUNC_PARAMS( OnAssetSelected, "AssetSelected", params ); + + virtual void OnSelectedAssetPicked( const char *pMDLName ); + + void RefreshActivitiesAndSequencesList(); + void RefreshRenderSettings(); + int UpdateSkinsList(); + void UpdateInfoTab(); + int UpdatePropDataList( KeyValues *pkvPropData, bool &bIsStatic ); + + // Plays the selected activity + void PlaySelectedActivity( ); + + // Plays the selected sequence + void PlaySelectedSequence( ); + + MESSAGE_FUNC_PARAMS( OnCheckButtonChecked, "CheckButtonChecked", kv ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC( OnPageChanged, "PageChanged" ); + + CMDLPanel *m_pMDLPreview; + vgui::Splitter* m_pFileBrowserSplitter; + vgui::Splitter* m_pPreviewSplitter; + + vgui::PropertySheet *m_pViewsSheet; + vgui::PropertyPage *m_pRenderPage; + vgui::PropertyPage *m_pSequencesPage; + vgui::PropertyPage *m_pActivitiesPage; + vgui::PropertyPage *m_pSkinsPage; + vgui::PropertyPage *m_pInfoPage; + + vgui::ListPanel *m_pSequencesList; + vgui::ListPanel *m_pActivitiesList; + vgui::ListPanel *m_pSkinsList; + vgui::ListPanel *m_pPropDataList; + + MDLHandle_t m_hSelectedMDL; + + int m_nFlags; + + friend class CMDLPickerFrame; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Main app window +//----------------------------------------------------------------------------- +class CMDLPickerFrame : public CBaseAssetPickerFrame +{ + DECLARE_CLASS_SIMPLE( CMDLPickerFrame, CBaseAssetPickerFrame ); + +public: + CMDLPickerFrame( vgui::Panel *pParent, const char *pTitle, int nFlags = CMDLPicker::PAGE_ALL ); + virtual ~CMDLPickerFrame(); + + // Allows external apps to select a MDL + void SelectMDL( const char *pRelativePath ); +}; + + +#endif // MDLPICKER_H diff --git a/public/matsys_controls/mdlsequencepicker.h b/public/matsys_controls/mdlsequencepicker.h new file mode 100644 index 0000000..aa62906 --- /dev/null +++ b/public/matsys_controls/mdlsequencepicker.h @@ -0,0 +1,116 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef MDLSEQUENCEPICKER_H +#define MDLSEQUENCEPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/ImageList.h" +#include "vgui_controls/Frame.h" +#include "datacache/imdlcache.h" +#include "matsys_controls/mdlpanel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Splitter; + class Button; +} + +class CGameFileTreeView; + + +//----------------------------------------------------------------------------- +// Purpose: Main app window +//----------------------------------------------------------------------------- +class CMDLSequencePicker : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CMDLSequencePicker, vgui::EditablePanel ); +public: + CMDLSequencePicker( vgui::Panel *pParent ); + virtual ~CMDLSequencePicker(); + + // overridden frame functions + virtual void Activate(); + virtual void OnClose(); + virtual void PerformLayout(); + virtual void OnTick(); + + char const *GetModelName(); + char const *GetSequenceName(); + int GetSequenceNumber(); + +private: + void SelectMDL( const char *pMDLName ); + void RefreshFileList(); + void RefreshActivitiesAndSequencesList(); + + // Plays the selected activity + void PlaySelectedActivity( ); + + // Plays the selected sequence + void PlaySelectedSequence( ); + + MESSAGE_FUNC( OnFileSelected, "TreeViewItemSelected" ); + MESSAGE_FUNC_PTR_CHARPTR( OnTextChanged, "TextChanged", Panel, text ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC( OnPageChanged, "PageChanged" ); + + // changes +// MESSAGE_FUNC_INT( CloakFolder, "CloakFolder", item ); +// MESSAGE_FUNC_INT( OpenFileForEdit, "EditFile", item ); +// MESSAGE_FUNC_INT( OpenFileForDelete, "DeleteFile", item ); + + CMDLPanel *m_pMDLPreview; + vgui::ComboBox *m_pFilterList; + CGameFileTreeView *m_pFileTree; + vgui::ImageList m_Images; + vgui::Splitter* m_pMDLSplitter; + vgui::Splitter* m_pSequenceSplitter; + vgui::PropertySheet *m_pViewsSheet; + vgui::PropertyPage *m_pSequencesPage; + vgui::PropertyPage *m_pActivitiesPage; + + vgui::ListPanel *m_pSequencesList; + vgui::ListPanel *m_pActivitiesList; + + MDLHandle_t m_hSelectedMDL; + + friend class CMDLSequencePickerFrame; +}; + +//----------------------------------------------------------------------------- +// Model sequence picker frame +//----------------------------------------------------------------------------- +class CMDLSequencePickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CMDLSequencePickerFrame, vgui::Frame ); +public: + CMDLSequencePickerFrame( vgui::Panel *parent, char const *title ); + virtual ~CMDLSequencePickerFrame(); + + virtual void PerformLayout(); + +protected: + + virtual void OnTick(); + + MESSAGE_FUNC( OnOK, "OnOK" ); + MESSAGE_FUNC( OnCancel, "OnCancel" ); + +private: + CMDLSequencePicker *m_pMDLSequencePicker; + + vgui::Button *m_pOK; + vgui::Button *m_pCancel; +}; + +#endif // MDLSEQUENCEPICKER_H diff --git a/public/matsys_controls/picker.h b/public/matsys_controls/picker.h new file mode 100644 index 0000000..9361b05 --- /dev/null +++ b/public/matsys_controls/picker.h @@ -0,0 +1,133 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: An arbitrary picker +// +//============================================================================= + +#ifndef PICKER_H +#define PICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/Frame.h" +#include "tier1/utlstring.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Panel; +} + + +//----------------------------------------------------------------------------- +// List of strings to appear in the picker +//----------------------------------------------------------------------------- +enum PickerChoiceType_t +{ + PICKER_CHOICE_STRING = 0, + PICKER_CHOICE_PTR, +}; + +struct PickerInfo_t +{ + const char *m_pChoiceString; // This is what displays in the dialog + union + { + const char *m_pChoiceValue; + void *m_pChoiceValuePtr; + }; +}; + +struct PickerList_t +{ + PickerList_t() : m_Type( PICKER_CHOICE_STRING ) {} + PickerList_t( int nGrowSize, int nInitSize ) : m_Choices( nGrowSize, nInitSize ), m_Type( PICKER_CHOICE_STRING ) {} + + int Count() const { return m_Choices.Count(); } + PickerInfo_t& operator[]( int i ) { return m_Choices[i]; } + const PickerInfo_t& operator[]( int i ) const { return m_Choices[i]; } + int AddToTail() { return m_Choices.AddToTail(); } + void RemoveAll() { return m_Choices.RemoveAll(); } + + PickerChoiceType_t m_Type; + CUtlVector< PickerInfo_t > m_Choices; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Base class for choosing raw assets +//----------------------------------------------------------------------------- +class CPicker : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CPicker, vgui::EditablePanel ); + +public: + CPicker( vgui::Panel *pParent, const char *pColumnHeader, const char *pTextType ); + ~CPicker(); + + // Sets the list of strings to display + void SetStringList( const PickerList_t &list ); + + // Purpose: + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + + // Returns the selected string + PickerChoiceType_t GetSelectionType() const; + const char *GetSelectedString( ) const; + void *GetSelectedPtr( ) const; + + // Returns the index of the selected string + int GetSelectedIndex(); + +private: + void RefreshChoiceList( ); + MESSAGE_FUNC( OnTextChanged, "TextChanged" ); + + vgui::TextEntry *m_pFilterList; + vgui::ListPanel *m_pPickerBrowser; + CUtlString m_Filter; + const char *m_pPickerType; + const char *m_pPickerTextType; + const char *m_pPickerExt; + const char *m_pPickerSubDir; + PickerChoiceType_t m_Type; + + friend class CPickerFrame; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for picker +//----------------------------------------------------------------------------- +class CPickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CPickerFrame, vgui::Frame ); + +public: + CPickerFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, const char *pTextType ); + ~CPickerFrame(); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + + // Purpose: Activate the dialog + // The message "Picked" will be sent if something is picked. + // You can pass in keyvalues to get added to the message also. + void DoModal( const PickerList_t &list, KeyValues *pContextKeyValues = NULL ); + +private: + void CleanUpMessage(); + + CPicker *m_pPicker; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + KeyValues *m_pContextKeyValues; +}; + + +#endif // PICKER_H diff --git a/public/matsys_controls/potterywheelpanel.h b/public/matsys_controls/potterywheelpanel.h new file mode 100644 index 0000000..a097eb5 --- /dev/null +++ b/public/matsys_controls/potterywheelpanel.h @@ -0,0 +1,160 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef POTTERYWHEELPANEL_H +#define POTTERYWHEELPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/EditablePanel.h" +#include "materialsystem/materialsystemutil.h" +#include "mathlib/camera.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IManipulator; +class CPotteryWheelManip; +class CBaseManipulator; +class CTransformManipulator; +class CRotationManipulator; +class CTranslationManipulator; +class CZoomManipulator; +class CDmxElement; + +namespace vgui +{ + class IScheme; +} + + +//----------------------------------------------------------------------------- +// Pottery wheel Panel +//----------------------------------------------------------------------------- +class CPotteryWheelPanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CPotteryWheelPanel, vgui::EditablePanel ); + +public: + // constructor, destructor + CPotteryWheelPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CPotteryWheelPanel(); + + // Overriden methods of vgui::Panel + virtual void Paint(); + + virtual void OnKeyCodePressed ( vgui::KeyCode code ); + virtual void OnKeyCodeReleased( vgui::KeyCode code ); + virtual void OnMousePressed ( vgui::MouseCode code ); + virtual void OnMouseDoublePressed( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnCursorMoved( int x, int y ); + virtual void OnMouseWheeled( int delta ); + virtual void OnTick(); + + virtual void OnMouseCaptureLost(); + + // Sets the camera to look at the the thing we're spinning around + void LookAt( const Vector &vecCenter, float flRadius ); + void LookAt( float flRadius ); + + void ComputePanelPosition( const Vector &vecPosition, Vector2D *pPanelPos ); + + void SetBackgroundColor( int r, int g, int b ); + void SetBackgroundColor( const Color& c ); + const Color& GetBackgroundColor() const; + void SetGridColor( int r, int g, int b ); + + // Light probe + void SetLightProbe( CDmxElement *pLightProbe ); + + // Camera. + int GetCameraFOV( void ); + void SetCameraFOV( float flFOV ); + void SetCameraPositionAndAngles( const Vector &vecPos, const QAngle &angDir ); + void SetCameraOffset( const Vector &vecOffset ); + void ResetCameraPivot( void ); + void ComputeCameraTransform( matrix3x4_t *pWorldToCamera ); + void UpdateCameraTransform(); + virtual void ResetView(); + + // Allow the parent to be notified of mouse actions + void SetParentMouseNotify( bool bParentMouseNotify ); + + + +private: + // Inherited classes must implement this + virtual void OnPaint3D() = 0; + +protected: + + + + enum ManipulationMode_t + { + CAMERA_ROTATE, + CAMERA_TRANSLATE, + CAMERA_ZOOM, + LIGHT_MODE, + }; + + virtual void EnterManipulationMode( ManipulationMode_t manipMode, bool bMouseCapture = true, vgui::MouseCode mouseCode = vgui::MouseCode( -1 ) ); + void Select(); + void AcceptManipulation( bool bReleaseMouseCapture = true ); + void CancelManipulation(); + void EnableMouseCapture( bool enable, vgui::MouseCode code = vgui::MouseCode( -1 ) ); + bool WarpMouse( int &x, int &y ); + + void CreateDefaultLights(); + MaterialLightingState_t m_LightingState; + matrix3x4_t m_LightToWorld[MATERIAL_MAX_LIGHT_COUNT]; + + + IManipulator *m_pCurrentManip; + int m_nManipStartX, m_nManipStartY; + + bool HasLightProbe() const; + ITexture *GetLightProbeCubemap( bool bHDR ); + void DrawGrid(); + CMaterialReference m_Wireframe; + +private: + void SetupRenderState( int nDisplayWidth, int nDisplayHeight ); + void DestroyLights(); + + CMaterialReference m_LightProbeBackground; + CMaterialReference m_LightProbeHDRBackground; + CTextureReference m_LightProbeCubemap; + CTextureReference m_LightProbeHDRCubemap; + + Camera_t m_Camera; + matrix3x4_t m_CameraPivot; + + Color m_ClearColor; + Color m_GridColor; + + Vector m_vecCameraOffset; + CRotationManipulator *m_pCameraRotate; + CTranslationManipulator *m_pCameraTranslate; + CZoomManipulator *m_pCameraZoom; + CPotteryWheelManip *m_pLightManip; + vgui::MouseCode m_nCaptureMouseCode; + + int m_xoffset, m_yoffset; + + bool m_bHasLightProbe : 1; + bool m_bParentMouseNotify : 1; + + CPanelAnimationVar( bool, m_bUseParentBG, "useparentbg", "0" ); +}; + + +#endif // SIMPLEPOTTERYWHEELPANEL_H diff --git a/public/matsys_controls/proceduraltexturepanel.h b/public/matsys_controls/proceduraltexturepanel.h new file mode 100644 index 0000000..d5a7bde --- /dev/null +++ b/public/matsys_controls/proceduraltexturepanel.h @@ -0,0 +1,92 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef PROCEDURALTEXTUREPANEL_H +#define PROCEDURALTEXTUREPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/itexture.h" +#include "materialsystem/MaterialSystemUtil.h" +#include "vgui_controls/EditablePanel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct BGRA8888_t; + + +//----------------------------------------------------------------------------- +// +// Procedural texture image panel +// +//----------------------------------------------------------------------------- +class CProceduralTexturePanel : public vgui::EditablePanel, public ITextureRegenerator +{ + DECLARE_CLASS_SIMPLE( CProceduralTexturePanel, vgui::EditablePanel ); + +public: + // constructor + CProceduralTexturePanel( vgui::Panel *pParent, const char *pName ); + ~CProceduralTexturePanel(); + + // Methods of ITextureRegenerator + virtual void Release() {} + virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ); + + // initialization, shutdown + virtual bool Init( int nWidth, int nHeight, bool bAllocateImageBuffer ); + virtual void Shutdown(); + + // Returns the image buffer + dimensions + BGRA8888_t *GetImageBuffer(); + int GetImageWidth() const; + int GetImageHeight() const; + + // Redownloads the procedural texture + void DownloadTexture(); + + // Sets the rectangle to paint. Use null to fill the entire panel + void SetPaintRect( const Rect_t *pPaintRect = NULL ); + + // Sets the texcoords to use with the procedural texture + void SetTextureSubRect( const Rect_t &subRect ); + + // Maintain proportions when drawing + void MaintainProportions( bool bEnable ); + + virtual void Paint( void ); + virtual void PaintBackground( void ) {} + +private: + void CleanUp(); + +protected: + // Image buffer + BGRA8888_t *m_pImageBuffer; + int m_nWidth; + int m_nHeight; + + // Paint rectangle + Rect_t m_PaintRect; + + // Texture coordinate rectangle + Rect_t m_TextureSubRect; + + CTextureReference m_ProceduralTexture; + CMaterialReference m_ProceduralMaterial; + + int m_nTextureID; + bool m_bMaintainProportions; + bool m_bUsePaintRect; +}; + + +#endif // PROCEDURALTEXTUREPANEL_H diff --git a/public/matsys_controls/sequencepicker.h b/public/matsys_controls/sequencepicker.h new file mode 100644 index 0000000..36ae0df --- /dev/null +++ b/public/matsys_controls/sequencepicker.h @@ -0,0 +1,178 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef SEQUENCEPICKER_H +#define SEQUENCEPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlstring.h" +#include "vgui_controls/Frame.h" +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/ImageList.h" +#include "datacache/imdlcache.h" +#include "matsys_controls/mdlpanel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class Splitter; +} + + +//----------------------------------------------------------------------------- +// Purpose: Sequence picker panel +//----------------------------------------------------------------------------- +class CSequencePicker : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CSequencePicker, vgui::EditablePanel ); + +public: + enum PickType_t + { + PICK_NONE = 0, + PICK_SEQUENCES = 0x1, + PICK_ACTIVITIES = 0x2, + PICK_ALL = PICK_SEQUENCES | PICK_ACTIVITIES, + PICK_SEQUENCE_PARAMETERS = 0x10, + }; + + static const int NUM_POSE_CONTROLS = 6; + static const int NUM_SEQUENCE_LAYERS = 4; + + // Flags come from PickType_t + CSequencePicker( vgui::Panel *pParent, int nFlags = PICK_ALL ); + ~CSequencePicker(); + + // overridden frame functions + virtual void PerformLayout(); + + // Process command messages + virtual void OnCommand( const char *pCommand ); + + // Sets the MDL to preview sequences for + void SetMDL( const char *pMDLName ); + + // Gets the selected activity/sequence + PickType_t GetSelectedSequenceType(); + const char *GetSelectedSequenceName( ); + + // Get the array of pose parameter values + void GetPoseParameters( CUtlVector< float > &poseParameters ) const; + + // Get the array of sequence layers + void GetSeqenceLayers( CUtlVector< MDLSquenceLayer_t > &sequenceLayers ) const; + + // Get the root motion generation state + bool GetGenerateRootMotion() const; + + +private: + + MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pData ); + MESSAGE_FUNC_PARAMS( OnTextKillFocus, "TextKillFocus", pData ); + MESSAGE_FUNC_PARAMS( OnTextNewLine, "TextNewLine", pData ); + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", pData ); + MESSAGE_FUNC_PARAMS( OnItemSelected, "ItemSelected", kv ); + MESSAGE_FUNC( OnPageChanged, "PageChanged" ); + + void RefreshActivitiesAndSequencesList(); + + // Plays the selected activity + void PlayActivity( const char *pActivityName ); + + // Update the controls and plays the selected sequence + void UpdateActiveSequence( const char *pSequenceName ); + + // Find the index of the sequence with the specified name + int FindSequence( const char *pSequenceName ) const; + + // Update the set of available pose parameters + void UpdateAvailablePoseParmeters(); + + // Update the value of the specified control control group + void SetPoseParameterValue( float flPoseParameterValue, int nParameterIndex ); + + // Set all pose parameters to their default values + void ResetPoseParametersToDefault(); + + // Update the pose parameter controls using the stored pose parameter values + void UpdatePoseControlsFromParameters(); + + // Update the pose parameter controls to match the set of pose parameters for the current mdl + void UpdatePoseParameterControlsForMdl(); + + // Update the sequence and weighting of the specified layer + void SetSequenceLayer( int nLayerIndex, int nSequenceIndex, float flWeight ); + + // Update the sequence layer controls to match the current layer data set + void UpdateLayerControls(); + + // Clear all the layer information and update the controls + void ResetLayers(); + + + + + CMDLPanel *m_pMDLPreview; + vgui::Splitter *m_pPreviewSplitter; + vgui::PropertySheet *m_pViewsSheet; + vgui::PropertyPage *m_pSequencesPage; + vgui::PropertyPage *m_pActivitiesPage; + vgui::ListPanel *m_pSequencesList; + vgui::ListPanel *m_pActivitiesList; + + vgui::ComboBox *m_pLayerSequenceSelectors[ NUM_SEQUENCE_LAYERS ]; + vgui::Slider *m_pLayerSequenceSliders[ NUM_SEQUENCE_LAYERS ]; + + vgui::CheckButton *m_pRootMotionCheckBox; + vgui::Button *m_pPoseDefaultButton; + vgui::Slider *m_pPoseValueSliders[ NUM_POSE_CONTROLS ]; + vgui::TextEntry *m_pPoseValueEntries[ NUM_POSE_CONTROLS ]; + vgui::ComboBox *m_pPoseParameterName[ NUM_POSE_CONTROLS ]; + + MDLSquenceLayer_t m_SequenceLayers[ NUM_SEQUENCE_LAYERS ]; + int m_PoseControlMap[ NUM_POSE_CONTROLS ]; // Provides index of pose parameter driven by each control + float m_PoseParameters[ MAXSTUDIOPOSEPARAM ]; // Array of pose parameter values to be used when rendering the mdl + + bool m_bSequenceParams; + MDLHandle_t m_hSelectedMDL; + CUtlString m_Filter; + + friend class CSequencePickerFrame; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Main app window +//----------------------------------------------------------------------------- +class CSequencePickerFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CSequencePickerFrame, vgui::Frame ); + +public: + CSequencePickerFrame( vgui::Panel *pParent, int nFlags ); + + // Inherited from Frame + virtual void OnCommand( const char *pCommand ); + + // Purpose: Activate the dialog + void DoModal( const char *pMDLName ); + +private: + MESSAGE_FUNC_PARAMS( OnSequencePreviewChanged, "SequencePreviewChanged", kv ); + + CSequencePicker *m_pPicker; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; +}; + + +#endif // SEQUENCEPICKER_H diff --git a/public/matsys_controls/tgapreviewpanel.h b/public/matsys_controls/tgapreviewpanel.h new file mode 100644 index 0000000..9ced8da --- /dev/null +++ b/public/matsys_controls/tgapreviewpanel.h @@ -0,0 +1,41 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef TGAPREVIEWPANEL_H +#define TGAPREVIEWPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "matsys_controls/proceduraltexturepanel.h" +#include "tier1/utlstring.h" + + +//----------------------------------------------------------------------------- +// +// TGA Preview panel +// +//----------------------------------------------------------------------------- +class CTGAPreviewPanel : public CProceduralTexturePanel +{ + DECLARE_CLASS_SIMPLE( CTGAPreviewPanel, CProceduralTexturePanel ); + +public: + // constructor + CTGAPreviewPanel( vgui::Panel *pParent, const char *pName ); + void SetTGA( const char *pFullPath ); + const char *GetTGA() const; + + virtual void PerformLayout(); + +private: + CUtlString m_TGAName; +}; + + +#endif // TGAPREVIEWPANEL_H \ No newline at end of file diff --git a/public/matsys_controls/vmtpanel.h b/public/matsys_controls/vmtpanel.h new file mode 100644 index 0000000..1e69b92 --- /dev/null +++ b/public/matsys_controls/vmtpanel.h @@ -0,0 +1,91 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VMTPANEL_H +#define VMTPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "matsys_controls/PotteryWheelPanel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterial; +class CMeshBuilder; +class Vector; + +namespace vgui +{ + class ScrollBar; + class IScheme; +} + + +//----------------------------------------------------------------------------- +// Material Viewer Panel +//----------------------------------------------------------------------------- +class CVMTPanel : public CPotteryWheelPanel +{ + DECLARE_CLASS_SIMPLE( CVMTPanel, CPotteryWheelPanel ); + +public: + // constructor, destructor + CVMTPanel( vgui::Panel *pParent, const char *pName ); + virtual ~CVMTPanel(); + + // Set the material to draw + void SetMaterial( IMaterial *pMaterial ); + + // Set rendering mode (stretch to full screen, or use actual size) + void RenderUsingActualSize( bool bEnable ); + + // performs the layout + virtual void PerformLayout(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + +private: + // paint it stretched to the window size + void DrawStretchedToPanel( CMeshBuilder &meshBuilder ); + + // paint it actual size + void DrawActualSize( CMeshBuilder &meshBuilder ); + + // Draw it on a sphere + void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ); + + // paint it! + virtual void OnPaint3D(); + +private: + // The material to draw + IMaterial *m_pMaterial; + + // A texture to use for a lightmap + CTextureReference m_pLightmapTexture; + + // The default env_cubemap + CTextureReference m_DefaultEnvCubemap; + + // Are we using actual size or not? + bool m_bUseActualSize; + + // Scroll bars + vgui::ScrollBar *m_pHorizontalBar; + vgui::ScrollBar *m_pVerticalBar; + + // The viewable size + int m_iViewableWidth; + int m_iViewableHeight; +}; + +#endif // VMTPANEL_H + \ No newline at end of file diff --git a/public/matsys_controls/vmtpicker.h b/public/matsys_controls/vmtpicker.h new file mode 100644 index 0000000..87a035b --- /dev/null +++ b/public/matsys_controls/vmtpicker.h @@ -0,0 +1,116 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VMTPICKER_H +#define VMTPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "matsys_controls/BaseAssetPicker.h" +#include "materialsystem/materialsystemutil.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CVMTPreviewPanel; +class CSheetExtended; +class CVMTSequenceMenuButton; +class CVMTPicker; +class CVMTPreviewToolbar; +class CSheetSequencePanel; + +namespace vgui +{ + class Splitter; + class CheckButton; + class Slider; + class Panel; +} + +//----------------------------------------------------------------------------- + +class CVMTPreviewToolbar : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CVMTPreviewToolbar, EditablePanel ); +public: + CVMTPreviewToolbar( vgui::Panel *parent, const char *panelName, CVMTPicker *parentpicker ); + + void PopulateSequenceMenu( vgui::Menu *menu ); + int GetSequenceMenuItemCount( ); + void UpdateToolbarGUI(); + + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + + MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", pData ); + MESSAGE_FUNC_INT( OnSelectSequence, "OnSelectSequence", nSequenceNumber ); + + MESSAGE_FUNC( OnNextSequence, "OnNextSequence" ); + MESSAGE_FUNC( OnPrevSequence, "OnPrevSequence" ); + + MESSAGE_FUNC_PARAMS( OnSheetSequenceSelected, "SheetSequenceSelected", pData ); + +private: + vgui::Slider *m_pSheetPreviewSpeed; + CVMTPicker *m_pParentPicker; + vgui::Button *m_pNextSeqButton; + vgui::Button *m_pPrevSeqButton; + vgui::MenuButton *m_pSequenceSelection; + vgui::MenuButton *m_pSequenceSelection_Second; + CSheetSequencePanel *m_pSheetPanel; + CSheetSequencePanel *m_pSheetPanel_Second; +}; + +//----------------------------------------------------------------------------- +// Purpose: Base class for choosing raw assets +//----------------------------------------------------------------------------- +class CVMTPicker : public CBaseAssetPicker +{ + DECLARE_CLASS_SIMPLE( CVMTPicker, CBaseAssetPicker ); + +public: + CVMTPicker( vgui::Panel *pParent, bool bAllowMultiselect = false ); + virtual ~CVMTPicker(); + + virtual void CustomizeSelectionMessage( KeyValues *pKeyValues ); + + void SetSheetPreviewSpeed( float flPreviewSpeed ); + void SetSelectedSequence( int nSequence ); + void SetSelectedSecondarySequence( int nSequence ); + + int GetSheetSequenceCount(); + int GetCurrentSequence(); + int GetCurrentSecondarySequence(); + int GetRealSequenceNumber(); + + CSheetExtended* GetSheet(); + IMaterial* GetMaterial(); + +private: + // Derived classes have this called when the previewed asset changes + virtual void OnSelectedAssetPicked( const char *pAssetName ); + + CVMTPreviewPanel *m_pVMTPreview2D; + CVMTPreviewPanel *m_pVMTPreview3D; + vgui::Splitter *m_p2D3DSplitter; + vgui::Splitter *m_pPreviewSplitter; + CVMTPreviewToolbar *m_pVMTPreviewToolbar; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for asset picker +//----------------------------------------------------------------------------- +class CVMTPickerFrame : public CBaseAssetPickerFrame +{ + DECLARE_CLASS_SIMPLE( CVMTPickerFrame, CBaseAssetPickerFrame ); + +public: + CVMTPickerFrame( vgui::Panel *pParent, const char *pTitle, bool bAllowMultiselect = false ); +}; + + +#endif // VMTPICKER_H diff --git a/public/matsys_controls/vmtpreviewpanel.h b/public/matsys_controls/vmtpreviewpanel.h new file mode 100644 index 0000000..5435ccc --- /dev/null +++ b/public/matsys_controls/vmtpreviewpanel.h @@ -0,0 +1,101 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VMTPREVIEWPANEL_H +#define VMTPREVIEWPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/panel.h" +#include "tier1/utlstring.h" +#include "materialsystem/materialsystemutil.h" +#include "mathlib/vector.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + +class CSheetExtended; + +//----------------------------------------------------------------------------- +// +// VMT Preview panel +// +//----------------------------------------------------------------------------- +class CVMTPreviewPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CVMTPreviewPanel, vgui::Panel ); + +public: + // constructor + CVMTPreviewPanel( vgui::Panel *pParent, const char *pName ); + void SetVMT( const char *pMaterialName ); + const char *GetVMT() const; + + CSheetExtended* GetSheet(); + IMaterial* GetMaterial(); + + bool VMTUsesSheets(); + int GetSheetSequenceCount(); + int GetCurrentSequence(); + int GetCurrentSecondarySequence(); + int GetRealSequenceNumber(); + void SetSheetSequence( int nSequence ); + void SetSecondarySheetSequence( int nSequence ); + + // Paints the texture + virtual void Paint( void ); + + // View it in 3D or 2D mode + void DrawIn3DMode( bool b3DMode ); + + void SetSheetPreviewSpeed( float flPreviewSpeed ); + +private: + + // Two different preview methods + void DrawSphere( void ); + void DrawRectangle( void ); + + // Set up a projection matrix for a 90 degree fov + void SetupProjectionMatrix( int nWidth, int nHeight ); + void SetupOrthoMatrix( int nWidth, int nHeight ); + + // Sets the camera to look at the the thing we're spinning around + void LookAt( const Vector &vecLookAt, float flRadius ); + + // Sets up lighting state + void SetupLightingState(); + + // Draw a sphere + void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ); + + // Draw sprite-card based materials + void RenderSheet( const Vector &vCenter, float flRadius ); + + CUtlString m_VMTName; + CMaterialReference m_Material; + CSheetExtended* m_pMaterialSheet; + CTextureReference m_pLightmapTexture; + CTextureReference m_DefaultEnvCubemap; + Vector m_LightDirection; + Color m_LightColor; + float m_flLightIntensity; + Vector m_vecCameraDirection; + float m_flLastRotationTime; + bool m_bDrawIn3DMode; + float m_flLastSwitchTime; + float m_flSheetPreviewSpeed; + int m_nCurrentSheetSequence; + int m_nCurrentSecondarySheetSequence; +}; + + +#endif // VMTPREVIEWPANEL_H \ No newline at end of file diff --git a/public/matsys_controls/vtfpicker.h b/public/matsys_controls/vtfpicker.h new file mode 100644 index 0000000..c1a77cc --- /dev/null +++ b/public/matsys_controls/vtfpicker.h @@ -0,0 +1,59 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VTFPICKER_H +#define VTFPICKER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "matsys_controls/BaseAssetPicker.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CVTFPreviewPanel; + +namespace vgui +{ + class Splitter; +} + + +//----------------------------------------------------------------------------- +// Purpose: Base class for choosing raw assets +//----------------------------------------------------------------------------- +class CVTFPicker : public CBaseAssetPicker +{ + DECLARE_CLASS_SIMPLE( CVTFPicker, CBaseAssetPicker ); + +public: + CVTFPicker( vgui::Panel *pParent ); + virtual ~CVTFPicker(); + +private: + // Derived classes have this called when the previewed asset changes + virtual void OnSelectedAssetPicked( const char *pAssetName ); + + CVTFPreviewPanel *m_pVTFPreview; + vgui::Splitter *m_pPreviewSplitter; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for asset picker +//----------------------------------------------------------------------------- +class CVTFPickerFrame : public CBaseAssetPickerFrame +{ + DECLARE_CLASS_SIMPLE( CVTFPickerFrame, CBaseAssetPickerFrame ); + +public: + CVTFPickerFrame( vgui::Panel *pParent, const char *pTitle ); +}; + + +#endif // VTFPICKER_H diff --git a/public/matsys_controls/vtfpreviewpanel.h b/public/matsys_controls/vtfpreviewpanel.h new file mode 100644 index 0000000..b48972a --- /dev/null +++ b/public/matsys_controls/vtfpreviewpanel.h @@ -0,0 +1,68 @@ +//====== Copyright © 1996-2003, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VTFPREVIEWPANEL_H +#define VTFPREVIEWPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "vgui_controls/panel.h" +#include "tier1/utlstring.h" +#include "materialsystem/materialsystemutil.h" +#include "mathlib/vector.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// +// VTF Preview panel +// +//----------------------------------------------------------------------------- +class CVTFPreviewPanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CVTFPreviewPanel, vgui::Panel ); + +public: + // constructor + CVTFPreviewPanel( vgui::Panel *pParent, const char *pName ); + void SetVTF( const char *pFullPath, bool bLoadImmediately = true ); + const char *GetVTF() const; + + // Paints the texture + virtual void Paint( void ); + +private: + void PaintNormalMapTexture( void ); + void PaintCubeTexture( void ); + void PaintStandardTexture( void ); + void PaintVolumeTexture( void ); + + // Set up a projection matrix for a 90 degree fov + void SetupProjectionMatrix( int nWidth, int nHeight ); + + // Sets the camera to look at the the thing we're spinning around + void LookAt( const Vector &vecLookAt, float flRadius ); + + // Draw a sphere + void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ); + + CUtlString m_VTFName; + CTextureReference m_PreviewTexture; + CMaterialReference m_PreviewMaterial; + int m_nTextureID; + Vector m_vecCameraDirection; + float m_flLastRotationTime; +}; + + +#endif // VTFPREVIEWPANEL_H \ No newline at end of file diff --git a/public/maya/IMayaVGui.h b/public/maya/IMayaVGui.h new file mode 100644 index 0000000..eda8f25 --- /dev/null +++ b/public/maya/IMayaVGui.h @@ -0,0 +1,67 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Interface for dealing with vgui focus issues across all plugins +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IMAYAVGUI_H +#define IMAYAVGUI_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/platform.h" +#include "appframework/iappsystem.h" +#include "vgui_controls/Frame.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class EditablePanel; +} + +class CVsVGuiWindowBase; + + +//----------------------------------------------------------------------------- +// Factory for creating vgui windows +//----------------------------------------------------------------------------- +abstract_class IMayaVguiWindowFactory +{ +public: + virtual void CreateVguiWindow( const char *pPanelName ) = 0; + virtual void DestroyVguiWindow( const char *pPanelName ) = 0; + virtual vgui::Frame *GetVGuiPanel( const char *pPanelName = NULL ) = 0; + virtual CVsVGuiWindowBase *GetVGuiWindow( const char *pPanelName = NULL ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Interface for dealing with vgui focus issues across all plugins +//----------------------------------------------------------------------------- +#define MAYA_VGUI_INTERFACE_VERSION "VMayaVGui001" +abstract_class IMayaVGui : public IAppSystem +{ +public: + virtual void InstallVguiWindowFactory( const char *pWindowTypeName, IMayaVguiWindowFactory *pFactory ) = 0; + virtual void RemoveVguiWindowFactory( const char *pWindowTypeName, IMayaVguiWindowFactory *pFactory ) = 0; + virtual void SetFocus( void *hWnd, int hVGuiContext ) = 0; + virtual bool HasFocus( void *hWnd ) = 0; + + // In this mode, maya's in a strange re-entrant mode waiting for a modal dialog + // We still get WM_PAINT messages, but we're in the middle of a callstack + // deep in the bowels of VGUI + virtual void SetModalMode( bool bEnable ) = 0; + virtual bool IsInModalMode( ) const = 0; +}; + +extern IMayaVGui* g_pMayaVGui; + + +#endif // IMAYAVGUI_H diff --git a/public/maya/VsMayaDmx.h b/public/maya/VsMayaDmx.h new file mode 100644 index 0000000..9037df2 --- /dev/null +++ b/public/maya/VsMayaDmx.h @@ -0,0 +1,30 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VSMAYADMX_H +#define VSMAYADMX_H + +#ifdef _WIN32 +#pragma once +#endif + +// Maya includes + +#include + +// Valve includes + +#include "movieobjects/dmemesh.h" + +class VsMayaDmx +{ + static CDmeMesh *MayaMeshToDmeMesh( + const MDagPath &i_mDagPath, + DmFileId_t fileId ); + +}; + +#endif // VSMAYADMX_H \ No newline at end of file diff --git a/public/maya/VsMayaMPxFactory.h b/public/maya/VsMayaMPxFactory.h new file mode 100644 index 0000000..dc0ca0a --- /dev/null +++ b/public/maya/VsMayaMPxFactory.h @@ -0,0 +1,1120 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== +// +// Purpose: Utility classes for creating, registering & deregistering +// Maya MPx* derived classes +// +//============================================================================= + +#ifndef VSMAYAMPXFACTORY_H +#define VSMAYAMPXFACTORY_H +#if defined( _WIN32 ) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MAYA_API_VERSION >= 200800 +#include +#endif //MAYA_API_VERSION >= 200800 + +//----------------------------------------------------------------------------- +// +// Forward declarations +// +//----------------------------------------------------------------------------- +class MFnPlugin; +namespace ValveMaya +{ + class CMSyntaxHelp; +} + + +//============================================================================= +// +// Base class for Maya MPx factories +// +//============================================================================= +class CVsMayaMPxFactoryBase +{ +public: + // Registers all MPx derived things that have been allocated + static MStatus RegisterEverything( MFnPlugin &pluginFn ); + + // Deregisters all MPx derived things that have been allocated + static MStatus DeregisterEverything( MFnPlugin &pluginFn ); + + // Displays a list of stuff in the plugin + static void DisplaySummary( const MString &pluginName ); + + // Types of things the MPxFactory can create + enum Type + { + // NOTE: Ensure this list of enums stays in sync with GetTypeName() array + kCommand, + kFileTranslator, + kDependencyNode, + kShaderNode, + kTransform, + kLocatorNode, + kImageFile, + // Insert new ones above here + kUnknown + }; + + void Enable( bool bEnabled ) { m_bEnabled = bEnabled; } + + bool IsEnabled() const { return m_bEnabled; } + +protected: + // Constructor + CVsMayaMPxFactoryBase(); + +private: + // The next factory + CVsMayaMPxFactoryBase* m_pNextFactory; + + // The starting factory + static CVsMayaMPxFactoryBase *s_pFirstFactory; + + // Register the thing associated with this factory + virtual MStatus Register( MFnPlugin &pluginFn ) const = 0; + + // Deregister the thing associated with this factory + virtual MStatus Deregister( MFnPlugin &pluginFn ) const = 0; + + // Everything has a name + virtual const MString &GetName() const = 0; + + // Everything has a description + virtual const MString &GetDesc() const = 0; + + // Everything has a type + virtual Type GetType() const = 0; + + // Everything has a type (map types to names) + MString GetTypeName() const; + + // Whether this factory is enabled or not + bool m_bEnabled; +}; + + +//----------------------------------------------------------------------------- +// +// Templatized helpers for creating MPx derived classes +// +//----------------------------------------------------------------------------- +template< class T > +class CVsMayaMPxFactory : public CVsMayaMPxFactoryBase +{ +private: + // Register the thing associated with this factory + virtual MStatus Register( MFnPlugin &pluginFn ) const + { + return T::Register( pluginFn ); + } + + // Deregister the thing associated with this factory + virtual MStatus Deregister( MFnPlugin &pluginFn ) const + { + return T::Deregister( pluginFn ); + } + + virtual const MString &GetName() const + { + return T::s_name; + } + + virtual const MString &GetDesc() const + { + return T::s_desc; + } + + virtual Type GetType() const + { + return T::GetType(); + } +}; + + +//============================================================================ +// +// Base class for Valve Maya commands ( CVsMayaMPxCommand ) +// +//============================================================================ +class CVsMayaMPxCommand : public MPxCommand +{ +public: + virtual const MString &GetName() const { return m_nullStr; } + virtual const MString &GetDesc() const { return m_nullStr; } + +protected: + // Derived classes must specify this to override syntax + virtual void SpecifySyntax( MSyntax &mSyntax, ValveMaya::CMSyntaxHelp &help ); + ValveMaya::CMSyntaxHelp *GetSyntaxHelp() { return m_pSyntaxHelp; } + +private: + ValveMaya::CMSyntaxHelp *m_pSyntaxHelp; + + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + MCreatorFunction creatorFunction, + MCreateSyntaxFunction createSyntaxFunction = NULL ); + + static MStatus Deregister( MFnPlugin &pluginFn, const MString &name ); + + template < class T > friend class CVsMayaMPxCommandDecorator; + + MString m_nullStr; +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya commands ( CVsMayaMPxCommandDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxCommandDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + static const MString &Desc() { return s_desc; }; + + virtual const MString &GetName() const { return Name(); }; + virtual const MString &GetDesc() const { return Desc(); }; + + static CVsMayaMPxFactoryBase::Type GetType() { return CVsMayaMPxFactoryBase::kCommand; } + +private: + friend class CVsMayaMPxFactoryBase; + template < class U > friend class CVsMayaMPxFactory; + + // These should be const but it's not because the CVsMayaMPxFactoryCommand class + // only knows its name and therefore it's description at runtime + + static MString s_name; + static MString s_desc; + static ValveMaya::CMSyntaxHelp s_mSyntaxHelp; // Keeps track of command line flags + + static void *Create() + { + CVsMayaMPxCommandDecorator *pDecorator = new CVsMayaMPxCommandDecorator< T >; + pDecorator->m_pSyntaxHelp = &s_mSyntaxHelp; + return pDecorator; + } + + static MSyntax CreateSyntax() + { + // Maya will simply never call this unless the 'hasSyntax()' virtual returns true + // doesn't matter if a syntaxCreator is registered or not, and an empty + // MSyntax is fine too. Also note the return is by value and not reference. + // Also... even when Maya does call this, it is only ever called once, the + // first time Maya needs to know what the syntax is (when the command is + // invoked or when help cmd is done + + MSyntax mSyntax; + T().SpecifySyntax( mSyntax, s_mSyntaxHelp ); + return mSyntax; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, Create, T().hasSyntax() ? CreateSyntax : NULL ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_name ); + } +}; + + +//============================================================================ +// +// Base class for Valve Maya commands ( CVsMayaMPxToolCommand ) +// +//============================================================================ +class CVsMayaMPxToolCommand : public MPxToolCommand +{ +public: + virtual const MString &GetName() const { return m_nullStr; } + virtual const MString &GetDesc() const { return m_nullStr; } + +protected: + // Derived classes must specify this to override syntax + virtual void SpecifySyntax( MSyntax &mSyntax, ValveMaya::CMSyntaxHelp &help ); + ValveMaya::CMSyntaxHelp *GetSyntaxHelp() { return m_pSyntaxHelp; } + +private: + ValveMaya::CMSyntaxHelp *m_pSyntaxHelp; + + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + MCreatorFunction creatorFunction, + MCreateSyntaxFunction createSyntaxFunction = NULL ); + + static MStatus Deregister( MFnPlugin &pluginFn, const MString &name ); + + template < class T > friend class CVsMayaMPxToolCommandDecorator; + + MString m_nullStr; +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya commands ( CVsMayaMPxToolCommandDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxToolCommandDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + static const MString &Desc() { return s_desc; }; + + virtual const MString &GetName() const { return Name(); }; + virtual const MString &GetDesc() const { return Desc(); }; + + static CVsMayaMPxFactoryBase::Type GetType() { return CVsMayaMPxFactoryBase::kCommand; } + +private: + friend class CVsMayaMPxFactoryBase; + template < class U > friend class CVsMayaMPxFactory; + + // These should be const but it's not because the CVsMayaMPxFactoryCommand class + // only knows its name and therefore it's description at runtime + + static MString s_name; + static MString s_desc; + static ValveMaya::CMSyntaxHelp s_mSyntaxHelp; // Keeps track of command line flags + + static void *Create() + { + CVsMayaMPxToolCommandDecorator *pDecorator = new CVsMayaMPxToolCommandDecorator< T >; + pDecorator->m_pSyntaxHelp = &s_mSyntaxHelp; + return pDecorator; + } + + static MSyntax CreateSyntax() + { + // Maya will simply never call this unless the 'hasSyntax()' virtual returns true + // doesn't matter if a syntaxCreator is registered or not, and an empty + // MSyntax is fine too. Also note the return is by value and not reference. + // Also... even when Maya does call this, it is only ever called once, the + // first time Maya needs to know what the syntax is (when the command is + // invoked or when help cmd is done + + MSyntax mSyntax; + T().SpecifySyntax( mSyntax, s_mSyntaxHelp ); + return mSyntax; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, Create, T().hasSyntax() ? CreateSyntax : NULL ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_name ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya file translators ( CVsMayaMPxFileTranslator ) +// +//============================================================================= +class CVsMayaMPxFileTranslator : public MPxFileTranslator +{ +public: + virtual const MString &GetName() const = 0; + virtual const MString &GetGUIName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MString &guiName, + MCreatorFunction creatorFunction ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MString &name ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya file translators ( CVsMayaMPxFileTranslatorDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxFileTranslatorDecorator : public T +{ +public: + virtual const MString &GetName() const { return s_name; }; + + virtual const MString &GetGUIName() const { return s_guiName; }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() { return CVsMayaMPxFactoryBase::kFileTranslator; } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MString s_guiName; + + static void *Create() + { + return new CVsMayaMPxFileTranslatorDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_guiName, Create ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_guiName ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya Dependency Nodes ( CVsMayaMPxNode ) +// +//============================================================================ +class CVsMayaMPxNode : public MPxNode +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction, + const MString &classification ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MTypeId &mTypeId ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya nodes ( CVsMayaMPxNodeDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxNodeDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return s_classification.length() ? CVsMayaMPxFactoryBase::kShaderNode : CVsMayaMPxFactoryBase::kDependencyNode; + } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MTypeId s_mTypeId; + + static const MInitializeFunction s_mInitializeFunction; + + static const MString s_classification; + + static void *Create() + { + return new CVsMayaMPxNodeDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_mTypeId, Create, s_mInitializeFunction, s_classification ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_mTypeId ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya Transform Nodes ( CVsMayaMPxTransform ) +// +//============================================================================ +class CVsMayaMPxTransform : public MPxTransform +{ +public: + virtual const MString &GetName() const = 0; + +protected: + +#if MAYA_API_VERSION >= 200900 + + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction, + MCreateXformMatrixFunction xformCreatorFunction = MPxTransformationMatrix::creator, + const MTypeId &xformMTypeId = MPxTransformationMatrix::baseTransformationMatrixId, + const MString *classification = NULL ); + +#else // #if MAYA_API_VERSION >= 200900 + + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction, + MCreatorFunction xformCreatorFunction = MPxTransformationMatrix::creator, + const MTypeId &xformMTypeId = MPxTransformationMatrix::baseTransformationMatrixId, + const MString *classification = NULL ); + +#endif // #if MAYA_API_VERSION >= 200900 + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MTypeId &mTypeId ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya commands ( CVsMayaMPxCommandDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxTransformDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() { return CVsMayaMPxFactoryBase::kTransform; } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MTypeId s_mTypeId; + + static const MInitializeFunction s_mInitializeFunction; + +#if MAYA_API_VERSION >= 200900 + + static const MCreateXformMatrixFunction s_xformMCreatorFunction; + +#else // #if MAYA_API_VERSION >= 200900 + + static const MCreatorFunction s_xformMCreatorFunction; + +#endif // #if MAYA_API_VERSION >= 200900 + + static const MTypeId s_xformMTypeId; + + static void *Create() + { + return new CVsMayaMPxTransformDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_mTypeId, Create, s_mInitializeFunction, s_xformMCreatorFunction, s_xformMTypeId ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_mTypeId ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya Locator Nodes ( CVsMayaMPxLocatorNode ) +// +//============================================================================ +class CVsMayaMPxLocatorNode : public MPxLocatorNode +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MTypeId &mTypeId ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya nodes ( CVsMayaMPxLocatorNodeDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxLocatorNodeDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return CVsMayaMPxFactoryBase::kLocatorNode; + } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MTypeId s_mTypeId; + + static const MInitializeFunction s_mInitializeFunction; + + static void *Create() + { + return new CVsMayaMPxLocatorNodeDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_mTypeId, Create, s_mInitializeFunction ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_mTypeId ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya Drag And Drop Behaviors ( CVsMayaMPxDragAndDropBehavior ) +// +//============================================================================ +class CVsMayaMPxDragAndDropBehavior : public MPxDragAndDropBehavior +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + MCreatorFunction creatorFunction ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MString &name ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya drag and drop behaviors ( CVsMayaMPxDragAndDropBehavior ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxDragAndDropBehaviorDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return CVsMayaMPxFactoryBase::kLocatorNode; + } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MInitializeFunction s_mInitializeFunction; + + static void *Create() + { + return new CVsMayaMPxDragAndDropBehaviorDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, Create ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_name ); + } +}; + + +//============================================================================= +// +// Base class for Valve Maya Shape Nodes ( CVsMayaMPxShapeNode ) +// +//============================================================================ +class CVsMayaMPxShapeNode : public MPxSurfaceShape +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction, + MCreatorFunction uiCreatorFunction ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MTypeId &mTypeId ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya shape nodes ( CVsMayaMPxShapeNodeDecorator ) +// +//----------------------------------------------------------------------------- +template < class T, class U > +class CVsMayaMPxShapeNodeDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return CVsMayaMPxFactoryBase::kLocatorNode; + } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MTypeId s_mTypeId; + + static const MInitializeFunction s_mInitializeFunction; + + static const MCreatorFunction s_uiCreatorFunction; + + static void *Create() + { + return new CVsMayaMPxShapeNodeDecorator< T, U >; + } + + static void *CreateUI() + { + return new U; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_mTypeId, Create, s_mInitializeFunction, CreateUI ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_mTypeId ); + } +}; + +#if MAYA_API_VERSION >= 200800 +//============================================================================= +// +// Base class for Valve Maya Image File Types ( CVsMayaMPxImageFile ) +// +//============================================================================ +class CVsMayaMPxImageFile : public MPxImageFile +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + MCreatorFunction creatorFunction, + const MStringArray &extensions ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MString &name ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya Image Files ( CVsMayaMPxImageFileDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxImageFileDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return CVsMayaMPxFactoryBase::kImageFile; + } + +private: + template < class T > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MStringArray s_extensions; + + static const MCreatorFunction s_creatorFunction; + + static void *Create() + { + return new CVsMayaMPxImageFileDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, Create, s_extensions ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_name ); + } +}; + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate an image file +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXIMAGEFILE( _class, _name, _extensions, _desc ) \ + const MString CVsMayaMPxImageFileDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxImageFileDecorator< _class >::s_desc( _desc ); \ + const MStringArray CVsMayaMPxImageFileDecorator< _class >::s_extensions( _extensions ); \ + static CVsMayaMPxFactory< CVsMayaMPxImageFileDecorator< _class > > s_##_name##_Factory + + +#endif // MAYA_API_VERSION >= 200800 + + +//============================================================================= +// +// Base class for Valve Maya Dependency Nodes ( CVsMayaMPxNode ) +// +//============================================================================ +class CVsMayaMPxDeformerNode : public MPxDeformerNode +{ +public: + virtual const MString &GetName() const = 0; + +protected: + static MStatus Register( + MFnPlugin &pluginFn, + const MString &name, + const MTypeId &mTypeId, + MCreatorFunction creatorFunction, + MInitializeFunction initFunction, + const MString &classification ); + + static MStatus Deregister( + MFnPlugin &pluginFn, + const MTypeId &mTypeId ); +}; + + +//----------------------------------------------------------------------------- +// +// Decorator class for Valve Maya nodes ( CVsMayaMPxDeformerNodeDecorator ) +// +//----------------------------------------------------------------------------- +template < class T > +class CVsMayaMPxDeformerNodeDecorator : public T +{ +public: + static const MString &Name() { return s_name; }; + + virtual const MString &GetName() const { return Name(); }; + + virtual const MString &GetDesc() const { return s_desc; }; + + static CVsMayaMPxFactoryBase::Type GetType() + { + return s_classification.length() ? CVsMayaMPxFactoryBase::kShaderNode : CVsMayaMPxFactoryBase::kDependencyNode; + } + +private: + template < class U > friend class CVsMayaMPxFactory; + + static const MString s_name; + + static const MString s_desc; + + static const MTypeId s_mTypeId; + + static const MInitializeFunction s_mInitializeFunction; + + static const MString s_classification; + + static void *Create() + { + return new CVsMayaMPxDeformerNodeDecorator< T >; + } + + static MStatus Register( MFnPlugin &pluginFn ) + { + return T::Register( pluginFn, s_name, s_mTypeId, Create, s_mInitializeFunction, s_classification ); + } + + static MStatus Deregister( MFnPlugin &pluginFn ) + { + return T::Deregister( pluginFn, s_mTypeId ); + } +}; + + +//============================================================================= +// +// Helper Macros +// +//============================================================================= + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a command +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXCOMMAND( _class, _name, _desc ) \ + MString CVsMayaMPxCommandDecorator< _class >::s_name( #_name ); \ + MString CVsMayaMPxCommandDecorator< _class >::s_desc( _desc ); \ + ValveMaya::CMSyntaxHelp CVsMayaMPxCommandDecorator< _class >::s_mSyntaxHelp; \ + static CVsMayaMPxFactory< CVsMayaMPxCommandDecorator< _class > > s_##_name##_Factory + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a command +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXTOOLCOMMAND( _class, _name, _desc ) \ + MString CVsMayaMPxToolCommandDecorator< _class >::s_name( #_name ); \ + MString CVsMayaMPxToolCommandDecorator< _class >::s_desc( _desc ); \ + ValveMaya::CMSyntaxHelp CVsMayaMPxToolCommandDecorator< _class >::s_mSyntaxHelp; \ + static CVsMayaMPxFactory< CVsMayaMPxToolCommandDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a translator +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXFILETRANSLATOR( _class, _name, _guiName, _desc ) \ + const MString CVsMayaMPxFileTranslatorDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxFileTranslatorDecorator< _class >::s_desc( _desc ); \ + const MString CVsMayaMPxFileTranslatorDecorator< _class >::s_guiName( _guiName ); \ + static CVsMayaMPxFactory< CVsMayaMPxFileTranslatorDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a regular dependency node +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXNODE( _class, _name, _typeId, _initializeFunction, _desc ) \ + const MString CVsMayaMPxNodeDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxNodeDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxNodeDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxNodeDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MString CVsMayaMPxNodeDecorator< _class >::s_classification( "" ); \ + static CVsMayaMPxFactory< CVsMayaMPxNodeDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a shader dependency node +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXSHADERNODE( _class, _name, _typeId, _initializeFunction, _classification, _desc ) \ + const MString CVsMayaMPxNodeDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxNodeDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxNodeDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxNodeDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MString CVsMayaMPxNodeDecorator< _class >::s_classification( _classification ); \ + static CVsMayaMPxFactory< CVsMayaMPxNodeDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a transform node +//----------------------------------------------------------------------------- +#if MAYA_API_VERSION >= 200900 + +#define INSTALL_MAYA_MPXTRANSFORM( _class, _name, _typeId, _initializeFunction, _desc ) \ + const MString CVsMayaMPxTransformDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxTransformDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxTransformDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MCreateXformMatrixFunction CVsMayaMPxTransformDecorator< _class >::s_xformMCreatorFunction( MPxTransformationMatrix::creator ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_xformMTypeId( MPxTransformationMatrix::baseTransformationMatrixId ); \ + static CVsMayaMPxFactory< CVsMayaMPxTransformDecorator< _class > > s_##_name##_Factory + +#else // #if MAYA_API_VERSION >= 200900 + +#define INSTALL_MAYA_MPXTRANSFORM( _class, _name, _typeId, _initializeFunction, _desc ) \ + const MString CVsMayaMPxTransformDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxTransformDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxTransformDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MCreatorFunction CVsMayaMPxTransformDecorator< _class >::s_xformMCreatorFunction( MPxTransformationMatrix::creator ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_xformMTypeId( MPxTransformationMatrix::baseTransformationMatrixId ); \ + static CVsMayaMPxFactory< CVsMayaMPxTransformDecorator< _class > > s_##_name##_Factory + +#endif // #if MAYA_API_VERSION >= 200900 + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a transform node with a custom transformation matrix +// TODO: Make CVsMayaMPxTransformationMatrix and create the MCreatorFunction for the user +//----------------------------------------------------------------------------- +#if MAYA_API_VERSION >= 200900 + +#define INSTALL_MAYA_MPXTRANSFORM_WITHMATRIX( _class, _name, _typeId, _initializeFunction, _xformCreatorFunction, _xformTypeId, _desc ) \ + const MString CVsMayaMPxTransformDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxTransformDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxTransformDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MCreateXformMatrixFunction CVsMayaMPxTransformDecorator< _class >::s_xformMCreatorFunction( _xformCreatorFunction ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_xformMTypeId( _xformTypeId ); \ + static CVsMayaMPxFactory< CVsMayaMPxTransformDecorator< _class > > s_##_name##_Factory + +#else // #if MAYA_API_VERSION >= 200900 + +#define INSTALL_MAYA_MPXTRANSFORM_WITHMATRIX( _class, _name, _typeId, _initializeFunction, _xformCreatorFunction, _xformTypeId, _desc ) \ + const MString CVsMayaMPxTransformDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxTransformDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxTransformDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MCreatorFunction CVsMayaMPxTransformDecorator< _class >::s_xformMCreatorFunction( _xformCreatorFunction ); \ + const MTypeId CVsMayaMPxTransformDecorator< _class >::s_xformMTypeId( _xformTypeId ); \ + static CVsMayaMPxFactory< CVsMayaMPxTransformDecorator< _class > > s_##_name##_Factory + +#endif // #if MAYA_API_VERSION >= 200900 + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a locator node +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXLOCATORNODE( _class, _name, _typeId, _initializeFunction, _desc ) \ + const MString CVsMayaMPxLocatorNodeDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxLocatorNodeDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxLocatorNodeDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxLocatorNodeDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + static CVsMayaMPxFactory< CVsMayaMPxLocatorNodeDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a drag and drop behavior +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXDRAGANDDROPBEHAVIOR( _class, _name, _desc ) \ + const MString CVsMayaMPxDragAndDropBehaviorDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxDragAndDropBehaviorDecorator< _class >::s_desc( _desc ); \ + static CVsMayaMPxFactory< CVsMayaMPxDragAndDropBehaviorDecorator< _class > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a shape node +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXSHAPENODE( _class, _name, _typeId, _initializeFunction, _uiClass, _desc ) \ + const MString CVsMayaMPxShapeNodeDecorator< _class, _uiClass >::s_name( #_name ); \ + const MString CVsMayaMPxShapeNodeDecorator< _class, _uiClass >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxShapeNodeDecorator< _class, _uiClass >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxShapeNodeDecorator< _class, _uiClass >::s_mInitializeFunction( _initializeFunction ); \ + static CVsMayaMPxFactory< CVsMayaMPxShapeNodeDecorator< _class, _uiClass > > s_##_name##_Factory + + +//----------------------------------------------------------------------------- +// Helper macro to instantiate a deformer dependency node +//----------------------------------------------------------------------------- +#define INSTALL_MAYA_MPXDEFORMERNODE( _class, _name, _typeId, _initializeFunction, _desc ) \ + const MString CVsMayaMPxDeformerNodeDecorator< _class >::s_name( #_name ); \ + const MString CVsMayaMPxDeformerNodeDecorator< _class >::s_desc( _desc ); \ + const MTypeId CVsMayaMPxDeformerNodeDecorator< _class >::s_mTypeId( _typeId ); \ + const MInitializeFunction CVsMayaMPxDeformerNodeDecorator< _class >::s_mInitializeFunction( _initializeFunction ); \ + const MString CVsMayaMPxDeformerNodeDecorator< _class >::s_classification( "" ); \ + static CVsMayaMPxFactory< CVsMayaMPxDeformerNodeDecorator< _class > > s_##_name##_Factory + + +#endif // VSMAYAMPXFACTORY_H \ No newline at end of file diff --git a/public/maya/VsVGuiWindow.h b/public/maya/VsVGuiWindow.h new file mode 100644 index 0000000..3a38fc6 --- /dev/null +++ b/public/maya/VsVGuiWindow.h @@ -0,0 +1,188 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Base class for windows that draw vgui in Maya +// +//===========================================================================// + +#ifndef VSVGUIWINDOW_H +#define VSVGUIWINDOW_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "imayavgui.h" +#include "vgui_controls/Frame.h" +#include "tier1/UtlMap.h" +#include "valveMaya.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMayaVGui; + + +//----------------------------------------------------------------------------- +// The singleton is defined here twice just so we don't have to include valvemaya.h also +//----------------------------------------------------------------------------- +extern IMayaVGui *g_pMayaVGui; + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class EditablePanel; +} + + +class CVsVGuiWindowBase +{ +public: + virtual void SetPeriod( float flPeriod ) = 0; + virtual void StartTick() = 0; + virtual void StartTick( float flPeriod ) = 0; + virtual void StopTick() = 0; + virtual void Tick( float flElapsedTime ) = 0; + virtual void NonTimerTick() = 0; +}; + + +//----------------------------------------------------------------------------- +// Creates, destroys a maya vgui window +//----------------------------------------------------------------------------- +CVsVGuiWindowBase *CreateMayaVGuiWindow( vgui::EditablePanel *pRootPanel, const char *pPanelName ); +void DestroyMayaVGuiWindow( const char *pPanelName ); + + +//----------------------------------------------------------------------------- +// Factory used to install vgui windows easily +//----------------------------------------------------------------------------- +class CVsVguiWindowFactoryBase : public IMayaVguiWindowFactory +{ +public: + CVsVguiWindowFactoryBase( const char *pWindowTypeName, const char *pDccStartupCommand ); + + // Returns the DCC command + const char *GetDccStartupCommand() const; + + // Registers/deregisters all vgui windows + static void RegisterAllVguiWindows( ); + static void UnregisterAllVguiWindows( ); + +protected: + const char *m_pWindowTypeName; + const char *m_pDccStartupCommand; + +private: + CVsVguiWindowFactoryBase *m_pNext; + static CVsVguiWindowFactoryBase *s_pFirstCommandFactory; +}; + +template< class T > +class CVsVguiWindowFactory : public CVsVguiWindowFactoryBase +{ + typedef CVsVguiWindowFactoryBase BaseClass; + + static bool StringLessFunc( const CUtlString &a, const CUtlString &b ) + { + return StringLessThan( a.Get(), b.Get() ); + } + + +public: + CVsVguiWindowFactory( const char *pWindowTypeName, const char *pDccCommand ) + : BaseClass( pWindowTypeName, pDccCommand ) + , m_panelMap( StringLessFunc ) + { + } + + struct PanelMapElem_s + { + CVsVGuiWindowBase *m_pVGuiWindow; + T *m_pPanel; + }; + + typedef CUtlMap< CUtlString, PanelMapElem_s > PanelMap_t; + + virtual void CreateVguiWindow( const char *pPanelName ) + { + T *pVGuiPanel = new T( NULL, pPanelName ); + vgui::Frame *pFrame = dynamic_cast< vgui::Frame * >( pVGuiPanel ); + + if ( pFrame ) + { + pFrame->SetSizeable( false ); + pFrame->SetCloseButtonVisible( false ); + pFrame->SetMoveable( false ); + + CVsVGuiWindowBase *pVGuiWindow = CreateMayaVGuiWindow( pVGuiPanel, pPanelName ); + + const CUtlString panelName( pPanelName ); + + PanelMap_t::IndexType_t nIndex = m_panelMap.Find( panelName ); + if ( m_panelMap.IsValidIndex( nIndex ) ) + { + merr << "[vsVguiWindow]: Panel \"" << pPanelName << "\" of Type: \"" << m_pWindowTypeName << "\" Already Exists!!!" << std::endl; + } + else + { + PanelMap_t::ElemType_t &element = m_panelMap.Element( m_panelMap.Insert( panelName ) ); + element.m_pVGuiWindow = pVGuiWindow; + element.m_pPanel = pVGuiPanel; + } + } + } + + virtual void DestroyVguiWindow( const char *pPanelName ) + { + m_panelMap.Remove( CUtlString( pPanelName ) ); + DestroyMayaVGuiWindow( pPanelName ); + } + + virtual vgui::Frame *GetVGuiPanel( const char *pPanelName = NULL ) + { + if ( pPanelName ) + { + PanelMap_t::IndexType_t nPanelIndex = m_panelMap.Find( CUtlString( pPanelName ) ); + if ( m_panelMap.IsValidIndex( nPanelIndex ) ) + return dynamic_cast< vgui::Frame * >( m_panelMap.Element( nPanelIndex ).m_pPanel ); + } + else if ( m_panelMap.Count() > 0 ) + { + return dynamic_cast< vgui::Frame * >( m_panelMap.Element( m_panelMap.FirstInorder() ).m_pPanel ); + } + + return NULL; + } + + virtual CVsVGuiWindowBase *GetVGuiWindow( const char *pPanelName = NULL ) + { + if ( pPanelName ) + { + PanelMap_t::IndexType_t nPanelIndex = m_panelMap.Find( CUtlString( pPanelName ) ); + if ( m_panelMap.IsValidIndex( nPanelIndex ) ) + return m_panelMap.Element( nPanelIndex ).m_pVGuiWindow; + } + else if ( m_panelMap.Count() > 0 ) + { + return m_panelMap.Element( m_panelMap.FirstInorder() ).m_pVGuiWindow; + } + + return NULL; + } + +private: + + PanelMap_t m_panelMap; +}; + + +#define INSTALL_MAYA_VGUI_WINDOW( _className, _windowTypeName, _dccCommand ) \ + static CVsVguiWindowFactory< _className > s_VsVguiWindowFactory##_className##( _windowTypeName, _dccCommand ) + + +#endif // VSVGUIWINDOW_H diff --git a/public/maya/valveMaya.h b/public/maya/valveMaya.h new file mode 100644 index 0000000..95fa579 --- /dev/null +++ b/public/maya/valveMaya.h @@ -0,0 +1,544 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== +// +// Purpose: +// +//============================================================================= + +#ifndef VALVEMAYA_H +#define VALVEMAYA_H + +#if defined( _WIN32 ) +#pragma once +#endif + +// Maya Includes + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Valve Includes + +#include "tier1/stringpool.h" +#include "tier1/utlstring.h" +#include "tier1/utlstringmap.h" +#include "tier1/utlvector.h" +#include "tier1/interface.h" +#include "mathlib/mathlib.h" + +#include "ValveMaya/Undo.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMayaVGui; + + +//----------------------------------------------------------------------------- +// Maya-specific library singletons +//----------------------------------------------------------------------------- +extern IMayaVGui *g_pMayaVGui; + + +//----------------------------------------------------------------------------- +// +// Purpose: Group a bunch of functions into the Valve Maya Namespace +// +//----------------------------------------------------------------------------- + + +namespace ValveMaya +{ + //----------------------------------------------------------------------------- + // Forward declarations + //----------------------------------------------------------------------------- + class CUndo; + + //----------------------------------------------------------------------------- + // Statics + //----------------------------------------------------------------------------- + extern const MQuaternion v2mQuat; // Valve Engine -> Maya Quaternion + extern const MQuaternion m2vQuat; // Maya -> Valve Engine Quaternion + extern const MMatrix v2mMat; // Valve Engine -> Maya Matrix + extern const MMatrix m2vMat; // Maya -> Valve Engine Matrix + + extern const MQuaternion vc2mcQuat; // Valve Engine Camera -> Maya Camera Quaternion + extern const MQuaternion mc2vcQuat; // Maya Camera -> Valve Camera Engine Quaternion + extern const MMatrix vc2mcMat; // Valve Engine Camera -> Maya Camera Quaternion + extern const MMatrix mc2vcMat; // Maya Camera -> Valve Camera Engine Quaternion + + inline MQuaternion ValveToMaya( const MQuaternion &q ) { return q * v2mQuat; } + inline MQuaternion ValveCameraToMayaCamera( const MQuaternion &q ) { return vc2mcQuat * q * v2mQuat; } + inline MQuaternion MayaToValve( const MQuaternion &q ) { return q * m2vQuat; } + inline MQuaternion MayaCameraToValveCamera( const MQuaternion &q ) { return mc2vcQuat * q * m2vQuat; } + + inline MVector ValveToMaya( const MVector &p ) { return p.rotateBy( v2mQuat ); } + inline MVector ValveCameraToMayaCamera( const MVector &p ) { return p.rotateBy( v2mQuat ); } + inline MVector MayaToValve( const MVector &p ) { return p.rotateBy( m2vQuat ); } + inline MVector MayaCameraToValveCamera( const MVector &p ) { return p.rotateBy( m2vQuat ); } + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool ConnectLibraries( CreateInterfaceFn factory ); +void DisconnectLibraries(); + +MStatus CreateDagNode( + const char *const i_nodeType, + const char *const i_transformName = NULL, + const MObject &i_parentObj = MObject::kNullObj, + MObject *o_pTransformObj = NULL, + MObject *o_pShapeObj = NULL, + MDagModifier *i_mDagModifier = NULL); + +inline MStatus CreateDagNode( + CUndo &undo, + const char *const i_nodeType, + const char *const i_transformName = NULL, + const MObject &i_parentObj = MObject::kNullObj, + MObject *o_pTransformObj = NULL, + MObject *o_pShapeObj = NULL ) +{ + return CreateDagNode( i_nodeType, i_transformName, i_parentObj, o_pTransformObj, o_pShapeObj, &undo.DagModifier() ); +} + +bool IsNodeVisible( const MDagPath &mDagPath, bool bTemplateAsInvisible = true ); +bool IsPathVisible( MDagPath mDagPath, bool bTemplateAsInvisible = true ); + +class CMSyntaxHelp +{ +public: + CMSyntaxHelp() + : m_groupedHelp( false ) // Make case sensitive + , m_helpCount( 0 ) + , m_shortNameLength( 0 ) + {} + + void Clear(); + + MStatus AddFlag( + MSyntax &i_mSyntax, + const char *i_shortName, + const char *i_longName, + const char *i_group, + const char *i_help, + const MSyntax::MArgType i_argType1 = MSyntax::kNoArg, + const MSyntax::MArgType i_argType2 = MSyntax::kNoArg, + const MSyntax::MArgType i_argType3 = MSyntax::kNoArg, + const MSyntax::MArgType i_argType4 = MSyntax::kNoArg, + const MSyntax::MArgType i_argType5 = MSyntax::kNoArg, + const MSyntax::MArgType i_argType6 = MSyntax::kNoArg); + + void PrintHelp( + const char *const i_cmdName, + const char *const i_cmdDesc, + int i_lineLength = 0U); + + void PrintHelp( + const MString &i_cmdName, + const MString &i_cmdDesc, + int i_lineLength = 0U) + { + PrintHelp( i_cmdName.asChar(), i_cmdDesc.asChar(), i_lineLength ); + } + +protected: +public: +protected: + CCountedStringPool m_stringPool; + + struct HelpData_t + { + const char *m_shortName; + const char *m_longName; + const char *m_help; + CUtlVector m_argTypes; + }; + + CUtlVector m_groupOrder; + CUtlStringMap > m_groupedHelp; + int m_helpCount; + int m_shortNameLength; +}; + +MString RemoveNameSpace( const MString &mString ); + +MString &BackslashToSlash( MString &mString ); + +template < class T_t > bool IsDefault( T_t &, const MPlug & ); + +bool IsDefault( const MPlug &aPlug ); + +uint NextAvailable( MPlug &mPlug ); + +MPlug NextAvailablePlug( MPlug &mPlug ); + +MObject AddColorSetToMesh( + const MString &colorSetName, + const MDagPath &mDagPath, + MDagModifier &mDagModifier ); + +MString GetMaterialPath( + const MObject &shadingGroupObj, + MObject *pSurfaceShaderObj, + MObject *pFileObj, + MObject *pPlace2dTextureObj, + MObject *pVmtObj, + bool *pbTransparent, + MString *pDebugWhy ); + +// Returns the first node that is connected to the specified plug as input or output +MObject FindConnectedNode( const MPlug &mPlug ); + +// Returns the plug connected to the specified plug as an input, a NULL plug if no plug is connected +MPlug FindInputPlug( const MPlug &dstPlug ); + +MPlug FindInputPlug( const MObject &dstNodeObj, const MString &dstPlugName ); + +MObject FindInputNode( const MPlug &dstPlug ); + +MObject FindInputNode( const MObject &dstNodeObj, const MString &dstPlugName ); + +MObject FindInputAttr( const MPlug &dstPlug ); + +MObject FindInputAttr( const MObject &dstNodeObj, const MString &dstPlugName ); + +// Returns the first found node MObject of the specified type in the history of the specified node +MObject FindInputNodeOfType( const MObject &dstNodeObj, const MString &typeName, const MString &dstPlugName ); + +MObject FindInputNodeOfType( const MObject &dstNodeObj, const MString &typeName, MSelectionList &doneList ); + +// Creates a deformer of the specified type and deforms the specified shape with an optional component +MObject DeformShape( ValveMaya::CUndo &undo, const MString &deformerType, const MDagPath &inOrigDagPath, MObject &origCompObj = MObject::kNullObj ); + +bool Substitute( MString &str, const MString &searchStr, const MString &replaceStr ); + +bool SubstituteCaseInsensitive( MString &str, const MString &searchStr, const MString &replaceStr ); + +bool SubstituteAll( MString &str, const MString &searchStr, const MString &replaceStr ); + +bool SubstituteAllCaseInsensitive( MString &str, const MString &searchStr, const MString &replaceStr ); + +void FixSlashes( MString &str, char correctPathSeparator = '/' ); + +//----------------------------------------------------------------------------- +// Converts a Maya MMatrix to a Valve matrix3x4_t (transpose) +//----------------------------------------------------------------------------- +inline void MatrixMayaToValve( matrix3x4_t &mValve, const MMatrix &mMaya ) +{ + mValve[0][0] = mMaya[0][0]; mValve[0][1] = mMaya[1][0]; mValve[0][2] = mMaya[2][0]; mValve[0][3] = mMaya[3][0]; + mValve[1][0] = mMaya[0][1]; mValve[1][1] = mMaya[1][1]; mValve[1][2] = mMaya[2][1]; mValve[1][3] = mMaya[3][1]; + mValve[2][0] = mMaya[0][2]; mValve[2][1] = mMaya[1][2]; mValve[2][2] = mMaya[2][2]; mValve[2][3] = mMaya[3][2]; +} + + +//----------------------------------------------------------------------------- +// Converts a Valve matrix3x4_t to a Maya MMatrix (transpose) +//----------------------------------------------------------------------------- +inline void MatrixValveToMaya( MMatrix &mMaya, const matrix3x4_t &mValve ) +{ + mMaya[0][0] = mValve[0][0]; mMaya[0][1] = mValve[1][0]; mMaya[0][2] = mValve[2][0]; mMaya[3][0] = 0.0; + mMaya[1][0] = mValve[0][1]; mMaya[1][1] = mValve[1][1]; mMaya[1][2] = mValve[2][1]; mMaya[3][1] = 0.0; + mMaya[2][0] = mValve[0][2]; mMaya[2][1] = mValve[1][2]; mMaya[2][2] = mValve[2][2]; mMaya[3][2] = 0.0; + mMaya[3][0] = mValve[0][3]; mMaya[3][1] = mValve[1][3]; mMaya[3][2] = mValve[2][3]; mMaya[3][3] = 1.0; +} + + +} // end namespace ValveMaya + + +// Make an alias for the ValveMaya namespace + +namespace vm = ValveMaya; + + +//----------------------------------------------------------------------------- +// A simple stream class for printing information to the Maya script editor +//----------------------------------------------------------------------------- +class CMayaStream +{ +public: + enum StreamType { kInfo, kWarning, kError }; + + CMayaStream( const StreamType i_streamType = kInfo ) + : m_streamType( i_streamType ) + {} + + template < class T_t > + CMayaStream &operator<<( const T_t &v ); + + // This is a hack so CMayaStream << std::endl works as expected + CMayaStream &operator<<( std::ostream &(*StdEndl_t)( std::ostream & ) ) + { + return flush(); + } + + CMayaStream &flush() { return outputString(); } + +protected: + CMayaStream &outputString() + { + // Always ensure it's terminated with a newline + if ( *( m_string.asChar() + m_string.length() - 1 ) != '\n' ) + { + m_string += "\n"; + } + + const char *pBegin = m_string.asChar(); + const char *const pEnd = pBegin + m_string.length(); + const char *pCurr = pBegin; + + while ( *pCurr && pCurr < pEnd ) + { + if ( *pCurr == '\n' ) + { + switch ( m_streamType ) + { + case kWarning: + MGlobal::displayWarning( MString( pBegin, pCurr - pBegin ) ); + break; + case kError: + MGlobal::displayError( MString( pBegin, pCurr - pBegin ) ); + break; + default: + MGlobal::displayInfo( MString( pBegin, pCurr - pBegin ) ); + break; + } + + ++pCurr; + pBegin = pCurr; + } + else + { + ++pCurr; + } + } + + m_string.clear(); + + return *this; + } + + CMayaStream &checkForFlush() + { + const char *pCurr = m_string.asChar(); + const char *pEnd = pCurr + m_string.length(); + while ( *pCurr && pCurr != pEnd ) + { + if ( *pCurr == '\n' ) + { + return outputString(); + } + + ++pCurr; + } + + return *this; + } + + StreamType m_streamType; + MString m_string; +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CMayaWarnStream : public CMayaStream +{ +public: + CMayaWarnStream() + : CMayaStream( CMayaStream::kWarning ) + {} + +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +class CMayaErrStream : public CMayaStream +{ +public: + CMayaErrStream() + : CMayaStream( CMayaStream::kError ) + {} +}; + + +//----------------------------------------------------------------------------- +// Specialization for std::string +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const std::string &v ) +{ + *this << v.c_str(); + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for char +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const char &v ) +{ + m_string += MString( &v, 1 ); + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for MVector +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const MVector &v ) +{ + m_string += v.x; + m_string += " "; + m_string += v.y; + m_string += " "; + m_string += v.z; + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for MFloatVector +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const MFloatVector &v ) +{ + m_string += v.x; + m_string += " "; + m_string += v.y; + m_string += " "; + m_string += v.z; + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for MEulerRotation +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const MEulerRotation &e ) +{ + m_string += MAngle( e.x, MAngle::kRadians ).asDegrees(); + m_string += " "; + m_string += MAngle( e.y, MAngle::kRadians ).asDegrees(); + m_string += " "; + m_string += MAngle( e.z, MAngle::kRadians ).asDegrees(); + switch ( e.order ) + { + case MEulerRotation::kXYZ: + m_string += " (XYZ)"; + break; + case MEulerRotation::kXZY: + m_string += " (XZY)"; + break; + case MEulerRotation::kYXZ: + m_string += " (YXZ)"; + break; + case MEulerRotation::kYZX: + m_string += " (YZX)"; + break; + case MEulerRotation::kZXY: + m_string += " (ZXY)"; + break; + case MEulerRotation::kZYX: + m_string += " (ZYX)"; + break; + default: + m_string += " (Unknown)"; + break; + } + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for MQuaternion +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const MQuaternion &q ) +{ + m_string += q.x; + m_string += " "; + m_string += q.y; + m_string += " "; + m_string += q.z; + m_string += " "; + m_string += q.w; + + m_string += " ("; + operator<<( q.asEulerRotation() ); + m_string += ")"; + + return *this; +} + + +//----------------------------------------------------------------------------- +// Specialization for Quaternion +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const Quaternion &q ) +{ + return operator<<( MQuaternion( q.x, q.y, q.z, q.w ) ); +} + + +//----------------------------------------------------------------------------- +// Specialization for RadianEuler +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const RadianEuler &e ) +{ + return operator<<( MEulerRotation( e.x, e.y, e.z ) ); +} + +//----------------------------------------------------------------------------- +// Specialization for RadianEuler +//----------------------------------------------------------------------------- +template <> +inline CMayaStream &CMayaStream::operator<<( const Vector &v ) +{ + return operator<<( MVector( v.x, v.y, v.z ) ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template < class T_t > +inline CMayaStream &CMayaStream::operator<<( const T_t &v ) +{ + m_string += v; + return checkForFlush(); +} + + +//----------------------------------------------------------------------------- +// +// minfo, mwarn & merr are ostreams which can be used to send stuff to +// the Maya history window +// +//----------------------------------------------------------------------------- + +extern CMayaStream minfo; +extern CMayaWarnStream mwarn; +extern CMayaErrStream merr; + + +#endif // VALVEMAYA_H diff --git a/public/maya/valveMaya/HyperShadeUtil.h b/public/maya/valveMaya/HyperShadeUtil.h new file mode 100644 index 0000000..cc6fa20 --- /dev/null +++ b/public/maya/valveMaya/HyperShadeUtil.h @@ -0,0 +1,46 @@ +//======= Copyright © 1996-2007, Valve Corporation, All rights reserved. ====== +// +// Purpose: Utils for working with HyperShade in Maya +// +//============================================================================= + + +// Maya includes +#include + + +// Valve includes +#include "valveMaya/Undo.h" + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +namespace ValveMaya +{ + + class CHyperShadeUtil + { + public: + CHyperShadeUtil(); + + CHyperShadeUtil( CUndo &undo ); + + MStatus AddUtility( const MObject &utilityNode ); + + MStatus AddShader( const MObject &shaderNode ); + + MStatus AddTexture( const MObject &textureNode ); + + protected: + CUndo m_tmpUndo; + CUndo &m_undo; + + MObject m_renderUtilityListObj; + MObject m_shaderListObj; + MObject m_textureListObj; + + void Init(); + }; + +} \ No newline at end of file diff --git a/public/maya/valveMaya/Undo.h b/public/maya/valveMaya/Undo.h new file mode 100644 index 0000000..c2e90f5 --- /dev/null +++ b/public/maya/valveMaya/Undo.h @@ -0,0 +1,327 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== +// +// Maya Undo helper, use it like this +// +// class CMyCmd : MPxCommand +// { +// // ... regular stuff ... // +// +// ValveMaya::CUndo m_undo; +// }; +// +// MStatus CMyCmd::doIt( const MArgList &mArgList ) +// { +// m_undo.SetArgList( Syntax(), mArgList ); +// } +// +// MStatus CMyCmd::redoIt( const MArgList &mArgList ) +// { +// // Get at command line args +// m_undo.ArgDatabase().isFlagSet( ... ); +// +// // Do operations +// m_undo.SaveCurrentSelection(); +// m_undo.DagModifier().createNode( ... ); +// m_undo.DagModifierDoIt(); +// m_undo.SetAttr( mPlug, value ); +// +// /// etc ... +// } +// +// MStatus CMyCmd::undoIt( const MArgList &mArgList ) +// { +// m_undo.Undo(); +// } +// +// bool CMyCmd::isUndoable() +// { +// return m_undo.IsUndoable(); +// } +// +// If there's a need to get fancy, any of the CUndoOp* classes can be +// constructed via 'new' and a boost::shared_ptr< CUndoOp > constructed +// with that pointed can be passed to CUndo::Push(). Note that means +// that the pointer will be managed by boost::shared_ptr so if the +// lifetime of the data needs to be controlled by the caller (it shouldn't) +// then a shared_ptr should be kept +// +// Setting the ArgList and using ArgDatabase doesn't affect the undo/redo +// ability but it's a convnient place to keep that data +// +//============================================================================= + +#ifndef VALVEMAYA_UNDO_H +#define VALVEMAYA_UNDO_H +#if defined( _WIN32 ) +#pragma once +#endif + + +// Standard includes + + +// Maya includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Valve includes +#include "tier1/utlstack.h" + + +namespace ValveMaya +{ + +// Forward declarations +class CUndoOp; + + +//============================================================================= +// +// +// CUndo: Undo stack manager class +// +// +//============================================================================= + +class CUndo +{ +public: + CUndo(); + + ~CUndo(); + + void Clear(); + + MStatus SetArgList( + const MSyntax &mSyntax, + const MArgList &mArgList ); + + const MArgDatabase &ArgDatabase(); + + void SaveCurrentSelection(); + + MDagModifier &DagModifier(); + + MStatus DagModifierDoIt(); + + MStatus Connect( + const MPlug &srcP, + const MPlug &dstP, + bool force = false ); + + bool SetAttr( + MPlug &mPlug, + MObject &val ); + + bool SetAttr( + MPlug &mPlug, + double val ); + + bool Lock( + MPlug &mPlug, + bool lock ); + + void NodeCreated( MObject &nodeObject ); + + void Push( + CUndoOp *pUndoOp ); + + bool IsUndoable() const; + + MStatus Undo(); + +protected: + MArgDatabase *m_pArgDatabase; + CUtlStack< CUndoOp * > m_undoStack; +}; + + +//============================================================================= +// +// +// CUndoOp: Undo stack member abstract base class +// +// +//============================================================================= +class CUndoOp +{ +public: + virtual ~CUndoOp() + { + } + + virtual void Undo() = 0; +}; + + +//============================================================================= +// +// +// CUndoOpDagModifier: Undo stack member Dag Modifier class +// +// +//============================================================================= +class CUndoOpDagModifier : public CUndoOp +{ +public: + virtual ~CUndoOpDagModifier() + { + } + + virtual void Undo() + { + m_mDagModifier.undoIt(); + } + +protected: + friend class CUndo; + + MDagModifier m_mDagModifier; +}; + + +//============================================================================= +// +// +// CUndoOpSetAttr: Undo stack member for setting attributes +// +// +//============================================================================= +class CUndoOpSetAttr : public CUndoOp +{ +public: + CUndoOpSetAttr( + MPlug &mPlug, + MObject &mObjectVal ); + + CUndoOpSetAttr( + MPlug &mPlug, + double numericVal ); + + virtual ~CUndoOpSetAttr() + { + } + + virtual void Undo(); + +protected: + MPlug m_mPlug; + MObject m_mObjectVal; + double m_numericVal; + const bool m_numeric; + +private: + // Visual C++ is retarded - tell it there's no assignment operator + CUndoOpSetAttr &operator=( const CUndoOpSetAttr &rhs ); + +}; + + +//============================================================================= +// +// +// CUndoOpSelection: Undo stack member for changing selection +// +// +//============================================================================= +class CUndoOpSelection : public CUndoOp +{ +public: + CUndoOpSelection(); + + virtual ~CUndoOpSelection() + { + } + + virtual void Undo(); + +protected: + MSelectionList m_mSelectionList; + +}; + + +//============================================================================= +// +// +// CUndoOpSelection: Undo stack member for locking and unlocking attributes +// +// +//============================================================================= +class CUndoOpLock : public CUndoOp +{ +public: + + CUndoOpLock( + MPlug &mPlug, + bool lock ); + + virtual ~CUndoOpLock() + { + } + + virtual void Undo(); + +protected: + MPlug m_mPlug; + const bool m_locked; + +private: + // Visual C++ is retarded - tell it there's no assignment operator + CUndoOpLock &operator=( const CUndoOpLock &rhs ); +}; + + +//============================================================================= +// +//============================================================================= +class CUndoOpResetRestPosition : public CUndoOp +{ +public: + CUndoOpResetRestPosition( + const MDagPath &mDagPath ); + + virtual ~CUndoOpResetRestPosition() + { + } + + virtual void Undo(); + +protected: + const MDagPath m_mDagPath; + MTransformationMatrix m_matrix; +}; + + +//============================================================================= +// For node creation via something like MFnMesh::create, etc.. +//============================================================================= +class CUndoOpNodeCreated : public CUndoOp +{ +public: + CUndoOpNodeCreated( + MObject &mObject ); + + virtual ~CUndoOpNodeCreated() + { + } + + virtual void Undo(); + +protected: + MObject m_nodeObject; +}; + + +} +#endif // VALVEMAYA_UNDO_H \ No newline at end of file diff --git a/public/mdllib/mdllib.h b/public/mdllib/mdllib.h new file mode 100644 index 0000000..f6d8431 --- /dev/null +++ b/public/mdllib/mdllib.h @@ -0,0 +1,149 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef MDLLIB_H +#define MDLLIB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlbuffer.h" +#include "appframework/IAppSystem.h" + +// +// Forward interface declarations +// +abstract_class IMdlLib; + +abstract_class IMdlStripInfo; + + +//----------------------------------------------------------------------------- +// Purpose: Interface to accessing model data operations +//----------------------------------------------------------------------------- + + +namespace MdlLib +{ + + struct MdlVertex + { + float position[3]; + float normal[3]; + float texcoord[2]; + }; + + struct MdlMesh + { + unsigned long checksum; + CUtlVector< unsigned int > ib; + CUtlVector< MdlVertex > vb; + }; + +}; + + +abstract_class IMdlLib : public IAppSystem +{ + // + // Stripping routines + // +public: + + // + // StripModelBuffers + // The main function that strips the model buffers + // mdlBuffer - mdl buffer, updated, no size change + // vvdBuffer - vvd buffer, updated, size reduced + // vtxBuffer - vtx buffer, updated, size reduced + // ppStripInfo - if nonzero on return will be filled with the stripping info + // + virtual bool StripModelBuffers( CUtlBuffer &mdlBuffer, CUtlBuffer &vvdBuffer, CUtlBuffer &vtxBuffer, IMdlStripInfo **ppStripInfo ) = 0; + + // + // CreateNewStripInfo + // Creates an empty strip info or resets an existing strip info so that it can be reused. + // + virtual bool CreateNewStripInfo( IMdlStripInfo **ppStripInfo ) = 0; + + // + // ParseMdlMesh + // The main function that parses the mesh buffers + // mdlBuffer - mdl buffer + // vvdBuffer - vvd buffer + // vtxBuffer - vtx buffer + // mesh - on return will be filled with the mesh info + // + virtual bool ParseMdlMesh( CUtlBuffer &mdlBuffer, CUtlBuffer &vvdBuffer, CUtlBuffer &vtxBuffer, MdlLib::MdlMesh &mesh ) = 0; + +}; + + +abstract_class IMdlStripInfo +{ + // + // Serialization + // +public: + // Save the strip info to the buffer (appends to the end) + virtual bool Serialize( CUtlBuffer &bufStorage ) const = 0; + + // Load the strip info from the buffer (reads from the current position as much as needed) + virtual bool UnSerialize( CUtlBuffer &bufData ) = 0; + + // + // Stripping info state + // +public: + // Returns the checksums that the stripping info was generated for: + // plChecksumOriginal if non-NULL will hold the checksum of the original model submitted for stripping + // plChecksumStripped if non-NULL will hold the resulting checksum of the stripped model + virtual bool GetCheckSum( long *plChecksumOriginal, long *plChecksumStripped ) const = 0; + + // + // Stripping + // +public: + + // + // StripHardwareVertsBuffer + // The main function that strips the vhv buffer + // vhvBuffer - vhv buffer, updated, size reduced + // + virtual bool StripHardwareVertsBuffer( CUtlBuffer &vhvBuffer ) = 0; + + // + // StripModelBuffer + // The main function that strips the mdl buffer + // mdlBuffer - mdl buffer, updated + // + virtual bool StripModelBuffer( CUtlBuffer &mdlBuffer ) = 0; + + // + // StripVertexDataBuffer + // The main function that strips the vvd buffer + // vvdBuffer - vvd buffer, updated, size reduced + // + virtual bool StripVertexDataBuffer( CUtlBuffer &vvdBuffer ) = 0; + + // + // StripOptimizedModelBuffer + // The main function that strips the vtx buffer + // vtxBuffer - vtx buffer, updated, size reduced + // + virtual bool StripOptimizedModelBuffer( CUtlBuffer &vtxBuffer ) = 0; + + // + // Release the object with "delete this" + // +public: + virtual void DeleteThis() = 0; +}; + + + +#endif // MDLLIB_H diff --git a/public/measure_section.cpp b/public/measure_section.cpp new file mode 100644 index 0000000..0cb11cc --- /dev/null +++ b/public/measure_section.cpp @@ -0,0 +1,271 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include +#include +#include "basetypes.h" +#include "measure_section.h" +#include "convar.h" + + +// Static members +CMeasureSection *CMeasureSection::s_pSections = 0; +int CMeasureSection::s_nCount = 0; +double CMeasureSection::m_dNextResort = 0.0; + +ConVar measure_resort( "measure_resort", "1.0", 0, "How often to re-sort profiling sections\n" ); +ConVar game_speeds( "game_speeds","0" ); +extern ConVar host_speeds; + +//----------------------------------------------------------------------------- +// Purpose: Creates a profiling section +// Input : *name - name of the section ( allocated on stack hopefully ) +//----------------------------------------------------------------------------- +CMeasureSection::CMeasureSection( const char *name ) +{ + // Just point at name since it's static + m_pszName = name; + + // Clear accumulators + Reset(); + SortReset(); + m_dMaxTime.Init(); + + // Link into master list + m_pNext = s_pSections; + s_pSections = this; + // Update count + s_nCount++; +} + +//----------------------------------------------------------------------------- +// Purpose: Destroys the object +//----------------------------------------------------------------------------- +CMeasureSection::~CMeasureSection( void ) +{ + m_pNext = NULL; + s_nCount--; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::UpdateMax( void ) +{ + if (m_dMaxTime.IsLessThan(m_dAccumulatedTime)) + { + m_dMaxTime.Init(); + CCycleCount::Add(m_dMaxTime,m_dAccumulatedTime,m_dMaxTime); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::Reset( void ) +{ + m_dAccumulatedTime.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMeasureSection::SortReset( void ) +{ + m_dTotalTime.Init(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CMeasureSection::GetName( void ) +{ + return m_pszName; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetTotalTime( void ) +{ + return m_dTotalTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetTime( void ) +{ + return m_dAccumulatedTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : +//----------------------------------------------------------------------------- +CCycleCount const& CMeasureSection::GetMaxTime( void ) +{ + return m_dMaxTime; +} + +//----------------------------------------------------------------------------- +// Purpose: Accumulates a timeslice +// Input : time - +//----------------------------------------------------------------------------- +void CMeasureSection::AddTime( CCycleCount const &rCount ) +{ + CCycleCount::Add(m_dAccumulatedTime, rCount, m_dAccumulatedTime); + CCycleCount::Add(m_dTotalTime, rCount, m_dTotalTime); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CMeasureSection +//----------------------------------------------------------------------------- +CMeasureSection *CMeasureSection::GetNext( void ) +{ + return m_pNext; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : CMeasureSection +//----------------------------------------------------------------------------- +CMeasureSection *CMeasureSection::GetList( void ) +{ + return s_pSections; +} + +//----------------------------------------------------------------------------- +// Purpose: Compares accumulated time for two sections +// Input : ppms1 - +// ppms2 - +// Output : static int +//----------------------------------------------------------------------------- +static int SectionCompare( const void* ppms1,const void* ppms2 ) +{ + CMeasureSection* pms1 = *(CMeasureSection**)ppms1; + CMeasureSection* pms2 = *(CMeasureSection**)ppms2; + + if ( pms1->GetTotalTime().IsLessThan(pms2->GetTotalTime()) ) + return 1; + else + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Sorts sections by time usage +//----------------------------------------------------------------------------- +void CMeasureSection::SortSections( void ) +{ + // Not enough to be sortable + if ( s_nCount <= 1 ) + return; + + CMeasureSection *sortarray[ 128 ]; + CMeasureSection *ms; + + memset(sortarray,sizeof(CMeasureSection*)*128,0); + + ms = GetList(); + int i; + int c = 0; + while ( ms ) + { + sortarray[ c++ ] = ms; + ms = ms->GetNext(); + } + + // Sort the array alphabetically + qsort( sortarray, c , sizeof( CMeasureSection * ), SectionCompare ); + + // Fix next pointers + for ( i = 0; i < c-1; i++ ) + { + sortarray[ i ]->m_pNext = sortarray[ i + 1 ]; + } + sortarray[i]->m_pNext = NULL; + + // Point head of list at it + s_pSections = sortarray[ 0 ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Creates an instance for timing a section +// Input : *ms - +//----------------------------------------------------------------------------- +CMeasureSectionInstance::CMeasureSectionInstance( CMeasureSection *ms ) +{ + // Remember where to put accumulated time + m_pMS = ms; + + if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) + return; + + // Get initial timestamp + m_Timer.Start(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMeasureSectionInstance::~CMeasureSectionInstance( void ) +{ + if ( host_speeds.GetInt() < 3 && !game_speeds.GetInt()) + return; + + // Get final timestamp + m_Timer.End(); + + // Add time to section + m_pMS->AddTime( m_Timer.GetDuration() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Re-sort all data and determine whether sort keys should be reset too ( after +// re-doing sort if needed ). +//----------------------------------------------------------------------------- +void ResetTimeMeasurements( void ) +{ +#if defined( _DEBUG ) || defined( FORCE_MEASURE ) + bool sort_reset = false; + + // Time to redo sort? + if ( measure_resort.GetFloat() > 0.0 && + GetRealTime() >= CMeasureSection::m_dNextResort ) + { + // Redo it + CMeasureSection::SortSections(); + // Set next time + CMeasureSection::m_dNextResort = GetRealTime() + measure_resort.GetFloat(); + // Flag to reset sort accumulator, too + sort_reset = true; + } + + // Iterate through the sections now + CMeasureSection *p = CMeasureSection::GetList(); + while ( p ) + { + // Reset regular accum. + p->Reset(); + // Reset sort accum less often + if ( sort_reset ) + { + p->SortReset(); + } + p = p->GetNext(); + } +#endif +} \ No newline at end of file diff --git a/public/measure_section.h b/public/measure_section.h new file mode 100644 index 0000000..4018a15 --- /dev/null +++ b/public/measure_section.h @@ -0,0 +1,127 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#if !defined( MEASURE_SECTION_H ) +#define MEASURE_SECTION_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/fasttimer.h" +#include "convar.h" + + +// This is the macro to use in your code to measure until the code goes +// out of scope +#if defined( _DEBUG ) || defined( FORCE_MEASURE ) +#define MEASURECODE( description ) \ + static CMeasureSection _xxx_ms( description ); \ + CMeasureSectionInstance _xxx_ms_inst( &_xxx_ms ); +#else +#define MEASURECODE( description ) +#endif + + +// ------------------------------------------------------------------------------------ // +// These things must exist in the executable for the CMeasureSection code to work. +// ------------------------------------------------------------------------------------ // +float GetRealTime(); // Get the clock's time. + +extern ConVar game_speeds; +extern ConVar measure_resort; +// ------------------------------------------------------------------------------------ // + + + +// Called once per frame to allow any necessary measurements to latch +void ResetTimeMeasurements( void ); + +//----------------------------------------------------------------------------- +// Purpose: Accumulates time for the named section +//----------------------------------------------------------------------------- +class CMeasureSection +{ +public: + // Allows for measuring named section + CMeasureSection( const char *name ); + virtual ~CMeasureSection( void ); + + + // Update max value hit + void UpdateMax( void ); + // Reset totals + void Reset( void ); + // Reset sortable totals + void SortReset( void ); + // Get static name of section + const char *GetName( void ); + + // Get accumulated time + CCycleCount const& GetTotalTime( void ); + + CCycleCount const& GetTime(); + + CCycleCount const& GetMaxTime(); + + // Add in some time + void AddTime( CCycleCount const &rCount ); + + // Get next section in chain + CMeasureSection *GetNext( void ); + + // Get head of list of all sections + static CMeasureSection *GetList( void ); + // Sort all sections by most time consuming + static void SortSections( void ); + +public: + // Time when list should be sorted again + static double m_dNextResort; + +private: + // Accumulated time for section + CCycleCount m_dAccumulatedTime; + + // Max time for section + CCycleCount m_dMaxTime; + + // Elapsed time for section + CCycleCount m_dTotalTime; + + // Name of section + const char *m_pszName; + // Next section in chain + CMeasureSection *m_pNext; + // Head of section list + static CMeasureSection *s_pSections; + // Quick total for doing sorts faster + static int s_nCount; +}; + +//----------------------------------------------------------------------------- +// Purpose: On construction marks time and on destruction adds time to +// parent CMeasureSection object +//----------------------------------------------------------------------------- +class CMeasureSectionInstance +{ +public: + // Constructor: Points to object to accumulate time into + CMeasureSectionInstance( CMeasureSection *ms ); + // Destructor: Latches accumulated time + virtual ~CMeasureSectionInstance( void ); + +private: + // Time of construction + CFastTimer m_Timer; + + // Where to place elapsed time + CMeasureSection *m_pMS; +}; + +#endif // MEASURE_SECTION_H \ No newline at end of file diff --git a/public/minmax.h b/public/minmax.h new file mode 100644 index 0000000..17b118e --- /dev/null +++ b/public/minmax.h @@ -0,0 +1,19 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#error use basetypes.h +#ifndef MINMAX_H +#define MINMAX_H + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#endif // MINMAX_H diff --git a/public/missionchooser/iasw_map_builder.h b/public/missionchooser/iasw_map_builder.h new file mode 100644 index 0000000..8d2a79b --- /dev/null +++ b/public/missionchooser/iasw_map_builder.h @@ -0,0 +1,67 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +// +// Interface to the runtime map (VMF) building class +// +//=============================================================================== + +#ifndef IASW_MAP_BUILDER_H +#define IASW_MAP_BUILDER_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +class KeyValues; + +class IASW_Map_Builder +{ +public: + //----------------------------------------------------------------------------- + // Ticks the map builder and updates progress. + //----------------------------------------------------------------------------- + virtual void Update( float flEngineTime ) = 0; + + // @TODO: clean up these entrypoints to be more consistent. It's confusing + // and convoluted right now because of the sheer number of different ways + // to build a map. + + //----------------------------------------------------------------------------- + // Schedules the random generation and BSP build of a map from its description + // or a pre-defined layout file. + //----------------------------------------------------------------------------- + virtual void ScheduleMapGeneration( + const char *pMapName, + float flTime, + KeyValues *pMissionSettings, + KeyValues *pMissionDefinition ) = 0; + + //----------------------------------------------------------------------------- + // Schedules the VMF export and BSP build of a .layout file. + //----------------------------------------------------------------------------- + virtual void ScheduleMapBuild( + const char *pMapName, + const float flTime ) = 0; + + //----------------------------------------------------------------------------- + // Gets a map build progress value in the range [0.0f, 1.0f] inclusive. + //----------------------------------------------------------------------------- + virtual float GetProgress() = 0; + + //----------------------------------------------------------------------------- + // Gets a string which describes the current status of the map builder. + //----------------------------------------------------------------------------- + virtual const char *GetStatusMessage() = 0; + + //----------------------------------------------------------------------------- + // Gets the name of the map being currently built. + //----------------------------------------------------------------------------- + virtual const char *GetMapName() = 0; + + //----------------------------------------------------------------------------- + // Gets a value indicating whether a map build is scheduled or in progress. + //----------------------------------------------------------------------------- + virtual bool IsBuildingMission() = 0; +}; + + +#endif // IASW_MAP_BUILDER_H \ No newline at end of file diff --git a/public/missionchooser/iasw_mission_chooser.h b/public/missionchooser/iasw_mission_chooser.h new file mode 100644 index 0000000..0c715a7 --- /dev/null +++ b/public/missionchooser/iasw_mission_chooser.h @@ -0,0 +1,135 @@ +#ifndef MISSION_CHOOSER_INT_H +#define MISSION_CHOOSER_INT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" +#include "utlvector.h" + +class IASW_Random_Missions; +class IASW_Mission_Chooser_Source; +class IASW_Map_Builder; +class KeyValues; +class CUniformRandomStream; +class Color; +class IASWSpawnSelection; + +class IASW_Location; + +class IASW_Location_Group +{ +public: + virtual const char* GetGroupName() = 0; + virtual const char* GetTitleText() = 0; + virtual const char* GetDescriptionText() = 0; + virtual const char* GetImageName() = 0; + virtual Color& GetColor() = 0; + virtual bool IsGroupLocked( CUtlVector &completedMissions ) = 0; + + virtual int GetHighestUnlockMissionID() = 0; + virtual int GetNumLocations() = 0; + virtual IASW_Location* GetLocation( int iIndex ) = 0; +}; + +enum ASW_Reward_Type +{ + ASW_REWARD_MONEY=0, + ASW_REWARD_XP, + ASW_REWARD_ITEM, + ASW_REWARD_SKILL_SLOT, +}; + +class IASW_Reward +{ +public: + virtual ASW_Reward_Type GetRewardType() = 0; + virtual int GetRewardAmount() = 0; + + // for item rewards + virtual const char* GetRewardName() = 0; + virtual int GetRewardLevel() = 0; + virtual int GetRewardQuality() = 0; +}; + +class IASW_Location +{ +public: + virtual int GetID() = 0; + virtual IASW_Location_Group* GetGroup() = 0; + virtual int GetDifficulty() = 0; + virtual bool GetCompleted() = 0; + virtual int GetXPos() = 0; + virtual int GetYPos() = 0; + // Mission Settings are settings not directly related to the level layout, but which have other effects on the mission. + // (This is the "mission_settings" sub-node of the mission definition). + virtual KeyValues* GetMissionSettings() = 0; + // Mission Definition is the key-values block fed to the layout system. + virtual KeyValues* GetMissionDefinition() = 0; + + virtual const char* GetMapName() = 0; + virtual const char* GetStoryScene() = 0; + virtual const char* GetImageName() = 0; + virtual const char* GetCompanyName() = 0; + virtual const char* GetCompanyImage() = 0; + virtual int GetNumRewards() = 0; + virtual IASW_Reward* GetReward( int iRewardIndex ) = 0; + virtual int GetMoneyReward() = 0; + virtual int GetXPReward() = 0; + virtual int IsMissionOptional() = 0; + virtual bool IsLocationLocked( CUtlVector &completedMissions ) = 0; + virtual void SetPos( int x, int y ) = 0; +}; + +class IASW_Location_Grid +{ +public: + virtual int GetNumGroups() = 0; + virtual IASW_Location_Group* GetGroup( int iIndex ) = 0; + virtual IASW_Location_Group* GetGroupByName( const char *szName ) = 0; + virtual IASW_Location* GetLocationByID( int iLocationID ) = 0; + + virtual void SetLocationComplete( int iLocationID ) = 0; +}; + +class IASW_Mission_Text_Database +{ +public: + virtual const char *GetShortDescriptionByID( unsigned int id ) = 0; + virtual const char *GetLongDescriptionByID( unsigned int id ) = 0; + + /// a unique identifier for each mission spec. + /// if they're loaded from the same files in the same + /// order by the database on both server and client, + /// we can refer to specs by this id across the network. + // if you change the size of this, also update the m_nObjectiveTextIdx + // datatable field in asw_objective.cpp. + typedef uint16 ID_t; + enum { INVALID_INDEX = ((ID_t)(~0)) }; + static inline bool IsIDValid( ID_t idx ) { return idx != INVALID_INDEX; } + + /// find the mission text id for a given entity name, mission filename, other criteria. + /// you can resolve the ID to textual descriptions with GetShortDescriptionByID() etc. + virtual ID_t FindMissionTextID( const char *pEntityName, const char *pMissionName ) = 0; + +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface exposed from the mission chooser .dll (to the game dlls and tilegen.exe) +//----------------------------------------------------------------------------- +abstract_class IASW_Mission_Chooser : public IAppSystem +{ +public: + virtual bool GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second) = 0; + + virtual IASW_Random_Missions *RandomMissions() = 0; + virtual IASW_Mission_Chooser_Source *LocalMissionSource() = 0; + virtual IASW_Location_Grid *LocationGrid() = 0; + virtual IASW_Mission_Text_Database *MissionTextDatabase() = 0; + virtual IASW_Map_Builder *MapBuilder() = 0; + virtual IASWSpawnSelection *SpawnSelection() = 0; +}; + +#define ASW_MISSION_CHOOSER_VERSION "VASWMissionChooser001" + +#endif // MISSION_CHOOSER_INT_H diff --git a/public/missionchooser/iasw_mission_chooser_source.h b/public/missionchooser/iasw_mission_chooser_source.h new file mode 100644 index 0000000..60f8a78 --- /dev/null +++ b/public/missionchooser/iasw_mission_chooser_source.h @@ -0,0 +1,78 @@ +#ifndef _INCLUDED_IASW_MISSION_CHOOSER_SOURCE_H +#define _INCLUDED_IASW_MISSION_CHOOSER_SOURCE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +class KeyValues; + +struct ASW_Mission_Chooser_Mission +{ + char m_szMissionName[64]; +}; + +struct ASW_Mission_Chooser_Saved_Campaign +{ + char m_szSaveName[64]; + char m_szCampaignName[64]; + char m_szDateTime[64]; + int m_iMissionsComplete; + char m_szPlayerNames[256]; + char m_szPlayerIDs[512]; + bool m_bMultiplayer; +}; + +#define ASW_MISSIONS_PER_PAGE 8 +#define ASW_CAMPAIGNS_PER_PAGE 3 +#define ASW_SAVES_PER_PAGE 8 + +abstract_class IASW_Mission_Chooser_Source +{ +public: + virtual void Think() = 0; + virtual void IdleThink() = 0; + virtual void FindMissionsInCampaign( int iCampaignIndex, int nMissionOffset, int iNumSlots ) = 0; + virtual int GetNumMissionsInCampaign( int iCampaignIndex ) = 0; + + virtual void FindMissions(int nMissionOffset, int iNumSlots, bool bRequireOverview) = 0; + virtual ASW_Mission_Chooser_Mission* GetMissions() = 0; // pass an array of mission names back + virtual ASW_Mission_Chooser_Mission* GetMission( int nIndex, bool bRequireOverview ) = 0; // may return NULL if asking for a mission outside of the found range + virtual int GetNumMissions(bool bRequireOverview) = 0; + + virtual void FindCampaigns(int nCampaignOffset, int iNumSlots) = 0; + virtual ASW_Mission_Chooser_Mission* GetCampaigns() = 0; // Passes an array of campaign names back + virtual ASW_Mission_Chooser_Mission* GetCampaign( int nIndex ) = 0; // may return NULL when requesting a campaign outside the found range + virtual int GetNumCampaigns() = 0; + + virtual void FindSavedCampaigns(int page, int iNumSlots, bool bMultiplayer, const char *szFilterID) = 0; + virtual ASW_Mission_Chooser_Saved_Campaign* GetSavedCampaigns() = 0; // passes an array of summary data for each save + virtual ASW_Mission_Chooser_Saved_Campaign* GetSavedCampaign( int nIndex, bool bMultiplayer, const char *szFilterID ) = 0; // may return NULL when requesting a save outside the found range + virtual int GetNumSavedCampaigns(bool bMultiplayer, const char *szFilterID) = 0; + virtual void RefreshSavedCampaigns() = 0; // call when the saved campaigns list is dirty and should be refreshed + virtual int GetNumMissionsCompleted(const char *szSaveName) = 0; + virtual void OnSaveDeleted(const char *szSaveName) = 0; // call when a particular save has been deleted + virtual void OnSaveUpdated(const char *szSaveName) = 0; // call when a particular save has been updated + + // following only supporter by the local mission source + virtual bool MissionExists(const char *szMapName, bool bRequireOverview) = 0; + virtual bool CampaignExists(const char *szCampaignName) = 0; + virtual bool SavedCampaignExists(const char *szSaveName) = 0; + virtual bool ASW_Campaign_CreateNewSaveGame(char *szFileName, int iFileNameMaxLen, const char *szCampaignName, bool bMultiplayerGame, const char *szStartingMission) = 0; + virtual void NotifySaveDeleted(const char *szSaveName) = 0; + virtual const char* GetCampaignSaveIntroMap(const char* szSaveName) = 0; // returns the intro map for the campaign that this save uses + + // returns nice version of the filenames (i.e. title from the overview.txt or from the campaign txt) + virtual const char* GetPrettyMissionName(const char *szMapName) = 0; + virtual const char* GetPrettyCampaignName(const char *szCampaignName) = 0; + virtual const char* GetPrettySavedCampaignName(const char *szSaveName) = 0; + + // needed by network source + virtual void ResetCurrentPage() = 0; + + virtual KeyValues *GetMissionDetails( const char *szMissionName ) = 0; + virtual KeyValues *GetCampaignDetails( const char *szCampaignName ) = 0; +}; + +#endif // _INCLUDED_IASW_MISSION_CHOOSER_SOURCE_H diff --git a/public/missionchooser/iasw_random_missions.h b/public/missionchooser/iasw_random_missions.h new file mode 100644 index 0000000..936e6cc --- /dev/null +++ b/public/missionchooser/iasw_random_missions.h @@ -0,0 +1,94 @@ +#ifndef _INCLUDED_IASW_RANDOM_MISSIONS_H +#define _INCLUDED_IASW_RANDOM_MISSIONS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "iasw_mission_chooser.h" + +namespace vgui +{ + class Panel; +}; + +class Vector; +class KeyValues; +class IASW_Encounter; + +enum +{ + ASW_TILETYPE_UNKNOWN = 0, + ASW_TILETYPE_OUTDOOR1, + ASW_TILETYPE_OUTDOOR2, + ASW_TILETYPE_ARENA1, + ASW_TILETYPE_ARENA2, + ASW_TILETYPE_ARENA3, + ASW_TILETYPE_ROOM2, + ASW_TILETYPE_ROOM1, + ASW_TILETYPE_CORRIDOR1, + ASW_TILETYPE_CORRIDOR2, + ASW_TILETYPE_VENTS, + + ASW_TILETYPE_COUNT +}; + +static const char *g_szASWTileTypeStrings[ASW_TILETYPE_COUNT] = +{ + "Unknown", + "Outdoor1", + "Outdoor2", + "Arena1", + "Arena2", + "Arena3", + "Room1", + "Room2", + "Corridor1", + "Corridor2", + "Vents" +}; + +class IASW_Room_Details +{ +public: + // tags + virtual bool HasTag( const char *szTag ) = 0; + virtual int GetNumTags() = 0; + virtual const char* GetTag( int i ) = 0; + virtual int GetSpawnWeight() = 0; + virtual int GetNumExits() = 0; + virtual IASW_Room_Details* GetAdjacentRoom( int nExit ) = 0; + virtual bool GetThumbnailName( char* szOut, int iBufferSize ) = 0; + virtual bool GetFullRoomName( char* szOut, int iBufferSize ) = 0; + virtual void GetSoundscape( char* szOut, int iBufferSize ) = 0; + virtual void GetTheme( char* szOut, int iBufferSize ) = 0; + virtual const Vector& GetAmbientLight() = 0; + virtual bool HasAlienEncounter() = 0; + virtual int GetTileType() = 0; + virtual const char* GetTileTypeName( int nType ) = 0; + virtual int GetRoomIndex() const = 0; + + // location + virtual void GetWorldBounds( Vector *vecWorldMins, Vector *vecWorldMaxs ) = 0; + virtual const Vector& WorldSpaceCenter() = 0; +}; + +class IASW_Random_Missions +{ +public: + virtual vgui::Panel* CreateTileGenFrame( vgui::Panel *parent ) = 0; + + virtual void LevelInitPostEntity( const char *pszMapName ) = 0; + virtual bool ValidMapLayout() = 0; + virtual IASW_Room_Details* GetRoomDetails( const Vector &vecPos ) = 0; + virtual IASW_Room_Details* GetRoomDetails( int iRoomIndex ) = 0; + virtual IASW_Room_Details* GetStartRoomDetails() = 0; + virtual int GetNumRooms() = 0; + virtual void GetMapBounds( Vector *vecWorldMins, Vector *vecWorldMaxs ) = 0; + virtual KeyValues* GetGenerationOptions() = 0; // returns the generation options for the currently loaded random map + virtual int GetNumEncounters() = 0; + virtual IASW_Encounter* GetEncounter( int i ) = 0; + + virtual bool CheckAndCleanDirtyLayout( void ) = 0; +}; + +#endif // _INCLUDED_IASW_RANDOM_MISSIONS_H diff --git a/public/missionchooser/iasw_spawn_selection.h b/public/missionchooser/iasw_spawn_selection.h new file mode 100644 index 0000000..6f0336d --- /dev/null +++ b/public/missionchooser/iasw_spawn_selection.h @@ -0,0 +1,81 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +// +// +// +//=============================================================================== +#ifndef IASW_SPAWN_SELECTION_H +#define IASW_SPAWN_SELECTION_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/strtools.h" +#include "mathlib/vector.h" + +class IASWSpawnDefinitionEntry; + +// NOTE: Text version of these enum types is in asw_spawn_selection.cpp! +enum +{ + ASW_NPC_SPAWN_TYPE_INVALID = -1, + ASW_NPC_SPAWN_TYPE_ANY = 0, + ASW_NPC_SPAWN_TYPE_FIXED = 1, + ASW_NPC_SPAWN_TYPE_WANDERER, + ASW_NPC_SPAWN_TYPE_SWARM, + ASW_NPC_SPAWN_TYPE_BOSS, + ASW_NPC_SPAWN_TYPE_PROP, + ASW_NPC_SPAWN_TYPE_ARENAWAVE, + ASW_NPC_SPAWN_TYPE_CONSOLE, + ASW_NPC_SPAWN_TYPE_SPAWNER, + ASW_NPC_SPAWN_TYPE_BIFURCATE, + ASW_NPC_SPAWN_TYPE_SPECIAL1, + ASW_NPC_SPAWN_TYPE_SPECIAL2, + + // Has to be last. + ASW_NPC_SPAWN_TYPE_COUNT +}; + +class IASWSpawnDefinitionEntry +{ +public: + + virtual const char *GetNPCClassname() = 0; + virtual void GetSpawnCountRange( int &nMin, int &nMax ) = 0; + virtual float GetEliteNPCChance( void ) = 0; + virtual bool UseSpawners() = 0; +}; + + +class IASWSpawnDefinition +{ +public: + + virtual int GetEntryCount() = 0; + virtual IASWSpawnDefinitionEntry *GetEntry( int nEntry ) = 0; +}; + + +class IASWSpawnSelection +{ +public: + + virtual IASWSpawnDefinition *GetSpawnDefinition( int nSpawnType ) = 0; + virtual bool IsAvailableNPC( const char *szName ) = 0; + + virtual void SetCurrentSpawnSet( int iMissionDifficulty ) = 0; + virtual bool SetCurrentSpawnSet( const char *szSetName ) = 0; + + virtual void DumpCurrentSpawnSet() = 0; +}; + +// fixed spawn encounter in the mission +class IASW_Encounter +{ +public: + virtual const Vector& GetEncounterPosition() = 0; + virtual int GetNumSpawnDefs() = 0; + virtual IASWSpawnDefinition* GetSpawnDef( int i ) = 0; + virtual float GetEncounterRadius() = 0; +}; + +#endif // IASW_SPAWN_SELECTION_H diff --git a/public/model_types.h b/public/model_types.h new file mode 100644 index 0000000..b538d19 --- /dev/null +++ b/public/model_types.h @@ -0,0 +1,48 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// +#if !defined( MODEL_TYPES_H ) +#define MODEL_TYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#define STUDIO_NONE 0x00000000 +#define STUDIO_RENDER 0x00000001 +#define STUDIO_VIEWXFORMATTACHMENTS 0x00000002 +#define STUDIO_DRAWTRANSLUCENTSUBMODELS 0x00000004 +#define STUDIO_TWOPASS 0x00000008 +#define STUDIO_STATIC_LIGHTING 0x00000010 +#define STUDIO_WIREFRAME 0x00000020 +#define STUDIO_ITEM_BLINK 0x00000040 +#define STUDIO_NOSHADOWS 0x00000080 +#define STUDIO_WIREFRAME_VCOLLIDE 0x00000100 +#define STUDIO_NOLIGHTING_OR_CUBEMAP 0x00000200 +#define STUDIO_SKIP_FLEXES 0x00000400 +#define STUDIO_DONOTMODIFYSTENCILSTATE 0x00000800 // TERROR + +// Not a studio flag, but used to flag model as a non-sorting brush model +#define STUDIO_TRANSPARENCY 0x80000000 + +// Not a studio flag, but used to flag model as using shadow depth material override +#define STUDIO_SHADOWDEPTHTEXTURE 0x40000000 + +// Not a studio flag, but used to flag model as doing custom rendering into shadow texture +#define STUDIO_SHADOWTEXTURE 0x20000000 + +#define STUDIO_SKIP_DECALS 0x10000000 + +enum modtype_t +{ + mod_bad = 0, + mod_brush, + mod_sprite, + mod_studio +}; + +#endif // MODEL_TYPES_H diff --git a/public/modes.h b/public/modes.h new file mode 100644 index 0000000..bcbf1f7 --- /dev/null +++ b/public/modes.h @@ -0,0 +1,21 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( MODES_H ) +#define MODES_H +#ifdef _WIN32 +#pragma once +#endif + +typedef struct vmode_s +{ + int width; + int height; + int bpp; + int refreshRate; +} vmode_t; + +#endif // MODES_H diff --git a/public/mouthinfo.h b/public/mouthinfo.h new file mode 100644 index 0000000..243c922 --- /dev/null +++ b/public/mouthinfo.h @@ -0,0 +1,201 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( MOUTHINFO_H ) +#define MOUTHINFO_H +#ifdef _WIN32 +#pragma once +#endif + +class CAudioSource; + +#pragma pack(push,4) +class CVoiceData +{ +public: + CVoiceData( void ) + { + m_flElapsed = 0.0f; + m_pAudioSource = NULL; + m_bIgnorePhonemes = false; + } + + void SetElapsedTime( float t ) + { + m_flElapsed = t; + } + + float GetElapsedTime() const + { + return m_flElapsed; + } + + void SetSource( CAudioSource *source, bool bIgnorePhonemes ) + { + m_pAudioSource = source; + m_bIgnorePhonemes = bIgnorePhonemes; + } + + bool ShouldIgnorePhonemes() const + { + return m_bIgnorePhonemes; + } + + CAudioSource *GetSource() + { + return m_pAudioSource; + } + +private: + float m_flElapsed; + CAudioSource *m_pAudioSource; + bool m_bIgnorePhonemes; +}; + +#define UNKNOWN_VOICE_SOURCE -1 + +//----------------------------------------------------------------------------- +// Purpose: Describes position of mouth for lip syncing +//----------------------------------------------------------------------------- +class CMouthInfo +{ +public: + // 0 = mouth closed, 255 = mouth agape + byte mouthopen; + // counter for running average + byte sndcount; + // running average + int sndavg; + +public: + CMouthInfo( void ) { m_nVoiceSources = 0; m_needsEnvelope = false; } + ~CMouthInfo( void ) { ClearVoiceSources(); } + + int GetNumVoiceSources( void ); + CVoiceData *GetVoiceSource( int number ); + + void ClearVoiceSources( void ); + int GetIndexForSource( CAudioSource *source ); + bool IsSourceReferenced( CAudioSource *source ); + + CVoiceData *AddSource( CAudioSource *source, bool bIgnorePhonemes ); + + void RemoveSource( CAudioSource *source ); + void RemoveSourceByIndex( int index ); + + bool IsActive( void ); + bool NeedsEnvelope() { return m_needsEnvelope != 0; } + void ActivateEnvelope() { m_needsEnvelope = true; } + +private: + enum + { + MAX_VOICE_DATA = 4 + }; + + short m_nVoiceSources; + short m_needsEnvelope; + CVoiceData m_VoiceSources[ MAX_VOICE_DATA ]; +}; +#pragma pack(pop) + + +inline bool CMouthInfo::IsActive( void ) +{ + return ( GetNumVoiceSources() > 0 ) ? true : false; +} + +inline int CMouthInfo::GetNumVoiceSources( void ) +{ + return m_nVoiceSources; +} + +inline CVoiceData *CMouthInfo::GetVoiceSource( int number ) +{ + if ( number < 0 || number >= m_nVoiceSources ) + return NULL; + + return &m_VoiceSources[ number ]; +} + +inline void CMouthInfo::ClearVoiceSources( void ) +{ + m_nVoiceSources = 0; +} + +inline int CMouthInfo::GetIndexForSource( CAudioSource *source ) +{ + for ( int i = 0; i < m_nVoiceSources; i++ ) + { + CVoiceData *v = &m_VoiceSources[ i ]; + if ( !v ) + continue; + + if ( v->GetSource() == source ) + return i; + } + + return UNKNOWN_VOICE_SOURCE; +} + +inline bool CMouthInfo::IsSourceReferenced( CAudioSource *source ) +{ + if ( GetIndexForSource( source ) != UNKNOWN_VOICE_SOURCE ) + return true; + + return false; +} + +inline void CMouthInfo::RemoveSource( CAudioSource *source ) +{ + int idx = GetIndexForSource( source ); + if ( idx == UNKNOWN_VOICE_SOURCE ) + return; + + RemoveSourceByIndex( idx ); +} + +inline void CMouthInfo::RemoveSourceByIndex( int index ) +{ + if ( index < 0 || index >= m_nVoiceSources ) + return; + + --m_nVoiceSources; + if ( m_nVoiceSources > 0 ) + { + m_VoiceSources[ index ] = m_VoiceSources[ m_nVoiceSources ]; + } +} + +inline CVoiceData *CMouthInfo::AddSource( CAudioSource *source, bool bIgnorePhonemes ) +{ + int idx = GetIndexForSource( source ); + if ( idx == UNKNOWN_VOICE_SOURCE ) + { + if ( m_nVoiceSources < MAX_VOICE_DATA ) + { + idx = m_nVoiceSources++; + } + else + { + // No room! + return NULL; + } + } + + CVoiceData *data = &m_VoiceSources[ idx ]; + data->SetSource( source, bIgnorePhonemes ); + data->SetElapsedTime( 0.0f ); + return data; +} + +#endif // MOUTHINFO_H \ No newline at end of file diff --git a/public/networkstringtabledefs.h b/public/networkstringtabledefs.h new file mode 100644 index 0000000..5fa3357 --- /dev/null +++ b/public/networkstringtabledefs.h @@ -0,0 +1,87 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef NETWORKSTRINGTABLEDEFS_H +#define NETWORKSTRINGTABLEDEFS_H +#ifdef _WIN32 +#pragma once +#endif + +typedef int TABLEID; + +#define INVALID_STRING_TABLE -1 +const unsigned short INVALID_STRING_INDEX = (unsigned short )-1; + +// table index is sent in log2(MAX_TABLES) bits +#define MAX_TABLES 32 // Table id is 4 bits + +#define INTERFACENAME_NETWORKSTRINGTABLESERVER "VEngineServerStringTable001" +#define INTERFACENAME_NETWORKSTRINGTABLECLIENT "VEngineClientStringTable001" + +class INetworkStringTable; + +typedef void (*pfnStringChanged)( void *object, INetworkStringTable *stringTable, int stringNumber, char const *newString, void const *newData ); + +//----------------------------------------------------------------------------- +// Purpose: Game .dll shared string table interfaces +//----------------------------------------------------------------------------- +class INetworkStringTable +{ +public: + + virtual ~INetworkStringTable( void ) {}; + + // Table Info + virtual const char *GetTableName( void ) const = 0; + virtual TABLEID GetTableId( void ) const = 0; + virtual int GetNumStrings( void ) const = 0; + virtual int GetMaxStrings( void ) const = 0; + virtual int GetEntryBits( void ) const = 0; + + // Networking + virtual void SetTick( int tick ) = 0; + virtual bool ChangedSinceTick( int tick ) const = 0; + + // Accessors (length -1 means don't change user data if string already exits) + virtual int AddString( bool bIsServer, const char *value, int length = -1, const void *userdata = 0 ) = 0; + + virtual const char *GetString( int stringNumber ) const = 0; + virtual void SetStringUserData( int stringNumber, int length, const void *userdata ) = 0; + virtual const void *GetStringUserData( int stringNumber, int *length ) const = 0; + virtual int FindStringIndex( char const *string ) = 0; // returns INVALID_STRING_INDEX if not found + + // Callbacks + virtual void SetStringChangedCallback( void *object, pfnStringChanged changeFunc ) = 0; +}; + +enum ENetworkStringtableFlags +{ + NSF_NONE = 0, + NSF_DICTIONARY_ENABLED = (1<<0), // Uses pre-calculated per map dictionaries to reduce bandwidth +}; + +class INetworkStringTableContainer +{ +public: + + virtual ~INetworkStringTableContainer( void ) {}; + + // table creation/destruction + virtual INetworkStringTable *CreateStringTable( const char *tableName, int maxentries, int userdatafixedsize = 0, int userdatanetworkbits = 0, int flags = NSF_NONE ) = 0; + virtual void RemoveAllTables( void ) = 0; + + // table infos + virtual INetworkStringTable *FindTable( const char *tableName ) const = 0; + virtual INetworkStringTable *GetTable( TABLEID stringTable ) const = 0; + virtual int GetNumTables( void ) const = 0; + + virtual void SetAllowClientSideAddString( INetworkStringTable *table, bool bAllowClientSideAddString ) = 0; + + virtual void CreateDictionary( char const *pchMapName ) = 0; +}; + +#endif // NETWORKSTRINGTABLEDEFS_H diff --git a/public/networkvar.cpp b/public/networkvar.cpp new file mode 100644 index 0000000..e96947c --- /dev/null +++ b/public/networkvar.cpp @@ -0,0 +1,15 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + + +bool g_bUseNetworkVars = true; + +#endif + + + diff --git a/public/networkvar.h b/public/networkvar.h new file mode 100644 index 0000000..5cd1645 --- /dev/null +++ b/public/networkvar.h @@ -0,0 +1,821 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef NETWORKVAR_H +#define NETWORKVAR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/dbg.h" +#include "convar.h" + +#if defined( CLIENT_DLL ) || defined( GAME_DLL ) + #include "basehandle.h" +#endif + + +#pragma warning( disable : 4284 ) // warning C4284: return type for 'CNetworkVarT::operator ->' is 'int *' (ie; not a UDT or reference to a UDT. Will produce errors if applied using infix notation) + +#define MyOffsetOf( type, var ) ( (int)&((type*)0)->var ) + +#ifdef _DEBUG + #undef new + extern bool g_bUseNetworkVars; + #define CHECK_USENETWORKVARS if(g_bUseNetworkVars) +#else + #define CHECK_USENETWORKVARS // don't check for g_bUseNetworkVars +#endif + + + +// network vars use memcmp when fields are set. To ensure proper behavior your +// object's memory should be initialized to zero. This happens for entities automatically +// use this for other classes. +class CMemZeroOnNew +{ +public: + void *operator new( size_t nSize ) + { + void *pMem = MemAlloc_Alloc( nSize ); + V_memset( pMem, 0, nSize ); + return pMem; + } + + void* operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) + { + void *pMem = MemAlloc_Alloc( nSize, pFileName, nLine ); + V_memset( pMem, 0, nSize ); + return pMem; + } + + void operator delete(void *pData) + { + if ( pData ) + { + g_pMemAlloc->Free(pData); + } + } + + void operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine ) + { + if ( pData ) + { + g_pMemAlloc->Free(pData, pFileName, nLine ); + } + } +}; + + +inline int InternalCheckDeclareClass( const char *pClassName, const char *pClassNameMatch, void *pTestPtr, void *pBasePtr ) +{ + // This makes sure that casting from ThisClass to BaseClass works right. You'll get a compiler error if it doesn't + // work at all, and you'll get a runtime error if you use multiple inheritance. + Assert( pTestPtr == pBasePtr ); + + // This is triggered by IMPLEMENT_SERVER_CLASS. It does DLLClassName::CheckDeclareClass( #DLLClassName ). + // If they didn't do a DECLARE_CLASS in DLLClassName, then it'll be calling its base class's version + // and the class names won't match. + Assert( (void*)pClassName == (void*)pClassNameMatch ); + return 0; +} + + +template +inline int CheckDeclareClass_Access( T *, const char *pShouldBe ) +{ + return T::CheckDeclareClass( pShouldBe ); +} + +#ifndef _STATIC_LINKED +#ifdef _MSC_VER +#if defined(_DEBUG) && (_MSC_VER > 1200 ) + #define VALIDATE_DECLARE_CLASS 1 +#endif +#endif +#endif + +#ifdef VALIDATE_DECLARE_CLASS + + #define DECLARE_CLASS( className, baseClassName ) \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; \ + template friend int CheckDeclareClass_Access(T *, const char *pShouldBe); \ + static int CheckDeclareClass( const char *pShouldBe ) \ + { \ + InternalCheckDeclareClass( pShouldBe, #className, (ThisClass*)0xFFFFF, (BaseClass*)(ThisClass*)0xFFFFF ); \ + return CheckDeclareClass_Access( (BaseClass *)NULL, #baseClassName ); \ + } + + // Use this macro when you have a base class, but it's part of a library that doesn't use network vars + // or any of the things that use ThisClass or BaseClass. + #define DECLARE_CLASS_GAMEROOT( className, baseClassName ) \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; \ + template friend int CheckDeclareClass_Access(T *, const char *pShouldBe); \ + static int CheckDeclareClass( const char *pShouldBe ) \ + { \ + return InternalCheckDeclareClass( pShouldBe, #className, (ThisClass*)0xFFFFF, (BaseClass*)(ThisClass*)0xFFFFF ); \ + } + + // Deprecated macro formerly used to work around VC++98 bug + #define DECLARE_CLASS_NOFRIEND( className, baseClassName ) \ + DECLARE_CLASS( className, baseClassName ) + + #define DECLARE_CLASS_NOBASE( className ) \ + typedef className ThisClass; \ + template friend int CheckDeclareClass_Access(T *, const char *pShouldBe); \ + static int CheckDeclareClass( const char *pShouldBe ) \ + { \ + return InternalCheckDeclareClass( pShouldBe, #className, 0, 0 ); \ + } + +#else + #define DECLARE_CLASS( className, baseClassName ) \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; + + #define DECLARE_CLASS_GAMEROOT( className, baseClassName ) DECLARE_CLASS( className, baseClassName ) + #define DECLARE_CLASS_NOFRIEND( className, baseClassName ) DECLARE_CLASS( className, baseClassName ) + + #define DECLARE_CLASS_NOBASE( className ) typedef className ThisClass; +#endif + + + + +// All classes that contain CNetworkVars need a NetworkStateChanged() function. If the class is not an entity, +// it needs to forward the call to the entity it's in. These macros can help. + + // These macros setup an entity pointer in your class. Use IMPLEMENT_NETWORKVAR_CHAIN before you do + // anything inside the class itself. + class CBaseEntity; + class CAutoInitEntPtr + { + public: + CAutoInitEntPtr() + { + m_pEnt = NULL; + } + CBaseEntity *m_pEnt; + }; + + //TODO: Currently, these don't get the benefit of tracking changes to individual vars. + // Would be nice if they did. + #define DECLARE_NETWORKVAR_CHAIN() \ + CAutoInitEntPtr __m_pChainEntity; \ + void NetworkStateChanged() { CHECK_USENETWORKVARS __m_pChainEntity.m_pEnt->NetworkStateChanged(); } \ + void NetworkStateChanged( void *pVar ) { CHECK_USENETWORKVARS __m_pChainEntity.m_pEnt->NetworkStateChanged(); } + + #define IMPLEMENT_NETWORKVAR_CHAIN( varName ) \ + (varName)->__m_pChainEntity.m_pEnt = this; + + + +// Use this macro when you want to embed a structure inside your entity and have CNetworkVars in it. +template< class T > +static inline void DispatchNetworkStateChanged( T *pObj ) +{ + CHECK_USENETWORKVARS pObj->NetworkStateChanged(); +} +template< class T > +static inline void DispatchNetworkStateChanged( T *pObj, void *pVar ) +{ + CHECK_USENETWORKVARS pObj->NetworkStateChanged( pVar ); +} + + +#define DECLARE_EMBEDDED_NETWORKVAR() \ + template friend int ServerClassInit(T *); \ + template friend int ClientClassInit(T *); \ + virtual void NetworkStateChanged() {} virtual void NetworkStateChanged( void *pProp ) {} + +// NOTE: Assignment operator is disabled because it doesn't call copy constructors of scalar types within the aggregate, so they are not marked changed +#define CNetworkVarEmbedded( type, name ) \ + class NetworkVar_##name; \ + friend class NetworkVar_##name; \ + static inline int GetOffset_##name() { return MyOffsetOf(ThisClass,name); } \ + typedef ThisClass ThisClass_##name; \ + class NetworkVar_##name : public type \ + { \ + template< class T > NetworkVar_##name& operator=( const T &val ) { *((type*)this) = val; return *this; } \ + public: \ + void CopyFrom( const type &src ) { *((type *)this) = src; NetworkStateChanged(); } \ + type & GetForModify( void ) { NetworkStateChanged(); return *((type *)this); } \ + virtual void NetworkStateChanged() \ + { \ + DispatchNetworkStateChanged( (ThisClass_##name*)( ((char*)this) - GetOffset_##name() ) ); \ + } \ + virtual void NetworkStateChanged( void *pVar ) \ + { \ + DispatchNetworkStateChanged( (ThisClass_##name*)( ((char*)this) - GetOffset_##name() ), pVar ); \ + } \ + }; \ + NetworkVar_##name name; + + + +template< class Type, class Changer > +class CNetworkVarBase +{ +public: + CNetworkVarBase() + { + } + + FORCEINLINE explicit CNetworkVarBase( Type val ) + : m_Value( val ) + { + NetworkStateChanged(); + } + + FORCEINLINE const Type& SetDirect( const Type &val ) + { + NetworkStateChanged(); + m_Value = val; + return m_Value; + } + + FORCEINLINE const Type& Set( const Type &val ) + { + if ( m_Value != val ) + { + NetworkStateChanged(); + m_Value = val; + } + return m_Value; + } + + template< class C > + FORCEINLINE const Type& operator=( const C &val ) + { + return Set( ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator=( const CNetworkVarBase< C, Changer > &val ) + { + return Set( ( const Type )val.m_Value ); + } + + FORCEINLINE Type& GetForModify() + { + NetworkStateChanged(); + return m_Value; + } + + template< class C > + FORCEINLINE const Type& operator+=( const C &val ) + { + return Set( m_Value + ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator-=( const C &val ) + { + return Set( m_Value - ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator/=( const C &val ) + { + return Set( m_Value / ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator*=( const C &val ) + { + return Set( m_Value * ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator^=( const C &val ) + { + return Set( m_Value ^ ( const Type )val ); + } + + template< class C > + FORCEINLINE const Type& operator|=( const C &val ) + { + return Set( m_Value | ( const Type )val ); + } + + FORCEINLINE const Type& operator++() + { + return (*this += 1); + } + + FORCEINLINE Type operator--() + { + return (*this -= 1); + } + + FORCEINLINE Type operator++( int ) // postfix version.. + { + Type val = m_Value; + (*this += 1); + return val; + } + + FORCEINLINE Type operator--( int ) // postfix version.. + { + Type val = m_Value; + (*this -= 1); + return val; + } + + // For some reason the compiler only generates type conversion warnings for this operator when used like + // CNetworkVarBase = 0x1 + // (it warns about converting from an int to an unsigned char). + template< class C > + FORCEINLINE const Type& operator&=( const C &val ) + { + return Set( m_Value & ( const Type )val ); + } + + FORCEINLINE operator const Type&() const + { + return m_Value; + } + + FORCEINLINE const Type& Get() const + { + return m_Value; + } + + FORCEINLINE const Type* operator->() const + { + return &m_Value; + } + + Type m_Value; + +protected: + FORCEINLINE void NetworkStateChanged() + { + Changer::NetworkStateChanged( this ); + } +}; + +template< class Type, class Changer > +class CNetworkColor32Base : public CNetworkVarBase< Type, Changer > +{ + typedef CNetworkVarBase< Type, Changer > base; +public: + inline void Init( byte rVal, byte gVal, byte bVal ) + { + SetR( rVal ); + SetG( gVal ); + SetB( bVal ); + } + inline void Init( byte rVal, byte gVal, byte bVal, byte aVal ) + { + SetR( rVal ); + SetG( gVal ); + SetB( bVal ); + SetA( aVal ); + } + + const Type& operator=( const Type &val ) + { + return Set( val ); + } + + const Type& operator=( const CNetworkColor32Base &val ) + { + return base::Set( val.m_Value ); + } + + inline byte GetR() const { return this->m_Value.r; } + inline byte GetG() const { return this->m_Value.g; } + inline byte GetB() const { return this->m_Value.b; } + inline byte GetA() const { return this->m_Value.a; } + inline void SetR( byte val ) { SetVal( this->m_Value.r, val ); } + inline void SetG( byte val ) { SetVal( this->m_Value.g, val ); } + inline void SetB( byte val ) { SetVal( this->m_Value.b, val ); } + inline void SetA( byte val ) { SetVal( this->m_Value.a, val ); } + +protected: + inline void SetVal( byte &out, const byte &in ) + { + if ( out != in ) + { + this->NetworkStateChanged(); + out = in; + } + } +}; + + +// Network vector wrapper. +template< class Type, class Changer > +class CNetworkVectorBase : public CNetworkVarBase< Type, Changer > +{ + typedef CNetworkVarBase< Type, Changer > base; +public: + FORCEINLINE void Init( float ix=0, float iy=0, float iz=0 ) + { + base::Set( Type( ix, iy, iz ) ); + } + + FORCEINLINE const Type& operator=( const Type &val ) + { + return base::Set( val ); + } + + FORCEINLINE const Type& operator=( const CNetworkVectorBase &val ) + { + return base::Set( val.m_Value ); + } + + FORCEINLINE float GetX() const { return this->m_Value.x; } + FORCEINLINE float GetY() const { return this->m_Value.y; } + FORCEINLINE float GetZ() const { return this->m_Value.z; } + FORCEINLINE float operator[]( int i ) const { return this->m_Value[i]; } + + FORCEINLINE void SetX( float val ) { DetectChange( this->m_Value.x, val ); } + FORCEINLINE void SetY( float val ) { DetectChange( this->m_Value.y, val ); } + FORCEINLINE void SetZ( float val ) { DetectChange( this->m_Value.z, val ); } + FORCEINLINE void Set( int i, float val ) { DetectChange( this->m_Value[i], val ); } + + FORCEINLINE bool operator==( const Type &val ) const + { + return this->m_Value == (Type)val; + } + + FORCEINLINE bool operator!=( const Type &val ) const + { + return this->m_Value != (Type)val; + } + + FORCEINLINE const Type operator+( const Type &val ) const + { + return this->m_Value + val; + } + + FORCEINLINE const Type operator-( const Type &val ) const + { + return this->m_Value - val; + } + + FORCEINLINE const Type operator*( const Type &val ) const + { + return this->m_Value * val; + } + + FORCEINLINE const Type& operator*=( float val ) + { + return base::Set( this->m_Value * val ); + } + + FORCEINLINE const Type operator*( float val ) const + { + return this->m_Value * val; + } + + FORCEINLINE const Type operator/( const Type &val ) const + { + return this->m_Value / val; + } + +private: + FORCEINLINE void DetectChange( float &out, float in ) + { + if ( out != in ) + { + this->NetworkStateChanged(); + out = in; + } + } +}; + + +// Network vector wrapper. +template< class Type, class Changer > +class CNetworkQuaternionBase : public CNetworkVarBase< Type, Changer > +{ + typedef CNetworkVarBase< Type, Changer > base; +public: + inline void Init( float ix=0, float iy=0, float iz=0, float iw = 0 ) + { + base::Set( Quaternion( ix, iy, iz, iw ) ); + } + + const Type& operator=( const Type &val ) + { + return Set( val ); + } + + const Type& operator=( const CNetworkQuaternionBase &val ) + { + return Set( val.m_Value ); + } + + inline float GetX() const { return this->m_Value.x; } + inline float GetY() const { return this->m_Value.y; } + inline float GetZ() const { return this->m_Value.z; } + inline float GetW() const { return this->m_Value.w; } + inline float operator[]( int i ) const { return this->m_Value[i]; } + + inline void SetX( float val ) { DetectChange( this->m_Value.x, val ); } + inline void SetY( float val ) { DetectChange( this->m_Value.y, val ); } + inline void SetZ( float val ) { DetectChange( this->m_Value.z, val ); } + inline void SetW( float val ) { DetectChange( this->m_Value.w, val ); } + inline void Set( int i, float val ) { DetectChange( this->m_Value[i], val ); } + + bool operator==( const Type &val ) const + { + return this->m_Value == (Type)val; + } + + bool operator!=( const Type &val ) const + { + return this->m_Value != (Type)val; + } + + const Type operator+( const Type &val ) const + { + return this->m_Value + val; + } + + const Type operator-( const Type &val ) const + { + return this->m_Value - val; + } + + const Type operator*( const Type &val ) const + { + return this->m_Value * val; + } + + const Type& operator*=( float val ) + { + return Set( this->m_Value * val ); + } + + const Type operator*( float val ) const + { + return this->m_Value * val; + } + + const Type operator/( const Type &val ) const + { + return this->m_Value / val; + } + +private: + inline void DetectChange( float &out, float in ) + { + if ( out != in ) + { + this->NetworkStateChanged(); + out = in; + } + } +}; + + +// Network ehandle wrapper. +#if defined( CLIENT_DLL ) || defined( GAME_DLL ) + template< class Type, class Changer > + class CNetworkHandleBase : public CNetworkVarBase< CBaseHandle, Changer > + { + typedef CNetworkVarBase< CBaseHandle, Changer > base; + public: + const Type* operator=( const Type *val ) + { + return Set( val ); + } + + const Type& operator=( const CNetworkHandleBase &val ) + { + const CBaseHandle &handle = CNetworkVarBase::Set( val.m_Value ); + return *(const Type*)handle.Get(); + } + + bool operator !() const + { + return !this->m_Value.Get(); + } + + operator Type*() const + { + return static_cast< Type* >( this->m_Value.Get() ); + } + + const Type* Set( const Type *val ) + { + if ( CNetworkHandleBase::m_Value != val ) + { + this->NetworkStateChanged(); + CNetworkHandleBase::m_Value = val; + } + return val; + } + + Type* Get() const + { + return static_cast< Type* >( CNetworkHandleBase::m_Value.Get() ); + } + + Type* operator->() const + { + return static_cast< Type* >( CNetworkHandleBase::m_Value.Get() ); + } + + bool operator==( const Type *val ) const + { + return CNetworkHandleBase::m_Value == val; + } + + bool operator!=( const Type *val ) const + { + return CNetworkHandleBase::m_Value != val; + } + }; + + + + #define CNetworkHandle( type, name ) CNetworkHandleInternal( type, name, NetworkStateChanged ) + + #define CNetworkHandleInternal( type, name, stateChangedFn ) \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, CNetworkHandleBase, stateChangedFn ) +#endif + + +// Use this macro to define a network variable. +#define CNetworkVar( type, name ) \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged ) + + +// Use this macro when you have a base class with a variable, and it doesn't have that variable in a SendTable, +// but a derived class does. Then, the entity is only flagged as changed when the variable is changed in +// an entity that wants to transmit the variable. + #define CNetworkVarForDerived( type, name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged_##name ) + + #define CNetworkVectorForDerived( name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + CNetworkVectorInternal( Vector, name, NetworkStateChanged_##name ) + + #define CNetworkHandleForDerived( type, name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + CNetworkHandleInternal( type, name, NetworkStateChanged_##name ) + + #define CNetworkArrayForDerived( type, name, count ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} \ + CNetworkArrayInternal( type, name, count, NetworkStateChanged_##name ) + + #define IMPLEMENT_NETWORK_VAR_FOR_DERIVED( name ) \ + virtual void NetworkStateChanged_##name() { CHECK_USENETWORKVARS NetworkStateChanged(); } \ + virtual void NetworkStateChanged_##name( void *pVar ) { CHECK_USENETWORKVARS NetworkStateChanged( pVar ); } + + +// This virtualizes the change detection on the variable, but it is ON by default. +// Use this when you have a base class in which MOST of its derived classes use this variable +// in their SendTables, but there are a couple that don't (and they +// can use DISABLE_NETWORK_VAR_FOR_DERIVED). + #define CNetworkVarForDerived_OnByDefault( type, name ) \ + virtual void NetworkStateChanged_##name() { CHECK_USENETWORKVARS NetworkStateChanged(); } \ + virtual void NetworkStateChanged_##name( void *pVar ) { CHECK_USENETWORKVARS NetworkStateChanged( pVar ); } \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, CNetworkVarBase, NetworkStateChanged_##name ) + + #define DISABLE_NETWORK_VAR_FOR_DERIVED( name ) \ + virtual void NetworkStateChanged_##name() {} \ + virtual void NetworkStateChanged_##name( void *pVar ) {} + + + +// Vectors + some convenient helper functions. +#define CNetworkVector( name ) CNetworkVectorInternal( Vector, name, NetworkStateChanged ) +#define CNetworkQAngle( name ) CNetworkVectorInternal( QAngle, name, NetworkStateChanged ) + +#define CNetworkVectorInternal( type, name, stateChangedFn ) \ + NETWORK_VAR_START( type, name ) \ + NETWORK_VAR_END( type, name, CNetworkVectorBase, stateChangedFn ) + +#define CNetworkQuaternion( name ) \ + NETWORK_VAR_START( Quaternion, name ) \ + NETWORK_VAR_END( Quaternion, name, CNetworkQuaternionBase, NetworkStateChanged ) + +// Helper for color32's. Contains GetR(), SetR(), etc.. functions. +#define CNetworkColor32( name ) \ + NETWORK_VAR_START( color32, name ) \ + NETWORK_VAR_END( color32, name, CNetworkColor32Base, NetworkStateChanged ) + + +#define CNetworkString( name, length ) \ + class NetworkVar_##name; \ + friend class NetworkVar_##name; \ + typedef ThisClass MakeANetworkVar_##name; \ + class NetworkVar_##name \ + { \ + public: \ + operator const char*() const { return m_Value; } \ + const char* Get() const { return m_Value; } \ + char* GetForModify() \ + { \ + NetworkStateChanged(); \ + return m_Value; \ + } \ + protected: \ + inline void NetworkStateChanged() \ + { \ + CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->NetworkStateChanged(); \ + } \ + private: \ + char m_Value[length]; \ + }; \ + NetworkVar_##name name; + + + + +// Use this to define networked arrays. +// You can access elements for reading with operator[], and you can set elements with the Set() function. +#define CNetworkArrayInternal( type, name, count, stateChangedFn ) \ + class NetworkVar_##name; \ + friend class NetworkVar_##name; \ + typedef ThisClass MakeANetworkVar_##name; \ + class NetworkVar_##name \ + { \ + public: \ + template friend int ServerClassInit(T *); \ + const type& operator[]( int i ) const \ + { \ + return Get( i ); \ + } \ + \ + const type& Get( int i ) const \ + { \ + Assert( i >= 0 && i < count ); \ + return m_Value[i]; \ + } \ + \ + type& GetForModify( int i ) \ + { \ + Assert( i >= 0 && i < count ); \ + NetworkStateChanged( i ); \ + return m_Value[i]; \ + } \ + \ + void Set( int i, const type &val ) \ + { \ + Assert( i >= 0 && i < count ); \ + if( memcmp( &m_Value[i], &val, sizeof(type) ) ) \ + { \ + NetworkStateChanged( i ); \ + m_Value[i] = val; \ + } \ + } \ + const type* Base() const { return m_Value; } \ + int Count() const { return count; } \ + type m_Value[count]; \ + protected: \ + inline void NetworkStateChanged( int index ) \ + { \ + CHECK_USENETWORKVARS ((ThisClass*)(((char*)this) - MyOffsetOf(ThisClass,name)))->stateChangedFn( &m_Value[index] ); \ + } \ + }; \ + NetworkVar_##name name; + + +#define CNetworkArray( type, name, count ) CNetworkArrayInternal( type, name, count, NetworkStateChanged ) + + +// Internal macros used in definitions of network vars. +#define NETWORK_VAR_START( type, name ) \ + class NetworkVar_##name; \ + friend class NetworkVar_##name; \ + typedef ThisClass MakeANetworkVar_##name; \ + class NetworkVar_##name \ + { \ + public: \ + template friend int ServerClassInit(T *); + + +#define NETWORK_VAR_END( type, name, base, stateChangedFn ) \ + public: \ + static inline void NetworkStateChanged( void *ptr ) \ + { \ + CHECK_USENETWORKVARS ((ThisClass*)(((char*)ptr) - MyOffsetOf(ThisClass,name)))->stateChangedFn( ptr ); \ + } \ + }; \ + base< type, NetworkVar_##name > name; + + + +#endif // NETWORKVAR_H diff --git a/public/nmatrix.h b/public/nmatrix.h new file mode 100644 index 0000000..2fc8e4c --- /dev/null +++ b/public/nmatrix.h @@ -0,0 +1,332 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef NMATRIX_H +#define NMATRIX_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "nvector.h" + + +#define NMatrixMN NMatrix + + +template +class NMatrix +{ +public: + + NMatrixMN() {} + + static NMatrixMN SetupNMatrixNull(); // Return a matrix of all zeros. + static NMatrixMN SetupNMatrixIdentity(); // Return an identity matrix. + + NMatrixMN const& operator=( NMatrixMN const &other ); + + NMatrixMN operator+( NMatrixMN const &v ) const; + NMatrixMN const& operator+=( NMatrixMN const &v ); + + NMatrixMN operator-() const; + NMatrixMN operator-( NMatrixMN const &v ) const; + + // Multiplies the column vector on the right-hand side. + NVector operator*( NVector const &v ) const; + + // Can't get the compiler to work with a real MxN * NxR matrix multiply... + NMatrix operator*( NMatrix const &b ) const; + + NMatrixMN operator*( float val ) const; + + bool InverseGeneral( NMatrixMN &mInverse ) const; + NMatrix Transpose() const; + + +public: + + float m[M][N]; +}; + + + +// Return the matrix generated by multiplying a column vector 'a' by row vector 'b'. +template +inline NMatrix OuterProduct( NVectorN const &a, NVectorN const &b ) +{ + NMatrix ret; + + for( int i=0; i < N; i++ ) + for( int j=0; j < N; j++ ) + ret.m[i][j] = a.v[i] * b.v[j]; + + return ret; +} + + +// -------------------------------------------------------------------------------- // +// NMatrix inlines. +// -------------------------------------------------------------------------------- // + +template +inline NMatrixMN NMatrixMN::SetupNMatrixNull() +{ + NMatrix ret; + memset( ret.m, 0, sizeof(float)*M*N ); + return ret; +} + + +template +inline NMatrixMN NMatrixMN::SetupNMatrixIdentity() +{ + assert( M == N ); // Identity matrices must be square. + + NMatrix ret; + memset( ret.m, 0, sizeof(float)*M*N ); + for( int i=0; i < N; i++ ) + ret.m[i][i] = 1; + return ret; +} + + +template +inline NMatrixMN const &NMatrixMN::operator=( NMatrixMN const &v ) +{ + memcpy( m, v.m, sizeof(float)*M*N ); + return *this; +} + + +template +inline NMatrixMN NMatrixMN::operator+( NMatrixMN const &v ) const +{ + NMatrixMN ret; + for( int i=0; i < M; i++ ) + for( int j=0; j < N; j++ ) + ret.m[i][j] = m[i][j] + v.m[i][j]; + + return ret; +} + + +template +inline NMatrixMN const &NMatrixMN::operator+=( NMatrixMN const &v ) +{ + for( int i=0; i < M; i++ ) + for( int j=0; j < N; j++ ) + m[i][j] += v.m[i][j]; + + return *this; +} + + +template +inline NMatrixMN NMatrixMN::operator-() const +{ + NMatrixMN ret; + + for( int i=0; i < M*N; i++ ) + ((float*)ret.m)[i] = -((float*)m)[i]; + + return ret; +} + + +template +inline NMatrixMN NMatrixMN::operator-( NMatrixMN const &v ) const +{ + NMatrixMN ret; + for( int i=0; i < M; i++ ) + for( int j=0; j < N; j++ ) + ret.m[i][j] = m[i][j] - v.m[i][j]; + return ret; +} + + +template +inline NVector NMatrixMN::operator*( NVectorN const &v ) const +{ + NVectorN ret; + + for( int i=0; i < M; i++ ) + { + ret.v[i] = 0; + + for( int j=0; j < N; j++ ) + ret.v[i] += m[i][j] * v.v[j]; + } + + return ret; +} + + +template +inline NMatrix NMatrixMN::operator*( NMatrix const &b ) const +{ + NMatrix ret; + + for( int myRow=0; myRow < M; myRow++ ) + { + for( int otherCol=0; otherCol < M; otherCol++ ) + { + ret[myRow][otherCol] = 0; + for( int i=0; i < N; i++ ) + ret[myRow][otherCol] += a.m[myRow][i] * b.m[i][otherCol]; + } + } + + return ret; +} + + +template +inline NMatrixMN NMatrixMN::operator*( float val ) const +{ + NMatrixMN ret; + + for( int i=0; i < N*M; i++ ) + ((float*)ret.m)[i] = ((float*)m)[i] * val; + + return ret; +} + + +template +bool NMatrixMN::InverseGeneral( NMatrixMN &mInverse ) const +{ + int iRow, i, j, iTemp, iTest; + float mul, fTest, fLargest; + float mat[N][2*N]; + int rowMap[N], iLargest; + float *pOut, *pRow, *pScaleRow; + + + // Can only invert square matrices. + if( M != N ) + { + assert( !"Tried to invert a non-square matrix" ); + return false; + } + + + // How it's done. + // AX = I + // A = this + // X = the matrix we're looking for + // I = identity + + // Setup AI + for(i=0; i < N; i++) + { + const float *pIn = m[i]; + pOut = mat[i]; + + for(j=0; j < N; j++) + { + pOut[j] = pIn[j]; + } + + for(j=N; j < 2*N; j++) + pOut[j] = 0; + + pOut[i+N] = 1.0f; + + rowMap[i] = i; + } + + // Use row operations to get to reduced row-echelon form using these rules: + // 1. Multiply or divide a row by a nonzero number. + // 2. Add a multiple of one row to another. + // 3. Interchange two rows. + + for(iRow=0; iRow < N; iRow++) + { + // Find the row with the largest element in this column. + fLargest = 1e-6f; + iLargest = -1; + for(iTest=iRow; iTest < N; iTest++) + { + fTest = (float)fabs(mat[rowMap[iTest]][iRow]); + if(fTest > fLargest) + { + iLargest = iTest; + fLargest = fTest; + } + } + + // They're all too small.. sorry. + if(iLargest == -1) + { + return false; + } + + // Swap the rows. + iTemp = rowMap[iLargest]; + rowMap[iLargest] = rowMap[iRow]; + rowMap[iRow] = iTemp; + + pRow = mat[rowMap[iRow]]; + + // Divide this row by the element. + mul = 1.0f / pRow[iRow]; + for(j=0; j < 2*N; j++) + pRow[j] *= mul; + + pRow[iRow] = 1.0f; // Preserve accuracy... + + // Eliminate this element from the other rows using operation 2. + for(i=0; i < N; i++) + { + if(i == iRow) + continue; + + pScaleRow = mat[rowMap[i]]; + + // Multiply this row by -(iRow*the element). + mul = -pScaleRow[iRow]; + for(j=0; j < 2*N; j++) + { + pScaleRow[j] += pRow[j] * mul; + } + + pScaleRow[iRow] = 0.0f; // Preserve accuracy... + } + } + + // The inverse is on the right side of AX now (the identity is on the left). + for(i=0; i < N; i++) + { + const float *pIn = mat[rowMap[i]] + N; + pOut = mInverse.m[i]; + + for(j=0; j < N; j++) + { + pOut[j] = pIn[j]; + } + } + + return true; +} + + +template +inline NMatrix NMatrixMN::Transpose() const +{ + NMatrix ret; + + for( int i=0; i < M; i++ ) + for( int j=0; j < N; j++ ) + ret.m[j][i] = m[i][j]; + + return ret; +} + +#endif // NMATRIX_H + diff --git a/public/ntree.h b/public/ntree.h new file mode 100644 index 0000000..ef5b725 --- /dev/null +++ b/public/ntree.h @@ -0,0 +1,316 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef __TREE_H__ +#define __TREE_H__ + +#include "List.h" +#include "ArrayStack.h" + +// NTreeNode: Class decleration and definition +template class NTreeNode +{ +public: + // constructor + NTreeNode( T data ); + + NTreeNode *PrependChild( NTreeNode *node ); + NTreeNode *AppendChild( NTreeNode *node ); + NTreeNode *InsertChildAfterIndex( NTreeNode *node, int index ); + NTreeNode *InsertChildBeforeIndex( NTreeNode *node, int index ); + NTreeNode *RemoveChild( Position position ); + NTreeNode *RemoveChild( int index ); + Position InsertAfter( NTreeNode *node, Position position ); + Position InsertBefore( NTreeNode *node, Position position ); + int GetNumChildren(); + Position GetChildPosition( int childNum ); + NTreeNode *GetChild( int childNum ); + NTreeNode *GetChild( Position position ); + int GetIndexRelativeToParent(); + T GetItem(); + NTreeNode *GetParent(); + NTreeNode *GetRoot(); + NTreeNode *GetNextSibling(); + void Traverse( void (*VisitFunc)( T, int depth ), int maxTreeDepth ); + NTreeNode *ReentrantTraversalGetFirst( int maxTreeDepth ); + NTreeNode *ReentrantTraversalGetNext( void ); + +protected: + GList * > *list; + T data; + NTreeNode *parent; + ArrayStack *> *reentrantStack; +}; + +template +NTreeNode::NTreeNode( T data ) +{ + list = new GList * >; + this->data = data; + this->parent = NULL; + this->reentrantStack = NULL; +} + +template +NTreeNode *NTreeNode::PrependChild( NTreeNode *node ) +{ + node->parent = this; + return list->GetItemAtPosition( list->InsertAtHead( node ) ); +} + +template +NTreeNode *NTreeNode::AppendChild( NTreeNode *node ) +{ + node->parent = this; + return list->GetItemAtPosition( list->InsertAtTail( node ) ); +} + +template +NTreeNode *NTreeNode::InsertChildAfterIndex( NTreeNode *node, int index ) +{ + node->parent = this; + if( index < 0 ) + { + // if out of range in the negative direction, prepend + this->PrependChild( node ); + } + else if( index > list->GetNumItems() - 1 ) + { + // if out of range, just append. + this->AppendChild( node ); + } + else + { + Position pos; + pos = list->GetPositionAtIndex( index ); + list->InsertAfter( node, pos ); + } + return node; +} + +template +NTreeNode *NTreeNode::InsertChildBeforeIndex( NTreeNode *node, int index ) +{ + node->parent = this; + if( index < 0 ) + { + // if out of range in the negative direction, prepend + this->PrependChild( node ); + } + else if( index > list->GetNumItems() - 1 ) + { + // if out of range, just append. + this->AppendChild( node ); + } + else + { + Position pos; + pos = list->GetPositionAtIndex( index ); + list->InsertBefore( node, pos ); + } + return node; +} + +template +NTreeNode *NTreeNode::RemoveChild( Position position ) +{ + NTreeNode **node = ( NTreeNode ** )( void * )position; + ( *node )->parent = NULL; + return list->Remove( position ); +} + +template +NTreeNode *NTreeNode::RemoveChild( int index ) +{ + Position position = list->GetPositionAtIndex( index ); + NTreeNode **node = ( NTreeNode ** )( void * )position; + ( *node )->parent = NULL; + return list->Remove( position ); +} + +template +Position NTreeNode::InsertAfter( NTreeNode *node, Position position ) +{ + node->parent = this; + return list->InsertAfter( node, position ); +} + +template +Position NTreeNode::InsertBefore( NTreeNode *node, Position position ) +{ + node->parent = this; + return list->InsertBefore( node, position ); +} + +template +int NTreeNode::GetNumChildren() +{ + return list->GetNumItems(); +} + +template +Position NTreeNode::GetChildPosition( int childNum ) +{ + return list->GetPositionAtIndex( childNum ); +} + +template +NTreeNode *NTreeNode::GetChild( int childNum ) +{ + return list->GetItemAtIndex( childNum ); +} + +template +NTreeNode *NTreeNode::GetChild( Position position ) +{ + return list->GetItemAtIndex( position ); +} + +template +int NTreeNode::GetIndexRelativeToParent() +{ + if( !parent ) + { + assert( 0 ); // hack + return -1; + } + GListIterator *> iterator( parent->list ); + int i; + for( i = 0, iterator.GotoHead(); !iterator.AtEnd(); iterator++, i++ ) + { + if( iterator.GetCurrent() == this ) + { + return i; + } + } + assert( 0 ); // hack + return -1; +} + +template +T NTreeNode::GetItem() +{ + return data; +} + +template +NTreeNode *NTreeNode::GetParent() +{ + return parent; +} + +template +NTreeNode *NTreeNode::GetRoot() +{ + NTreeNode *node; + node = this; + while( node->GetParent() ) + { + node = node->GetParent(); + } + return node; +} + +template +NTreeNode *NTreeNode::GetNextSibling() +{ + int currentID, siblingID; + NTreeNode *parent; + parent = this->GetParent(); + if( !parent ) + { + return NULL; + } + currentID = this->GetIndexRelativeToParent(); + siblingID = currentID + 1; + if( siblingID < parent->GetNumChildren() ) + { + return parent->GetChild( siblingID ); + } + else + { + return NULL; + } +} + +template +void NTreeNode::Traverse( void (*VisitFunc)( T, int depth ), int maxTreeDepth ) +{ + ArrayStack *> stack( maxTreeDepth ); + NTreeNode *current, *nextSibling; + + stack.Push( this ); + Visit( this->GetItem(), 0 ); + while( !stack.IsEmpty() ) + { + current = stack.Pop(); + if( current->GetNumChildren() > 0 ) + { + stack.Push( current ); + stack.Push( current->GetChild( 0 ) ); + Visit( current->GetChild( 0 )->GetItem(), stack.GetDepth() - 1 ); + } + else + { + while( !stack.IsEmpty() && !( nextSibling = current->GetNextSibling() ) ) + { + current = stack.Pop(); + } + if( !stack.IsEmpty() ) + { + stack.Push( nextSibling ); + Visit( nextSibling->GetItem(), stack.GetDepth() - 1 ); + } + } + } +} + +template +NTreeNode *NTreeNode::ReentrantTraversalGetFirst( int maxTreeDepth ) +{ + if( reentrantStack ) + { + delete reentrantStack; + } + reentrantStack = new ArrayStack *>( maxTreeDepth ); + reentrantStack->Push( this ); + return this; +} + +template +NTreeNode *NTreeNode::ReentrantTraversalGetNext( void ) +{ + NTreeNode *current, *nextSibling; + + while( !reentrantStack->IsEmpty() ) + { + current = reentrantStack->Pop(); + if( current->GetNumChildren() > 0 ) + { + reentrantStack->Push( current ); + reentrantStack->Push( current->GetChild( 0 ) ); + return current->GetChild( 0 ); + } + else + { + while( !reentrantStack->IsEmpty() && !( nextSibling = current->GetNextSibling() ) ) + { + current = reentrantStack->Pop(); + } + if( !reentrantStack->IsEmpty() ) + { + reentrantStack->Push( nextSibling ); + return nextSibling; + } + } + } + delete reentrantStack; + reentrantStack = NULL; + return NULL; +} + +#endif /* __TREE_H__ */ diff --git a/public/nvector.h b/public/nvector.h new file mode 100644 index 0000000..95813b9 --- /dev/null +++ b/public/nvector.h @@ -0,0 +1,203 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef NVECTOR_H +#define NVECTOR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include + + +#define NVectorN NVector +#define NVector3 NVector<3> + + +// N-dimensional vector. +template +class NVector +{ +public: + + NVectorN() {} + + float& operator[]( int i ); + float const& operator[]( int i ) const; + + float Dot( NVectorN const &b ) const; + NVectorN Cross( NVectorN const &b ) const; + NVectorN Normalize() const; + float Length() const; + + NVectorN operator-() const; + NVectorN operator+( NVectorN const &b ) const; + NVectorN const &operator+=( NVectorN const &b ); + NVectorN operator-( NVectorN const &b ) const; + NVectorN operator*( float val ) const; + + +// Static helpers. +public: + + static NVectorN SetupNVectorNull(); // Returns a vector of all zeros. + + +public: + + float v[N]; +}; + + +template +inline float NDot( NVector const &a, NVector const &b ) +{ + float ret = 0; + for( int i=0; i < N; i++ ) + ret += a.v[i] * b.v[i]; + return ret; +} + +template +Vector& ToVec( NVector &vec ) {assert( N >= 3 ); return *((Vector*)&vec);} + +template +Vector const& ToVec( NVector const &vec ){assert( N >= 3 ); return *((Vector const*)&vec);} + +NVector<3>& ToNVec( Vector &vec ) {return *((NVector<3>*)&vec);} +NVector<3> const& ToNVec( Vector const &vec ) {return *((NVector<3> const*)&vec);} + + +// ------------------------------------------------------------------------------------ // +// NVector inlines. +// ------------------------------------------------------------------------------------ // + +template +NVectorN NVectorN::SetupNVectorNull() +{ + NVector ret; + memset( ret.v, 0, sizeof(float)*N ); + return ret; +} + + +template +float& NVectorN::operator[]( int i ) +{ + assert( i >= 0 && i < N ); + return v[i]; +} + + +template +float const& NVectorN::operator[]( int i ) const +{ + assert( i >= 0 && i < N ); + return v[i]; +} + + +template +float NVectorN::Dot( NVectorN const &b ) const +{ + float ret = 0; + + for( int i=0; i < N; i++ ) + ret += v[i]*b.v[i]; + + return ret; +} + + +template +NVectorN NVectorN::Cross( NVectorN const &b ) const +{ + NVector ret; + NMatrix mat; + + for( int i=0; i < N; i++ ) + { + for( y=0; y < N; y++ ) + for( x=0; x < N; x++ ) + mat.m[y][x] = + + ret.v[i] = v[i]*b.v[i]; + } + + return ret; +} + + +template +NVectorN NVectorN::Normalize() const +{ + return *this * (1.0f / Length()); +} + + +template +float NVectorN::Length() const +{ + return (float)sqrt( Dot(*this) ); +} + + +template +NVectorN NVectorN::operator-() const +{ + NVectorN ret; + for( int i=0; i < N; i++ ) + ret.v[i] = -v[i]; + return ret; +} + + +template +NVectorN NVectorN::operator+( NVectorN const &b ) const +{ + NVectorN ret; + + for( int i=0; i < N; i++ ) + ret.v[i] = v[i]+b.v[i]; + + return ret; +} + + +template +NVectorN const &NVectorN::operator+=( NVectorN const &b ) +{ + for( int i=0; i < N; i++ ) + v[i] += b.v[i]; + return *this; +} + + +template +NVectorN NVectorN::operator-( NVectorN const &b ) const +{ + NVectorN ret; + + for( int i=0; i < N; i++ ) + ret.v[i] = v[i]-b.v[i]; + + return ret; +} + +template +NVectorN NVectorN::operator*( float val ) const +{ + NVectorN ret; + for( int i=0; i < N; i++ ) + ret.v[i] = v[i] * val; + return ret; +} + + +#endif // NVECTOR_H + diff --git a/public/nvtc.h b/public/nvtc.h new file mode 100644 index 0000000..876e838 --- /dev/null +++ b/public/nvtc.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1997-8 S3 Inc. All Rights Reserved. + * + * Module Name: s3_intrf.h + * + * Purpose: Constant, structure, and prototype definitions for S3TC + * interface to DX surface + * + * Author: Dan Hung, Martin Hoffesommer + * + * Revision History: + * version Beta 1.00.00-98-03-26 + */ + +// Highlevel interface + +#ifndef NVTC_H +#define NVTC_H + +#if defined( _WIN32 ) && !defined( _X360 ) +#if _MSC_VER >= 1400 +// This assumes that src\public is somewhere on the path +// ddraw.h doesn't actually live in the system headers in VS2005. +#include "..\dx9sdk\include\ddraw.h" +#else +#include +#endif +#endif + +// RGB encoding types +#define S3TC_ENCODE_RGB_FULL 0x0 +#define S3TC_ENCODE_RGB_COLOR_KEY 0x1 +#define S3TC_ENCODE_RGB_ALPHA_COMPARE 0x2 +#define _S3TC_ENCODE_RGB_MASK 0xff + +// alpha encoding types +#define S3TC_ENCODE_ALPHA_NONE 0x000 +#define S3TC_ENCODE_ALPHA_EXPLICIT 0x100 +#define S3TC_ENCODE_ALPHA_INTERPOLATED 0x200 +#define _S3TC_ENCODE_ALPHA_MASK 0xff00 + + +#if defined( _WIN32 ) && !defined( _X360 ) +// common encoding types +//@@@TBD + +// error codes +#define ERROR_ABORTED -1 + +// Progress Callback for S3TCencode +typedef BOOL (* LP_S3TC_PROGRESS_CALLBACK)(float fProgress, LPVOID lpUser1, LPVOID lpUser2); + +// set alpha reference value for alpha compare encoding +void S3TCsetAlphaReference(int nRef); + +// determine number of bytes needed to compress given source image +unsigned int S3TCgetEncodeSize(DDSURFACEDESC *lpDesc, // [in] + unsigned int dwEncodeType // [in] + ); + +// encode (compress) given source image to given destination surface +void S3TCencode(DDSURFACEDESC *lpSrc, // [in] + PALETTEENTRY *lpPal, // [in], may be NULL + DDSURFACEDESC *lpDest, // [out] + void *lpDestBuf, // [in] + unsigned int dwEncodeType, // [in] + float *weight // [in] + ); + +int S3TCencodeEx(DDSURFACEDESC *lpSrc, // [in] + PALETTEENTRY *lpPal, // [in], may be NULL + DDSURFACEDESC *lpDest, // [out] + void *lpDestBuf, // [in] + unsigned int dwEncodeType, // [in] + float *weight, // [in] + LP_S3TC_PROGRESS_CALLBACK lpS3TCProgressProc, // [in], may be NULL + LPVOID lpArg1, // in + LPVOID lpArg2 // in + ); + +// determine number of bytes needed do decompress given compressed image +unsigned int S3TCgetDecodeSize(DDSURFACEDESC *lpDesc); + +// decode (decompress) to ARGB8888 +void S3TCdecode(DDSURFACEDESC *lpSrc, // [in] + DDSURFACEDESC *lpDest, // [out] + void *lpDestBuf // [in] + ); + +#endif // _WIN32 + +#endif // NVTC_H diff --git a/public/optimize.h b/public/optimize.h new file mode 100644 index 0000000..0723ea9 --- /dev/null +++ b/public/optimize.h @@ -0,0 +1,264 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef OPTIMIZE_H +#define OPTIMIZE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "studio.h" + +#define MAX_NUM_BONES_PER_STRIP 512 + +#define OPTIMIZED_MODEL_FILE_VERSION 7 + +extern bool g_bDumpGLViewFiles; + +struct s_bodypart_t; + +namespace OptimizedModel +{ + +#pragma pack(1) + +struct BoneStateChangeHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int hardwareID; + int newBoneID; +}; + +struct Vertex_t +{ + DECLARE_BYTESWAP_DATADESC(); + // these index into the mesh's vert[origMeshVertID]'s bones + unsigned char boneWeightIndex[MAX_NUM_BONES_PER_VERT]; + unsigned char numBones; + + unsigned short origMeshVertID; + + // for sw skinned verts, these are indices into the global list of bones + // for hw skinned verts, these are hardware bone indices + char boneID[MAX_NUM_BONES_PER_VERT]; +}; + +// We don't do actual strips anymore, only triangle lists and subd quad lists +enum StripHeaderFlags_t +{ + STRIP_IS_TRILIST = 0x01, + STRIP_IS_QUADLIST_REG = 0x02, // Regular sub-d quads + STRIP_IS_QUADLIST_EXTRA = 0x04 // Extraordinary sub-d quads +}; + +// A strip is a piece of a stripgroup that is divided by bones +struct StripHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numIndices; // indexOffset offsets into the mesh's index array + int indexOffset; + + int numVerts; // vertexOffset offsets into the mesh's vert array + int vertOffset; + + // Use this to enable/disable skinning. + // May decide (in optimize.cpp) to put all with 1 bone in a different strip + // than those that need skinning. + short numBones; + + unsigned char flags; + + int numBoneStateChanges; + int boneStateChangeOffset; + inline BoneStateChangeHeader_t *pBoneStateChange( int i ) const + { + return (BoneStateChangeHeader_t *)(((byte *)this) + boneStateChangeOffset) + i; + }; + + // These go last on purpose! + int numTopologyIndices; + int topologyOffset; +}; + +enum StripGroupFlags_t +{ + STRIPGROUP_IS_HWSKINNED = 0x02, + STRIPGROUP_IS_DELTA_FLEXED = 0x04, + STRIPGROUP_SUPPRESS_HW_MORPH = 0x08, // NOTE: This is a temporary flag used at run time. +}; + +// a locking group +// a single vertex buffer +// a single index buffer +struct StripGroupHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + // These are the arrays of all verts and indices for this mesh. strips index into this. + int numVerts; + int vertOffset; + inline Vertex_t *pVertex( int i ) const + { + return (Vertex_t *)(((byte *)this) + vertOffset) + i; + }; + + int numIndices; + int indexOffset; + inline unsigned short *pIndex( int i ) const + { + return (unsigned short *)(((byte *)this) + indexOffset) + i; + }; + + int numStrips; + int stripOffset; + inline StripHeader_t *pStrip( int i ) const + { + return (StripHeader_t *)(((byte *)this) + stripOffset) + i; + }; + + unsigned char flags; + + int numTopologyIndices; + int topologyOffset; + inline unsigned short *pTopologyIndex( int i ) const + { + return (unsigned short *)(((byte *)this) + topologyOffset) + i; + }; +}; + +enum MeshFlags_t { + // these are both material properties, and a mesh has a single material. + MESH_IS_TEETH = 0x01, + MESH_IS_EYES = 0x02 +}; + +// a collection of locking groups: +// up to 4: +// non-flexed, hardware skinned +// flexed, hardware skinned +// non-flexed, software skinned +// flexed, software skinned +// +// A mesh has a material associated with it. +struct MeshHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numStripGroups; + int stripGroupHeaderOffset; + inline StripGroupHeader_t *pStripGroup( int i ) const + { + StripGroupHeader_t *pDebug = (StripGroupHeader_t *)(((byte *)this) + stripGroupHeaderOffset) + i; + return pDebug; + }; + unsigned char flags; +}; + +struct ModelLODHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numMeshes; + int meshOffset; + float switchPoint; + inline MeshHeader_t *pMesh( int i ) const + { + MeshHeader_t *pDebug = (MeshHeader_t *)(((byte *)this) + meshOffset) + i; + return pDebug; + }; +}; + +// This maps one to one with models in the mdl file. +// There are a bunch of model LODs stored inside potentially due to the qc $lod command +struct ModelHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numLODs; // garymcthack - this is also specified in FileHeader_t + int lodOffset; + inline ModelLODHeader_t *pLOD( int i ) const + { + ModelLODHeader_t *pDebug = ( ModelLODHeader_t *)(((byte *)this) + lodOffset) + i; + return pDebug; + }; +}; + +struct BodyPartHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numModels; + int modelOffset; + inline ModelHeader_t *pModel( int i ) const + { + ModelHeader_t *pDebug = (ModelHeader_t *)(((byte *)this) + modelOffset) + i; + return pDebug; + }; +}; + +struct MaterialReplacementHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + short materialID; + int replacementMaterialNameOffset; + inline const char *pMaterialReplacementName( void ) + { + const char *pDebug = (const char *)(((byte *)this) + replacementMaterialNameOffset); + return pDebug; + } +}; + +struct MaterialReplacementListHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int numReplacements; + int replacementOffset; + inline MaterialReplacementHeader_t *pMaterialReplacement( int i ) const + { + MaterialReplacementHeader_t *pDebug = ( MaterialReplacementHeader_t *)(((byte *)this) + replacementOffset) + i; + return pDebug; + } +}; + +struct FileHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + // file version as defined by OPTIMIZED_MODEL_FILE_VERSION + int version; + + // hardware params that affect how the model is to be optimized. + int vertCacheSize; + unsigned short maxBonesPerStrip; + unsigned short maxBonesPerFace; + int maxBonesPerVert; + + // must match checkSum in the .mdl + long checkSum; + + int numLODs; // garymcthack - this is also specified in ModelHeader_t and should match + + // one of these for each LOD + int materialReplacementListOffset; + MaterialReplacementListHeader_t *pMaterialReplacementList( int lodID ) const + { + MaterialReplacementListHeader_t *pDebug = + (MaterialReplacementListHeader_t *)(((byte *)this) + materialReplacementListOffset) + lodID; + return pDebug; + } + + int numBodyParts; + int bodyPartOffset; + inline BodyPartHeader_t *pBodyPart( int i ) const + { + BodyPartHeader_t *pDebug = (BodyPartHeader_t *)(((byte *)this) + bodyPartOffset) + i; + return pDebug; + }; +}; + +#pragma pack() + +void WriteOptimizedFiles( studiohdr_t *phdr, s_bodypart_t *pSrcBodyParts ); + +}; // namespace OptimizedModel + +#endif // OPTIMIZE_H diff --git a/public/overlaytext.h b/public/overlaytext.h new file mode 100644 index 0000000..5a52ff6 --- /dev/null +++ b/public/overlaytext.h @@ -0,0 +1,58 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( OVERLAYTEXT_H ) +#define OVERLAYTEXT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector.h" + +class OverlayText_t +{ +public: + OverlayText_t() + { + nextOverlayText = 0; + origin.Init(); + bUseOrigin = false; + lineOffset = 0; + flXPos = 0; + flYPos = 0; + text[ 0 ] = 0; + m_flEndTime = 0.0f; + m_nServerCount = -1; + m_nCreationTick = -1; + r = g = b = a = 255; + } + + bool IsDead(); + void SetEndTime( float duration ); + + Vector origin; + bool bUseOrigin; + int lineOffset; + float flXPos; + float flYPos; + char text[512]; + float m_flEndTime; // When does this text go away + int m_nCreationTick; // If > 0, show only one server frame + int m_nServerCount; // compare server spawn count to remove stale overlays + int r; + int g; + int b; + int a; + OverlayText_t *nextOverlayText; +}; + +#endif // OVERLAYTEXT_H \ No newline at end of file diff --git a/public/p4lib/ip4.h b/public/p4lib/ip4.h new file mode 100644 index 0000000..3114b1f --- /dev/null +++ b/public/p4lib/ip4.h @@ -0,0 +1,191 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef IP4_H +#define IP4_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlsymbol.h" +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" +#include "appframework/IAppSystem.h" + + +//----------------------------------------------------------------------------- +// Current perforce file state +//----------------------------------------------------------------------------- +enum P4FileState_t +{ + P4FILE_UNOPENED = 0, + P4FILE_OPENED_FOR_ADD, + P4FILE_OPENED_FOR_EDIT, + P4FILE_OPENED_FOR_DELETE, + P4FILE_OPENED_FOR_INTEGRATE, +}; + + +//----------------------------------------------------------------------------- +// Purpose: definition of a file +//----------------------------------------------------------------------------- +struct P4File_t +{ + CUtlSymbol m_sName; // file name + CUtlSymbol m_sPath; // residing folder + CUtlSymbol m_sDepotFile; // the name in the depot + CUtlSymbol m_sClientFile; // the name on the client in Perforce syntax + CUtlSymbol m_sLocalFile; // the name on the client in local syntax + int m_iHeadRevision; // head revision number + int m_iHaveRevision; // the revision the clientspec has synced locally + bool m_bOpenedByOther; // opened by another user + bool m_bDir; // directory + bool m_bDeleted; // deleted + P4FileState_t m_eOpenState; // current change state + int m_iChangelist; // changelist current opened in +}; + + +//----------------------------------------------------------------------------- +// Purpose: a single revision of a file +//----------------------------------------------------------------------------- +struct P4Revision_t +{ + int m_iChange; // changelist number + int m_nYear, m_nMonth, m_nDay; + int m_nHour, m_nMinute, m_nSecond; + + CUtlSymbol m_sUser; // submitting user + CUtlSymbol m_sClient; // submitting client + CUtlString m_Description; +}; + + +//----------------------------------------------------------------------------- +// Purpose: a single clientspec +//----------------------------------------------------------------------------- +struct P4Client_t +{ + CUtlSymbol m_sName; + CUtlSymbol m_sUser; + CUtlSymbol m_sHost; // machine name this client is on + CUtlSymbol m_sLocalRoot; // local path +}; + + +//----------------------------------------------------------------------------- +// Purpose: Interface to accessing P4 commands +//----------------------------------------------------------------------------- +#define P4_MAX_INPUT_BUFFER_SIZE 16384 // descriptions should be limited to this size! + +abstract_class IP4 : public IAppSystem +{ +public: + // name of the current clientspec + virtual P4Client_t &GetActiveClient() = 0; + + // changes the current client + virtual void SetActiveClient(const char *clientname) = 0; + + // Refreshes the current client from p4 settings + virtual void RefreshActiveClient() = 0; + + // translate filespecs into the desired syntax + virtual void GetDepotFilePath(char *depotFilePath, const char *filespec, int size) = 0; + virtual void GetClientFilePath(char *clientFilePath, const char *filespec, int size) = 0; + virtual void GetLocalFilePath(char *localFilePath, const char *filespec, int size) = 0; + + // retreives the list of files in a path + virtual CUtlVector &GetFileList( const char *path ) = 0; + + // returns the list of files opened for edit/integrate/delete + virtual void GetOpenedFileList( CUtlVector &fileList, bool bDefaultChangeOnly ) = 0; + virtual void GetOpenedFileList( const char *pRootDirectory, CUtlVector &fileList ) = 0; + virtual void GetOpenedFileListInPath( const char *pPathID, CUtlVector &fileList ) = 0; + + // retrieves revision history for a file or directory + virtual CUtlVector &GetRevisionList( const char *path, bool bIsDir ) = 0; + + // returns a list of clientspecs + virtual CUtlVector &GetClientList() = 0; + + // changes the clientspec to remove the specified path (cloaking) + virtual void RemovePathFromActiveClientspec( const char *path ) = 0; + + // file manipulation + virtual bool OpenFileForAdd( const char *pFullPath ) = 0; + virtual bool OpenFileForEdit( const char *pFullPath ) = 0; + virtual bool OpenFileForDelete( const char *pFullPath ) = 0; + virtual bool SyncFile( const char *pFullPath, int nRevision = -1 ) = 0; // default revision is to sync to the head revision + + // submit/revert + virtual bool SubmitFile( const char *pFullPath, const char *pDescription ) = 0; + virtual bool RevertFile( const char *pFullPath ) = 0; + + // file checkin/checkout for multiple files + virtual bool OpenFilesForAdd( int nCount, const char **ppFullPathList ) = 0; + virtual bool OpenFilesForEdit( int nCount, const char **ppFullPathList ) = 0; + virtual bool OpenFilesForDelete( int nCount, const char **ppFullPathList ) = 0; + + // submit/revert for multiple files + virtual bool SubmitFiles( int nCount, const char **ppFullPathList, const char *pDescription ) = 0; + virtual bool RevertFiles( int nCount, const char **ppFullPathList ) = 0; + + // Is this file in perforce? + virtual bool IsFileInPerforce( const char *pFullPath ) = 0; + + // Get the perforce file state + virtual P4FileState_t GetFileState( const char *pFullPath ) = 0; + + // depot root + virtual const char *GetDepotRoot() = 0; + virtual int GetDepotRootLength() = 0; + + // local root + virtual const char *GetLocalRoot() = 0; + virtual int GetLocalRootLength() = 0; + + // Gets a string for a symbol + virtual const char *String( CUtlSymbol s ) const = 0; + + // Returns which clientspec a file lies under. This will + // search for p4config files in root directories of the file + // It returns false if it didn't find a p4config file. + virtual bool GetClientSpecForFile( const char *pFullPath, char *pClientSpec, int nMaxLen ) = 0; + virtual bool GetClientSpecForDirectory( const char *pFullPathDir, char *pClientSpec, int nMaxLen ) = 0; + + // Returns which clientspec a filesystem path ID lies under. This will + // search for p4config files in all root directories of the all paths in + // the search path. NOTE: All directories in a path need to use the same clientspec + // or this function will return false. + // It returns false if it didn't find a p4config file. + virtual bool GetClientSpecForPath( const char *pPathId, char *pClientSpec, int nMaxLen ) = 0; + + // Opens a file in p4 win + virtual void OpenFileInP4Win( const char *pFullPath ) = 0; + + // have we connected? if not, nothing works + virtual bool IsConnectedToServer( bool bRetry = true ) = 0; + + // Returns file information for a single file + virtual bool GetFileInfo( const char *pFullPath, P4File_t *pFileInfo ) = 0; + + // retrieves the list of files in a path, using a known client spec + virtual CUtlVector &GetFileListUsingClientSpec( const char *pPath, const char *pClientSpec ) = 0; + + // retrieves the last error from the last op (which is likely to span multiple lines) + // this is only valid after OpenFile[s]For{Add,Edit,Delete} or {Submit,Revert}File[s] + virtual const char *GetLastError() = 0; + + // sets the name of the changelist to open files under, NULL for "Default" changelist + virtual void SetOpenFileChangeList( const char *pChangeListName ) = 0; + + virtual void GetFileListInChangelist( unsigned int changeListNumber, CUtlVector &fileList ) = 0; +}; + +DECLARE_TIER2_INTERFACE( IP4, p4 ); + +#endif // IP4_H diff --git a/public/particles/particles.h b/public/particles/particles.h new file mode 100644 index 0000000..10f0504 --- /dev/null +++ b/public/particles/particles.h @@ -0,0 +1,3280 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: particle system definitions +// +//===========================================================================// + +#ifndef PARTICLES_H +#define PARTICLES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/mathlib.h" +#include "mathlib/vector.h" +#include "mathlib/ssemath.h" +#include "appframework/iappsystem.h" +#if 1 +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/materialsystemutil.h" +#else +class IMaterial; +class IMatRenderContext; +#endif + +#include "dmxloader/dmxelement.h" +#include "tier1/utlintrusivelist.h" +#include "vstdlib/random.h" +#include "tier1/utlobjectreference.h" +#include "tier1/utlstringmap.h" +#include "tier1/utlmap.h" +#include "trace.h" +#include "tier1/utlsoacontainer.h" +#include "raytrace.h" +#if defined( CLIENT_DLL ) +#include "c_pixel_visibility.h" +#endif + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct DmxElementUnpackStructure_t; +class CParticleSystemDefinition; +class CParticleCollection; +class CParticleOperatorInstance; +class CParticleSystemDictionary; +class CUtlBuffer; +class IParticleOperatorDefinition; +class CSheet; +class CMeshBuilder; +extern float s_pRandomFloats[]; + + +//----------------------------------------------------------------------------- +// Random numbers +//----------------------------------------------------------------------------- +#define MAX_RANDOM_FLOATS 4096 +#define RANDOM_FLOAT_MASK ( MAX_RANDOM_FLOATS - 1 ) + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +// Get a list of the files inside the particle manifest file +void GetParticleManifest( CUtlVector& list ); +void GetParticleManifest( CUtlVector& list, const char *pFile ); + +//----------------------------------------------------------------------------- +// Particle attributes +//----------------------------------------------------------------------------- +#define MAX_PARTICLE_ATTRIBUTES 24 + +#define DEFPARTICLE_ATTRIBUTE( name, bit, datatype ) \ + const int PARTICLE_ATTRIBUTE_##name##_MASK = (1 << bit); \ + const int PARTICLE_ATTRIBUTE_##name = bit; \ + const EAttributeDataType PARTICLE_ATTRIBUTE_##name##_DATATYPE = datatype; + +// required +DEFPARTICLE_ATTRIBUTE( XYZ, 0, ATTRDATATYPE_4V ); + +// particle lifetime (duration) of particle as a float. +DEFPARTICLE_ATTRIBUTE( LIFE_DURATION, 1, ATTRDATATYPE_FLOAT ); + +// prev coordinates for verlet integration +DEFPARTICLE_ATTRIBUTE( PREV_XYZ, 2, ATTRDATATYPE_4V ); + +// radius of particle +DEFPARTICLE_ATTRIBUTE( RADIUS, 3, ATTRDATATYPE_FLOAT ); + +// rotation angle of particle +DEFPARTICLE_ATTRIBUTE( ROTATION, 4, ATTRDATATYPE_FLOAT ); + +// rotation speed of particle +DEFPARTICLE_ATTRIBUTE( ROTATION_SPEED, 5, ATTRDATATYPE_FLOAT ); + +// tint of particle +DEFPARTICLE_ATTRIBUTE( TINT_RGB, 6, ATTRDATATYPE_4V ); + +// alpha tint of particle +DEFPARTICLE_ATTRIBUTE( ALPHA, 7, ATTRDATATYPE_FLOAT ); + +// creation time stamp (relative to particle system creation) +DEFPARTICLE_ATTRIBUTE( CREATION_TIME, 8, ATTRDATATYPE_FLOAT ); + +// sequnece # (which animation sequence number this particle uses ) +DEFPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER, 9, ATTRDATATYPE_FLOAT ); + +// length of the trail +DEFPARTICLE_ATTRIBUTE( TRAIL_LENGTH, 10, ATTRDATATYPE_FLOAT ); + +// unique particle identifier +DEFPARTICLE_ATTRIBUTE( PARTICLE_ID, 11, ATTRDATATYPE_INT ); + +// unique rotation around up vector +DEFPARTICLE_ATTRIBUTE( YAW, 12, ATTRDATATYPE_FLOAT ); + +// second sequnece # (which animation sequence number this particle uses ) +DEFPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER1, 13, ATTRDATATYPE_FLOAT ); + +// hit box index +DEFPARTICLE_ATTRIBUTE( HITBOX_INDEX, 14, ATTRDATATYPE_INT ); + +DEFPARTICLE_ATTRIBUTE( HITBOX_RELATIVE_XYZ, 15, ATTRDATATYPE_4V ); + +DEFPARTICLE_ATTRIBUTE( ALPHA2, 16, ATTRDATATYPE_FLOAT ); + +// particle trace caching fields +DEFPARTICLE_ATTRIBUTE( TRACE_P0, 17, ATTRDATATYPE_4V ); // start pnt of trace +DEFPARTICLE_ATTRIBUTE( TRACE_P1, 18, ATTRDATATYPE_4V ); // end pnt of trace +DEFPARTICLE_ATTRIBUTE( TRACE_HIT_T, 19, ATTRDATATYPE_FLOAT ); // 0..1 if hit +DEFPARTICLE_ATTRIBUTE( TRACE_HIT_NORMAL, 20, ATTRDATATYPE_4V ); // 0 0 0 if no hit + +DEFPARTICLE_ATTRIBUTE( NORMAL, 21, ATTRDATATYPE_4V ); // 0 0 0 if none + +DEFPARTICLE_ATTRIBUTE( GLOW_RGB, 22, ATTRDATATYPE_4V ); // glow color +DEFPARTICLE_ATTRIBUTE( GLOW_ALPHA, 23, ATTRDATATYPE_FLOAT ); // glow alpha + +#define MAX_PARTICLE_CONTROL_POINTS 64 + +#define ATTRIBUTES_WHICH_ARE_VEC3S_MASK ( PARTICLE_ATTRIBUTE_TRACE_P0_MASK | PARTICLE_ATTRIBUTE_TRACE_P1_MASK | \ + PARTICLE_ATTRIBUTE_TRACE_HIT_NORMAL_MASK | PARTICLE_ATTRIBUTE_XYZ_MASK | \ + PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_TINT_RGB_MASK | \ + PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK | PARTICLE_ATTRIBUTE_NORMAL_MASK | \ + PARTICLE_ATTRIBUTE_GLOW_RGB_MASK ) + +#define ATTRIBUTES_WHICH_ARE_0_TO_1 (PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_ALPHA2_MASK) +#define ATTRIBUTES_WHICH_ARE_ANGLES (PARTICLE_ATTRIBUTE_ROTATION_MASK | PARTICLE_ATTRIBUTE_YAW_MASK ) +#define ATTRIBUTES_WHICH_ARE_INTS (PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK | PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK ) + +// Auto filters +#define ATTRIBUTES_WHICH_ARE_POSITION_AND_VELOCITY (PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK) +#define ATTRIBUTES_WHICH_ARE_LIFE_DURATION (PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK) +#define ATTRIBUTES_WHICH_ARE_ROTATION (PARTICLE_ATTRIBUTE_ROTATION_MASK | PARTICLE_ATTRIBUTE_YAW_MASK | PARTICLE_ATTRIBUTE_ROTATION_SPEED_MASK) +#define ATTRIBUTES_WHICH_ARE_SIZE (PARTICLE_ATTRIBUTE_RADIUS_MASK | PARTICLE_ATTRIBUTE_TRAIL_LENGTH_MASK) +#define ATTRIBUTES_WHICH_ARE_COLOR_AND_OPACITY (PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_GLOW_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_ALPHA2_MASK | PARTICLE_ATTRIBUTE_GLOW_ALPHA_MASK ) +#define ATTRIBUTES_WHICH_ARE_ANIMATION_SEQUENCE (PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK | PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1_MASK) +#define ATTRIBUTES_WHICH_ARE_HITBOX (PARTICLE_ATTRIBUTE_HITBOX_INDEX_MASK | PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ_MASK) +#define ATTRIBUTES_WHICH_ARE_NORMAL (PARTICLE_ATTRIBUTE_NORMAL_MASK) + +#if defined( PLATFORM_X360 ) +#define MAX_PARTICLES_IN_A_SYSTEM 2000 +#else +#ifdef SOB +#define MAX_PARTICLES_IN_A_SYSTEM 25000 +#else +#define MAX_PARTICLES_IN_A_SYSTEM 5000 +#endif +#endif + +#define MEASURE_PARTICLE_PERF 0 + +#define MIN_PARTICLE_SPEED 0.001 + +#define MAX_PARTICLE_ORIENTATION_TYPES 3 + +//----------------------------------------------------------------------------- +// Particle function types +//----------------------------------------------------------------------------- +enum ParticleFunctionType_t +{ + FUNCTION_RENDERER = 0, + FUNCTION_OPERATOR, + FUNCTION_INITIALIZER, + FUNCTION_EMITTER, + FUNCTION_CHILDREN, // NOTE: This one is a fake function type, only here to help eliminate a ton of duplicated code in the editor + FUNCTION_FORCEGENERATOR, + FUNCTION_CONSTRAINT, + PARTICLE_FUNCTION_COUNT +}; + +//----------------------------------------------------------------------------- +// Particle filter types +// Used for classifying operators in the interface +// Can only have 32 altogether +//----------------------------------------------------------------------------- +enum ParticleFilterType_t +{ + FILTER_NOT_SPECIAL = 0, + FILTER_POSITION_AND_VELOCITY, + FILTER_LIFE_DURATION, + FILTER_PARAMETER_REMAPPING, + FILTER_ROTATION, + FILTER_SIZE, + FILTER_COLOR_AND_OPACITY, + FILTER_ANIMATION_SEQUENCE, + FILTER_HITBOX, + FILTER_NORMAL, + FILTER_CONTROL_POINTS, + FILTER_COUNT +}; + +enum ParticleFilterMask_t +{ + FILTER_NOT_SPECIAL_MASK = 0, + FILTER_POSITION_AND_VELOCITY_MASK = 1 << 1, + FILTER_LIFE_DURATION_MASK = 1 << 2, + FILTER_PARAMETER_REMAPPING_MASK = 1 << 3, + FILTER_ROTATION_MASK = 1 << 4, + FILTER_SIZE_MASK = 1 << 5, + FILTER_COLOR_AND_OPACITY_MASK = 1 << 6, + FILTER_ANIMATION_SEQUENCE_MASK = 1 << 7, + FILTER_HITBOX_MASK = 1 << 8, + FILTER_NORMAL_MASK = 1 << 9, + FILTER_CONTROL_POINTS_MASK = 1 << 10 +}; + +struct CParticleVisibilityInputs +{ + float m_flInputMin; + float m_flInputMax; + float m_flAlphaScaleMin; + float m_flAlphaScaleMax; + float m_flRadiusScaleMin; + float m_flRadiusScaleMax; + float m_flRadiusScaleFOVBase; + float m_flProxyRadius; + float m_flDistanceInputMin; + float m_flDistanceInputMax; + float m_flDotInputMin; + float m_flDotInputMax; + int m_nCPin; +}; + +struct ModelHitBoxInfo_t +{ + Vector m_vecBoxMins; + Vector m_vecBoxMaxes; + matrix3x4_t m_Transform; +}; + +class CModelHitBoxesInfo +{ +public: + float m_flLastUpdateTime; + float m_flPrevLastUpdateTime; + int m_nNumHitBoxes; + int m_nNumPrevHitBoxes; + ModelHitBoxInfo_t *m_pHitBoxes; + ModelHitBoxInfo_t *m_pPrevBoxes; + + bool CurAndPrevValid( void ) const + { + return ( m_nNumHitBoxes && ( m_nNumPrevHitBoxes == m_nNumHitBoxes ) ); + } + + CModelHitBoxesInfo( void ) + { + m_flLastUpdateTime = -1; + m_nNumHitBoxes = 0; + m_nNumPrevHitBoxes = 0; + m_pHitBoxes = NULL; + m_pPrevBoxes = NULL; + } + + ~CModelHitBoxesInfo( void ) + { + if ( m_pHitBoxes ) + delete[] m_pHitBoxes; + if ( m_pPrevBoxes ) + delete[] m_pPrevBoxes; + } + +}; + + +//----------------------------------------------------------------------------- +// Particle kill list +//----------------------------------------------------------------------------- +#define KILL_LIST_INDEX_BITS 24 +#define KILL_LIST_FLAGS_BITS ( 32 - KILL_LIST_INDEX_BITS ) +#define KILL_LIST_INDEX_MASK ( ( 1 << KILL_LIST_INDEX_BITS ) - 1 ) +#define KILL_LIST_FLAGS_MASK ( ( 1 << KILL_LIST_FLAGS_BITS ) - 1 ) +struct KillListItem_t +{ + unsigned int nIndex : KILL_LIST_INDEX_BITS; + unsigned int nFlags : KILL_LIST_FLAGS_BITS; +}; +enum KillListFlags +{ + // TODO: use this in ApplyKillList (the idea: pass particles to a child system, but dont then kill them) + KILL_LIST_FLAG_DONT_KILL = ( 1 << 0 ) +}; + + +//----------------------------------------------------------------------------- +// Interface to allow the particle system to call back into the client +//----------------------------------------------------------------------------- + +#define PARTICLE_SYSTEM_QUERY_INTERFACE_VERSION "VParticleSystemQuery004" + +class IParticleSystemQuery : public IAppSystem +{ +public: + virtual bool IsEditor( ) = 0; + + virtual void GetLightingAtPoint( const Vector& vecOrigin, Color &tint ) = 0; + virtual void TraceLine( const Vector& vecAbsStart, + const Vector& vecAbsEnd, unsigned int mask, + const class IHandleEntity *ignore, + int collisionGroup, + CBaseTrace *ptr ) = 0; + + // given a possible spawn point, tries to movie it to be on or in the source object. returns + // true if it succeeded + virtual bool MovePointInsideControllingObject( CParticleCollection *pParticles, + void *pObject, + Vector *pPnt ) + { + return true; + } + + virtual bool IsPointInControllingObjectHitBox( + CParticleCollection *pParticles, + int nControlPointNumber, Vector vecPos, bool bBBoxOnly = false ) + { + return true; + } + + + virtual int GetRayTraceEnvironmentFromName( const char *pszRtEnvName ) + { + return 0; // == PRECIPITATION + } + + virtual int GetCollisionGroupFromName( const char *pszCollisionGroupName ) + { + return 0; // == COLLISION_GROUP_NONE + } + + virtual void GetRandomPointsOnControllingObjectHitBox( + CParticleCollection *pParticles, + int nControlPointNumber, + int nNumPtsOut, + float flBBoxScale, + int nNumTrysToGetAPointInsideTheModel, + Vector *pPntsOut, + Vector vecDirectionBias, + Vector *pHitBoxRelativeCoordOut = NULL, + int *pHitBoxIndexOut = NULL, + int nDesiredHitbox = -1 ) = 0; + + + virtual int GetControllingObjectHitBoxInfo( + CParticleCollection *pParticles, + int nControlPointNumber, + int nBufSize, // # of output slots available + ModelHitBoxInfo_t *pHitBoxOutputBuffer ) + { + // returns number of hit boxes output + return 0; + } + + virtual void GetControllingObjectOBBox( CParticleCollection *pParticles, + int nControlPointNumber, + Vector vecMin, Vector vecMax ) + { + vecMin = vecMax = vec3_origin; + } + + + // Traces Four Rays against a defined RayTraceEnvironment + virtual void TraceAgainstRayTraceEnv( + int envnumber, + const FourRays &rays, fltx4 TMin, fltx4 TMax, + RayTracingResult *rslt_out, int32 skip_id ) const = 0; + + virtual Vector GetLocalPlayerPos( void ) + { + return vec3_origin; + } + + virtual void GetLocalPlayerEyeVectors( Vector *pForward, Vector *pRight = NULL, Vector *pUp = NULL ) + { + *pForward = vec3_origin; + *pRight = vec3_origin; + *pUp = vec3_origin; + } + + virtual Vector GetCurrentViewOrigin() + { + return vec3_origin; + } + + virtual int GetActivityCount() = 0; + + virtual const char *GetActivityNameFromIndex( int nActivityIndex ) { return 0; } + + virtual float GetPixelVisibility( int *pQueryHandle, const Vector &vecOrigin, float flScale ) = 0; + + virtual void SetUpLightingEnvironment( const Vector& pos ) + { + } + + virtual void PreSimulate( ) = 0; + + virtual void PostSimulate( ) = 0; + + virtual void DebugDrawLine(const Vector& origin, const Vector& dest, int r, int g, int b,bool noDepthTest, float duration) = 0; + + virtual void *GetModel( char const *pMdlName ) { return NULL; } + + virtual void DrawModel( void *pModel, const matrix3x4_t &DrawMatrix, CParticleCollection *pParticles, int nParticleNumber, int nBodyPart, int nSubModel, + int nAnimationSequence = 0, float flAnimationRate = 30.0f, float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 1.0f ) = 0; + + virtual void BeginDrawModels( int nNumModels, Vector const &vecCenter, CParticleCollection *pParticles ) {} + + virtual void FinishDrawModels( CParticleCollection *pParticles ) {} + + virtual void UpdateProjectedTexture( const int nParticleID, IMaterial *pMaterial, Vector &vOrigin, float flRadius, float flRotation, float r, float g, float b, float a, void *&pUserVar ) = 0; +}; + + +//----------------------------------------------------------------------------- +// +// Particle system manager. Using a class because tools need it that way +// so the SFM and PET tools can share managers despite being linked to +// separate particle system .libs +// +//----------------------------------------------------------------------------- +typedef int ParticleSystemHandle_t; + +class CParticleSystemMgr +{ +public: + // Constructor, destructor + CParticleSystemMgr(); + ~CParticleSystemMgr(); + + // Initialize the particle system + bool Init( IParticleSystemQuery *pQuery, bool bAllowPrecache ); + + // methods to add builtin operators. If you don't call these at startup, you won't be able to sim or draw. These are done separately from Init, so that + // the server can omit the code needed for rendering/simulation, if desired. + void AddBuiltinSimulationOperators( void ); + void AddBuiltinRenderingOperators( void ); + + // Registration of known operators + void AddParticleOperator( ParticleFunctionType_t nOpType, IParticleOperatorDefinition *pOpFactory ); + + // Read a particle config file, add it to the list of particle configs + bool ReadParticleConfigFile( const char *pFileName, bool bPrecache, bool bDecommitTempMemory = true ); + bool ReadParticleConfigFile( CUtlBuffer &buf, bool bPrecache, bool bDecommitTempMemory = true, const char *pFileName = NULL ); + void DecommitTempMemory(); + + // For recording, write a specific particle system to a CUtlBuffer in DMX format + bool WriteParticleConfigFile( const char *pParticleSystemName, CUtlBuffer &buf, bool bPreventNameBasedLookup = false ); + bool WriteParticleConfigFile( const DmObjectId_t& id, CUtlBuffer &buf, bool bPreventNameBasedLookup = false ); + + // create a particle system by name. returns null if one of that name does not exist + CParticleCollection *CreateParticleCollection( const char *pParticleSystemName, float flDelay = 0.0f, int nRandomSeed = 0 ); + CParticleCollection *CreateParticleCollection( ParticleSystemHandle_t particleSystemName, float flDelay = 0.0f, int nRandomSeed = 0 ); + + // create a particle system given a particle system id + CParticleCollection *CreateParticleCollection( const DmObjectId_t &id, float flDelay = 0.0f, int nRandomSeed = 0 ); + + // Is a particular particle system defined? + bool IsParticleSystemDefined( const char *pParticleSystemName ); + bool IsParticleSystemDefined( const DmObjectId_t &id ); + + // Returns the index of the specified particle system. + ParticleSystemHandle_t GetParticleSystemIndex( const char *pParticleSystemName ); + ParticleSystemHandle_t FindOrAddParticleSystemIndex( const char *pParticleSystemName ); + + // Returns the name of the specified particle system. + const char *GetParticleSystemNameFromIndex( ParticleSystemHandle_t iIndex ); + + // Return the number of particle systems in our dictionary + int GetParticleSystemCount( void ); + + // Get the label for a filter + const char *GetFilterName( ParticleFilterType_t nFilterType ) const; + + // call to get available particle operator definitions + // NOTE: FUNCTION_CHILDREN will return a faked one, for ease of writing the editor + CUtlVector< IParticleOperatorDefinition *> &GetAvailableParticleOperatorList( ParticleFunctionType_t nWhichList ); + + void GetParticleSystemsInFile( const char *pFileName, CUtlVector *pOutSystemNameList ); + void GetParticleSystemsInBuffer( CUtlBuffer &buf, CUtlVector *pOutSystemNameList ); + + // Returns the unpack structure for a particle system definition + const DmxElementUnpackStructure_t *GetParticleSystemDefinitionUnpackStructure(); + + // Particle sheet management + void ShouldLoadSheets( bool bLoadSheets ); + CSheet *FindOrLoadSheet( CParticleSystemDefinition *pDef ); + void FlushAllSheets( void ); + + // Render cache used to render opaque particle collections + void ResetRenderCache( void ); + void AddToRenderCache( CParticleCollection *pParticles ); + void DrawRenderCache( bool bShadowDepth ); + + IParticleSystemQuery *Query( void ) { return m_pQuery; } + + // return the particle field name + const char* GetParticleFieldName( int nParticleField ) const; + + // WARNING: the pointer returned by this function may be invalidated + // *at any time* by the editor, so do not ever cache it. + CParticleSystemDefinition* FindParticleSystem( const char *pName ); + CParticleSystemDefinition* FindParticleSystem( const DmObjectId_t& id ); + CParticleSystemDefinition* FindParticleSystem( ParticleSystemHandle_t hParticleSystem ); + CParticleSystemDefinition* FindPrecachedParticleSystem( int nPrecacheIndex ); + + // Method for overriding a parameter in a loaded particle system definition + bool OverrideEffectParameter( const char *pParticleSystemName, const char *pOperatorName, const char *pParameterName, const char *pParameterValue ); + + + void CommitProfileInformation( bool bCommit ); // call after simulation, if you want + // sim time recorded. if oyu pass + // flase, info will be thrown away and + // uncomitted time reset. Having this + // function lets you only record + // profile data for slow frames if + // desired. + + + void DumpProfileInformation( void ); // write particle_profile.csv + + void DumpParticleList( const char *pNameSubstring ); + + // Cache/uncache materials used by particle systems + void PrecacheParticleSystem( int nStringNumber, const char *pName ); + void UncacheAllParticleSystems(); + + + // Sets the last simulation time, used for particle system sleeping logic + void SetLastSimulationTime( float flTime ); + float GetLastSimulationTime() const; + + // Sets the last simulation duration ( the amount of time we spent simulating particle ) last frame + // Used to fallback to cheaper particle systems under load + void SetLastSimulationDuration( float flDuration ); + float GetLastSimulationDuration() const; + + void SetFallbackParameters( float flBase, float flMultiplier, float flSimFallbackBaseMultiplier, float flSimThresholdMs ); + float GetFallbackBase() const; + float GetFallbackMultiplier() const; + float GetSimFallbackThresholdMs() const; + float GetSimFallbackBaseMultiplier() const; + + void SetSystemLevel( int nCPULevel, int nGPULevel ); + int GetParticleCPULevel() const; + int GetParticleGPULevel() const; + + void LevelShutdown( void ); // called at level unload time + + + void FrameUpdate( void ); // call this once per frame on main thread + + // Particle attribute query funcs + int GetParticleAttributeByName( const char *pAttribute ) const; // SLOW! returns -1 on error + const char *GetParticleAttributeName( int nAttribute ) const; // returns 'unknown' on error + EAttributeDataType GetParticleAttributeDataType( int nAttribute ) const; + +private: + struct RenderCache_t + { + IMaterial *m_pMaterial; + CUtlVector< CParticleCollection * > m_ParticleCollections; + }; + + struct BatchStep_t + { + CParticleCollection *m_pParticles; + CParticleOperatorInstance *m_pRenderer; + void *m_pContext; + int m_nFirstParticle; + int m_nParticleCount; + int m_nVertCount; + }; + + struct Batch_t + { + int m_nVertCount; + int m_nIndexCount; + CUtlVector< BatchStep_t > m_BatchStep; + }; + + struct ParticleAttribute_t + { + EAttributeDataType nDataType; + const char *pName; + }; + + // Unserialization-related methods + bool ReadParticleDefinitions( CUtlBuffer &buf, const char *pFileName, bool bPrecache, bool bDecommitTempMemory ); + void AddParticleSystem( CDmxElement *pParticleSystem ); + + // Serialization-related methods + CDmxElement *CreateParticleDmxElement( const DmObjectId_t &id ); + CDmxElement *CreateParticleDmxElement( const char *pParticleSystemName ); + + bool WriteParticleConfigFile( CDmxElement *pParticleSystem, CUtlBuffer &buf, bool bPreventNameBasedLookup ); + + // Builds a list of batches to render + void BuildBatchList( int iRenderCache, IMatRenderContext *pRenderContext, CUtlVector< Batch_t >& batches ); + + // Known operators + CUtlVector m_ParticleOperators[PARTICLE_FUNCTION_COUNT]; + + // Particle system dictionary + CParticleSystemDictionary *m_pParticleSystemDictionary; + + // typedef CUtlMap< ITexture *, CSheet* > SheetsCache; + typedef CUtlStringMap< CSheet* > SheetsCache_t; + SheetsCache_t m_SheetList; + + // attaching and dtaching killlists. when simulating, a particle system gets a kill list. after + // simulating, the memory for that will be used for the next particle system. This matters for + // threaded particles, because we don't want to share the same kill list between simultaneously + // simulating particle systems. + void AttachKillList( CParticleCollection *pParticles); + void DetachKillList( CParticleCollection *pParticles); + + // Set up s_AttributeTable + void InitAttributeTable( void ); + + // For visualization (currently can only visualize one operator at a time) + CParticleCollection *m_pVisualizedParticles; + DmObjectId_t m_VisualizedOperatorId; + IParticleSystemQuery *m_pQuery; + CUtlVector< RenderCache_t > m_RenderCache; + IMaterial *m_pShadowDepthMaterial; + float m_flLastSimulationTime; + float m_flLastSimulationDuration; + + CUtlVector< ParticleSystemHandle_t > m_PrecacheLookup; + CUtlVector< ParticleSystemHandle_t > m_ClientPrecacheLookup; + + bool m_bDidInit; + bool m_bUsingDefaultQuery; + bool m_bShouldLoadSheets; + bool m_bAllowPrecache; + + int m_nNumFramesMeasured; + + float m_flFallbackBase; + float m_flFallbackMultiplier; + float m_flSimFallbackBaseMultiplier; + float m_flSimThresholdMs; + + int m_nCPULevel; + int m_nGPULevel; + + static ParticleAttribute_t s_AttributeTable[MAX_PARTICLE_ATTRIBUTES]; + + friend class CParticleSystemDefinition; + friend class CParticleCollection; +}; + +extern CParticleSystemMgr *g_pParticleSystemMgr; + + +//----------------------------------------------------------------------------- +// A particle system can only have 1 operator using a particular ID +//----------------------------------------------------------------------------- +enum ParticleOperatorId_t +{ + // Generic IDs + OPERATOR_GENERIC = -2, // Can have as many of these as you want + OPERATOR_SINGLETON = -1, // Can only have 1 operator with the same name as this one + + // Renderer operator IDs + + // Operator IDs + + // Initializer operator IDs + OPERATOR_PI_POSITION, // Particle initializer: position (can only have 1 position setter) + OPERATOR_PI_RADIUS, + OPERATOR_PI_ALPHA, + OPERATOR_PI_TINT_RGB, + OPERATOR_PI_ROTATION, + OPERATOR_PI_YAW, + + // Emitter IDs + + OPERATOR_ID_COUNT, +}; + + +//----------------------------------------------------------------------------- +// Class factory for particle operators +//----------------------------------------------------------------------------- +class IParticleOperatorDefinition +{ +public: + virtual const char *GetName() const = 0; + virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const = 0; +// virtual void DestroyInstance( CParticleOperatorInstance *pInstance ) const = 0; + virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const = 0; + virtual ParticleOperatorId_t GetId() const = 0; + virtual uint32 GetFilter() const = 0; + virtual bool IsObsolete() const = 0; + +#if MEASURE_PARTICLE_PERF + // performance monitoring + float m_flMaxExecutionTime; + float m_flTotalExecutionTime; + float m_flUncomittedTime; + + FORCEINLINE void RecordExecutionTime( float flETime ) + { + m_flUncomittedTime += flETime; + m_flMaxExecutionTime = MAX( m_flMaxExecutionTime, flETime ); + } + + FORCEINLINE float TotalRecordedExecutionTime( void ) const + { + return m_flTotalExecutionTime; + } + + FORCEINLINE float MaximumRecordedExecutionTime( void ) const + { + return m_flMaxExecutionTime; + } +#else + FORCEINLINE void RecordExecutionTime( float flETime ) + { + } +#endif +}; + + +//----------------------------------------------------------------------------- +// Particle operators +//----------------------------------------------------------------------------- +class CParticleOperatorInstance +{ +public: + // custom allocators so we can be simd aligned + void *operator new( size_t nSize ); + void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ); + void operator delete( void *pData ); + void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ); + + // unpack structure will be applied by creator. add extra initialization needed here + virtual void InitParams( CParticleSystemDefinition *pDef ) + { + } + + virtual size_t GetRequiredContextBytes( ) const + { + return 0; + } + + virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const + { + } + + virtual uint32 GetWrittenAttributes( void ) const = 0; + virtual uint32 GetReadAttributes( void ) const = 0; + + virtual uint64 GetReadControlPointMask() const + { + return 0; + } + + virtual uint32 GetFilter( void ) const + { + uint32 filter = 0; + uint32 wrAttrib = GetWrittenAttributes(); + + if (wrAttrib & ATTRIBUTES_WHICH_ARE_POSITION_AND_VELOCITY) + { + filter = filter | FILTER_POSITION_AND_VELOCITY_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_LIFE_DURATION) + { + filter = filter | FILTER_LIFE_DURATION_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_ROTATION) + { + filter = filter | FILTER_ROTATION_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_SIZE) + { + filter = filter | FILTER_SIZE_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_COLOR_AND_OPACITY) + { + filter = filter | FILTER_COLOR_AND_OPACITY_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_ANIMATION_SEQUENCE) + { + filter = filter | FILTER_ANIMATION_SEQUENCE_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_HITBOX) + { + filter = filter | FILTER_HITBOX_MASK; + } + if (wrAttrib & ATTRIBUTES_WHICH_ARE_NORMAL) + { + filter = filter | FILTER_NORMAL_MASK; + } + + return filter; + } + + + // these control points are NOT positions or matrices (ie don't try to transform them) + virtual uint64 GetNonPositionalControlPointMask() const + { + return 0; + } + + // Used when an operator needs to read the attributes of a particle at spawn time + virtual uint32 GetReadInitialAttributes( void ) const + { + return 0; + } + + // a particle simulator does this + virtual void Operate( CParticleCollection *pParticles, float flOpStrength, void *pContext ) const + { + } + + virtual void PostSimulate( CParticleCollection *pParticles, void *pContext ) const + { + } + + // a renderer overrides this + virtual void Render( IMatRenderContext *pRenderContext, + CParticleCollection *pParticles, const Vector4D &vecDiffuseModulation, void *pContext ) const + { + } + + virtual bool IsBatchable() const + { + return true; + } + + virtual bool IsOrderImportant() const + { + return false; + } + + virtual bool ShouldRun( bool bApplyingParentKillList ) const + { + return !bApplyingParentKillList; + } + + virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const + { + } + + // Returns the number of verts + indices to render + virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const + { + *pVertsUsed = 0; + *pIndicesUsed = 0; + return 0; + } + + + // emitters over-ride this. Return a mask of what fields you initted + virtual uint32 Emit( CParticleCollection *pParticles, float flOpCurStrength, + void *pContext ) const + { + return 0; + } + + // emitters over-ride this. + virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly = false ) const + { + } + virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly = false ) const + { + } + virtual void Restart( CParticleCollection *pParticles, void *pContext ) {} + + // initters over-ride this + virtual void InitParticleSystem( CParticleCollection *pParticles, void *pContext ) const + { + } + + + // a force generator does this. It accumulates in the force array + virtual void AddForces( FourVectors *AccumulatedForces, + CParticleCollection *pParticles, + int nBlocks, + float flCurStrength, + void *pContext ) const + { + } + + + // this is called for each constarint every frame. It can set up data like nearby world traces, + // etc + virtual void SetupConstraintPerFrameData( CParticleCollection *pParticles, + void *pContext ) const + { + } + + + // a constraint overrides this. It shold return a true if it did anything + virtual bool EnforceConstraint( int nStartBlock, + int nNumBlocks, + CParticleCollection *pParticles, + void *pContext, + int nNumValidParticlesInLastChunk ) const + { + return false; + } + + // should the constraint be run only once after all other constraints? + virtual bool IsFinalConstaint( void ) const + { + return false; + } + + // determines if a mask needs to be initialized multiple times. + virtual bool InitMultipleOverride() + { + return false; + } + + + // Indicates if this initializer is scrub-safe (initializers don't use random numbers, for example) + virtual bool IsScrubSafe() + { + return false; + } + + // particle-initters over-ride this + virtual void InitNewParticlesScalar( CParticleCollection *pParticles, int nFirstParticle, int n_particles, int attribute_write_mask, void *pContext ) const + { + } + + // init new particles in blocks of 4. initters that have sse smarts should over ride this. the scalar particle initter will still be cllaed for head/tail. + virtual void InitNewParticlesBlock( CParticleCollection *pParticles, int start_block, int n_blocks, int attribute_write_mask, void *pContext ) const + { + // default behaviour is to call the scalar one 4x times + InitNewParticlesScalar( pParticles, 4*start_block, 4*n_blocks, attribute_write_mask, pContext ); + } + + // splits particle initialization up into scalar and block sections, callingt he right code + void InitNewParticles( CParticleCollection *pParticles, int nFirstParticle, int n_particles, int attribute_write_mask , void *pContext) const; + + + // this function is queried to determine if a particle system is over and doen with. A particle + // system is done with when it has noparticles and no operators intend to create any more + virtual bool MayCreateMoreParticles( CParticleCollection const *pParticles, void *pContext ) const + { + return false; + } + + // Returns the operator definition that spawned this operator + const IParticleOperatorDefinition *GetDefinition() + { + return m_pDef; + } + + virtual bool ShouldRunBeforeEmitters( void ) const + { + return false; + } + + // Called when the SFM wants to skip forward in time + virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const {} + + // Returns a unique ID for this definition + const DmObjectId_t& GetId() { return m_Id; } + + // Used for editing + debugging to visualize the operator in 3D + virtual void Render( CParticleCollection *pParticles ) const {} + + // Used as a debugging mechanism to prevent bogus calls to RandomInt or RandomFloat inside operators + // Use CParticleCollection::RandomInt/RandomFloat instead + int RandomInt( int nMin, int nMax ) + { + // NOTE: Use CParticleCollection::RandomInt! + Assert(0); + return 0; + } + + float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ) + { + // NOTE: Use CParticleCollection::RandomFloat! + Assert(0); + return 0.0f; + } + + float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ) + { + // NOTE: Use CParticleCollection::RandomFloatExp! + Assert(0); + return 0.0f; + } + + float m_flOpStartFadeInTime; + float m_flOpEndFadeInTime; + float m_flOpStartFadeOutTime; + float m_flOpEndFadeOutTime; + float m_flOpFadeOscillatePeriod; + + float m_flOpTimeOffsetMin; + float m_flOpTimeOffsetMax; + int m_nOpTimeOffsetSeed; + + int m_nOpStrengthScaleSeed; + float m_flOpStrengthMinScale; + float m_flOpStrengthMaxScale; + + int m_nOpTimeScaleSeed; + float m_flOpTimeScaleMin; + float m_flOpTimeScaleMax; + + bool m_bStrengthFastPath; // set for operators which just always have strengh = 0 + + int m_nOpEndCapState; + + virtual void Precache( void ) + { + } + + virtual void Uncache( void ) + { + } + + + + virtual ~CParticleOperatorInstance( void ) + { + // so that sheet references, etc can be cleaned up + } + +protected: + // utility function for initting a scalar attribute to a random range in an sse fashion + void InitScalarAttributeRandomRangeExpBlock( int nAttributeId, float fMinValue, float fMaxValue, float fExp, + CParticleCollection *pParticles, int nStartBlock, int nBlockCount ) const; + void AddScalarAttributeRandomRangeBlock( int nAttributeId, float fMinValue, float fMaxValue, float fExp, + CParticleCollection *pParticles, int nStartBlock, int nBlockCount, bool bRandomlyInvert ) const; + + void InitScalarAttributeRandomRangeExpScalar( int nAttributeId, float fMinValue, float fMaxValue, float fExp, + CParticleCollection *pParticles, int nStartParticle, int nParticleCount ) const; + + void CheckForFastPath( void ); // call at operator init time + + // utility funcs to access CParticleCollection data: + bool HasAttribute( CParticleCollection *pParticles, int nAttribute ) const; + KillListItem_t *GetParentKillList( CParticleCollection *pParticles, int &nNumParticlesToKill ) const; + +private: + friend class CParticleCollection; + friend class CParticleSystemDefinition; + friend class CParticleSystemMgr; + + const IParticleOperatorDefinition *m_pDef; + void SetDefinition( const IParticleOperatorDefinition * pDef, const DmObjectId_t &id ) + { + m_pDef = pDef; + CopyUniqueId( id, &m_Id ); + } + + DmObjectId_t m_Id; + + template friend class CParticleOperatorDefinition; +}; + +class CParticleInitializerOperatorInstance : public CParticleOperatorInstance +{ +public: + + virtual bool ShouldRun( bool bApplyingParentKillList ) const + { + return ( !bApplyingParentKillList ) || m_bRunForParentApplyKillList; + } + + bool m_bRunForParentApplyKillList; +}; + +class CParticleRenderOperatorInstance : public CParticleOperatorInstance +{ +public: + + CParticleVisibilityInputs VisibilityInputs; +}; + +//----------------------------------------------------------------------------- +// Helper macro for creating particle operator factories +//----------------------------------------------------------------------------- +template < class T > +class CParticleOperatorDefinition : public IParticleOperatorDefinition +{ +public: + CParticleOperatorDefinition( const char *pFactoryName, ParticleOperatorId_t id, bool bIsObsolete ) : m_pFactoryName( pFactoryName ), m_Id( id ) + { +#if MEASURE_PARTICLE_PERF + m_flTotalExecutionTime = 0.0f; + m_flMaxExecutionTime = 0.0f; + m_flUncomittedTime = 0.0f; +#endif + m_bIsObsolete = bIsObsolete; + } + + virtual const char *GetName() const + { + return m_pFactoryName; + } + + virtual ParticleOperatorId_t GetId() const + { + return m_Id; + } + + virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const + { + CParticleOperatorInstance *pOp = new T; + pOp->SetDefinition( this, id ); + return pOp; + } + + virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const + { + return m_pUnpackParams; + } + + // Editor won't display obsolete operators + virtual bool IsObsolete() const + { + return m_bIsObsolete; + } + + virtual uint32 GetFilter() const { T temp; return temp.GetFilter(); } + +private: + const char *m_pFactoryName; + ParticleOperatorId_t m_Id; + bool m_bIsObsolete; + static DmxElementUnpackStructure_t *m_pUnpackParams; +}; + +#define DECLARE_PARTICLE_OPERATOR( _className ) \ + DECLARE_DMXELEMENT_UNPACK() \ + friend class CParticleOperatorDefinition<_className > + +#define DEFINE_PARTICLE_OPERATOR( _className, _operatorName, _id ) \ + static CParticleOperatorDefinition<_className> s_##_className##Factory( _operatorName, _id, false ) + +#define DEFINE_PARTICLE_OPERATOR_OBSOLETE( _className, _operatorName, _id ) \ + static CParticleOperatorDefinition<_className> s_##_className##Factory( _operatorName, _id, true ) + +#define BEGIN_PARTICLE_OPERATOR_UNPACK( _className ) \ + BEGIN_DMXELEMENT_UNPACK( _className ) \ + DMXELEMENT_UNPACK_FIELD( "operator start fadein","0", float, m_flOpStartFadeInTime ) \ + DMXELEMENT_UNPACK_FIELD( "operator end fadein","0", float, m_flOpEndFadeInTime ) \ + DMXELEMENT_UNPACK_FIELD( "operator start fadeout","0", float, m_flOpStartFadeOutTime ) \ + DMXELEMENT_UNPACK_FIELD( "operator end fadeout","0", float, m_flOpEndFadeOutTime ) \ + DMXELEMENT_UNPACK_FIELD( "operator fade oscillate","0", float, m_flOpFadeOscillatePeriod ) \ + DMXELEMENT_UNPACK_FIELD( "operator time offset seed","0", int, m_nOpTimeOffsetSeed ) \ + DMXELEMENT_UNPACK_FIELD( "operator time offset min","0", float, m_flOpTimeOffsetMin ) \ + DMXELEMENT_UNPACK_FIELD( "operator time offset max","0", float, m_flOpTimeOffsetMax ) \ + DMXELEMENT_UNPACK_FIELD( "operator time scale seed","0", int, m_nOpTimeScaleSeed ) \ + DMXELEMENT_UNPACK_FIELD( "operator time scale min","1", float, m_flOpTimeScaleMin ) \ + DMXELEMENT_UNPACK_FIELD( "operator time scale max","1", float, m_flOpTimeScaleMax ) \ + DMXELEMENT_UNPACK_FIELD( "operator time strength random scale max", "1", float, m_flOpStrengthMaxScale ) \ + DMXELEMENT_UNPACK_FIELD( "operator strength scale seed","0", int, m_nOpStrengthScaleSeed ) \ + DMXELEMENT_UNPACK_FIELD( "operator strength random scale min", "1", float, m_flOpStrengthMinScale ) \ + DMXELEMENT_UNPACK_FIELD( "operator strength random scale max", "1", float, m_flOpStrengthMaxScale ) \ + DMXELEMENT_UNPACK_FIELD( "operator end cap state", "-1", int, m_nOpEndCapState ) +#define END_PARTICLE_OPERATOR_UNPACK( _className ) \ + END_DMXELEMENT_UNPACK_TEMPLATE( _className, CParticleOperatorDefinition<_className>::m_pUnpackParams ) + +#define BEGIN_PARTICLE_INITIALIZER_OPERATOR_UNPACK( _className ) \ + BEGIN_PARTICLE_OPERATOR_UNPACK( _className ) \ + DMXELEMENT_UNPACK_FIELD( "run for killed parent particles", "1", bool, m_bRunForParentApplyKillList ) + +#define BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( _className ) \ + BEGIN_PARTICLE_OPERATOR_UNPACK( _className ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Proxy Input Control Point Number", "-1", int, VisibilityInputs.m_nCPin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Proxy Radius", "1.0", float, VisibilityInputs.m_flProxyRadius ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input minimum","0", float, VisibilityInputs.m_flInputMin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input maximum","1", float, VisibilityInputs.m_flInputMax ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input dot minimum","0", float, VisibilityInputs.m_flDotInputMin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input dot maximum","0", float, VisibilityInputs.m_flDotInputMax ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input distance minimum","0", float, VisibilityInputs.m_flDistanceInputMin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility input distance maximum","0", float, VisibilityInputs.m_flDistanceInputMax ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Alpha Scale minimum","0", float, VisibilityInputs.m_flAlphaScaleMin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Alpha Scale maximum","1", float, VisibilityInputs.m_flAlphaScaleMax ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Radius Scale minimum","1", float, VisibilityInputs.m_flRadiusScaleMin ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Radius Scale maximum","1", float, VisibilityInputs.m_flRadiusScaleMax ) \ + DMXELEMENT_UNPACK_FIELD( "Visibility Radius FOV Scale base","0", float, VisibilityInputs.m_flRadiusScaleFOVBase ) +#define REGISTER_PARTICLE_OPERATOR( _type, _className ) \ + g_pParticleSystemMgr->AddParticleOperator( _type, &s_##_className##Factory ) + +// need to think about particle constraints in terms of segregating affected particles so as to +// run multi-pass constraints on only a subset + + +//----------------------------------------------------------------------------- +// flags for particle systems +//----------------------------------------------------------------------------- +enum +{ + PCFLAGS_FIRST_FRAME = 0x1, + PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED = 0x2, +}; + + + +#define DEBUG_PARTICLE_SORT 0 + +//------------------------------------------------------------------------------ +// particle render helpers +//------------------------------------------------------------------------------ +struct CParticleVisibilityData +{ + float m_flAlphaVisibility; + float m_flRadiusVisibility; + bool m_bUseVisibility; +}; +// sorting functionality for rendering. Call GetRenderList( bool bSorted ) to get the list of +// particles to render (sorted or not, including children). +// **do not casually change this structure**. The sorting code treats it interchangably as an SOA +// and accesses it using sse. Any changes to this struct need the sort code updated.** +struct ParticleRenderData_t +{ + float m_flSortKey; // what we sort by + int m_nIndex; // index or fudged index (for child particles) + float m_flRadius; // effective radius, using visibility +#if PLAT_LITTLE_ENDIAN + uint8 m_nAlpha; // effective alpha, combining alpha and alpha2 and vis. 0 - 255 + uint8 m_nAlphaPad[3]; // this will be written to +#endif +#if PLAT_BIG_ENDIAN + uint8 m_nAlphaPad[3]; // this will be written to + uint8 m_nAlpha; // effective alpha, combining alpha and alpha2 and vis. 0 - 255 +#endif +}; + +struct ExtendedParticleRenderData_t : ParticleRenderData_t +{ + float m_flX; + float m_flY; + float m_flZ; + float m_flPad; +}; + + +typedef struct ALIGN16 _FourInts +{ + int32 m_nValue[4]; +} ALIGN16_POST FourInts; + + +struct ParticleBaseRenderData_SIMD_View +{ + fltx4 m_fl4SortKey; + FourVectors m_fl4XYZ; + fltx4 m_fl4Alpha; + fltx4 m_fl4Red; + fltx4 m_fl4Green; + fltx4 m_fl4Blue; + fltx4 m_fl4Radius; + fltx4 m_fl4AnimationTimeValue; + fltx4 m_fl4SequenceID; // 56 bytes per particle +}; + +struct ParticleFullRenderData_SIMD_View : public ParticleBaseRenderData_SIMD_View +{ + fltx4 m_fl4Rotation; + fltx4 m_fl4Yaw; + + // no-op operation so templates can compile + FORCEINLINE void SetARGB2( fltx4 const &fl4Red, fltx4 const &fl4Green, fltx4 const &fl4Blue, fltx4 const &fl4Alpha ) + { + } + + FORCEINLINE void SetNormal( fltx4 const &fl4NormalX, fltx4 const &fl4NormalY, fltx4 const &fl4NormalZ ) + { + } +}; + +struct ParticleRenderDataWithOutlineInformation_SIMD_View : public ParticleFullRenderData_SIMD_View +{ + FourVectors m_v4Color2; + fltx4 m_fl4Alpha2; + + FORCEINLINE void SetARGB2( fltx4 const &fl4Red, fltx4 const &fl4Green, fltx4 const &fl4Blue, fltx4 const &fl4Alpha ) + { + m_v4Color2.x = fl4Red; + m_v4Color2.y = fl4Green; + m_v4Color2.z = fl4Blue; + m_fl4Alpha2 = fl4Alpha; + } +}; + +struct ParticleRenderDataWithNormal_SIMD_View : public ParticleFullRenderData_SIMD_View +{ + FourVectors m_v4Normal; + + FORCEINLINE void SetNormal( fltx4 const &fl4NormalX, fltx4 const &fl4NormalY, fltx4 const &fl4NormalZ ) + { + m_v4Normal.x = fl4NormalX; + m_v4Normal.y = fl4NormalY; + m_v4Normal.z = fl4NormalZ; + } + +}; + + + +// definitions for byte fields ( colors ). endian-ness matters here. +#if PLAT_LITTLE_ENDIAN +#define BYTE_FIELD(x) \ + uint8 x; \ + uint8 x##_Pad[3 + 3 * 4 ]; +#endif + +#if PLAT_BIG_ENDIAN +#define BYTE_FIELD(x) \ + uint8 x##_Pad0[3]; \ + uint8 x; \ + uint8 x##_Pad[3 * 4 ]; +#endif + +#define FLOAT_FIELD( x ) \ + float x; \ + float x##_Pad[3]; + +struct ParticleBaseRenderData_Scalar_View +{ + int32 m_nSortKey; + int32 m_nPad00[3]; + FLOAT_FIELD( m_flX ); + FLOAT_FIELD( m_flY ); + FLOAT_FIELD( m_flZ ); + + BYTE_FIELD( m_nAlpha ); + BYTE_FIELD( m_nRed ); + BYTE_FIELD( m_nGreen ); + BYTE_FIELD( m_nBlue ); + + FLOAT_FIELD( m_flRadius ); + FLOAT_FIELD( m_flAnimationTimeValue ); +#if PLAT_LITTLE_ENDIAN + uint8 m_nSequenceID; + uint8 m_nSequenceID1; + uint8 m_nPadSequence[2 + 3 * 4]; +#endif +#if PLAT_BIG_ENDIAN + uint8 m_nPadSequence[2]; + uint8 m_nSequenceID1; + uint8 m_nSequenceID; + uint8 m_nPadSequence1[ 3 * 4]; +#endif +}; +struct ParticleFullRenderData_Scalar_View : public ParticleBaseRenderData_Scalar_View +{ + FLOAT_FIELD( m_flRotation ); + FLOAT_FIELD( m_flYaw ); + + float Red2( void ) const { return 1.0; } + float Green2( void ) const { return 1.0; } + float Blue2( void ) const { return 1.0; } + float Alpha2( void ) const { return 1.0; } + + float NormalX( void ) const { return 1.0; } + float NormalY( void ) const { return 1.0; } + float NormalZ( void ) const { return 1.0; } +}; + +struct ParticleRenderDataWithOutlineInformation_Scalar_View : public ParticleFullRenderData_Scalar_View +{ + FLOAT_FIELD( m_flRed2 ); + FLOAT_FIELD( m_flGreen2 ); + FLOAT_FIELD( m_flBlue2 ); + FLOAT_FIELD( m_flAlpha2 ); + + float Red2( void ) const { return m_flRed2; } + float Green2( void ) const { return m_flGreen2; } + float Blue2( void ) const { return m_flBlue2; } + float Alpha2( void ) const { return m_flAlpha2; } +}; + +struct ParticleRenderDataWithNormal_Scalar_View : public ParticleFullRenderData_Scalar_View +{ + FLOAT_FIELD( m_flNormalX ); + FLOAT_FIELD( m_flNormalY ); + FLOAT_FIELD( m_flNormalZ ); + + float NormalX( void ) const { return m_flNormalX; } + float NormalY( void ) const { return m_flNormalY; } + float NormalZ( void ) const { return m_flNormalZ; } + +}; + + + +ParticleFullRenderData_Scalar_View **GetExtendedRenderList( CParticleCollection *pParticles, + IMatRenderContext *pRenderContext, + bool bSorted, int *pNparticles, + CParticleVisibilityData *pVisibilityData); + + +ParticleRenderDataWithOutlineInformation_Scalar_View **GetExtendedRenderListWithPerParticleGlow( + CParticleCollection *pParticles, + IMatRenderContext *pRenderContext, + bool bSorted, int *pNparticles, + CParticleVisibilityData *pVisibilityData ); + + +ParticleRenderDataWithNormal_Scalar_View **GetExtendedRenderListWithNormals( + CParticleCollection *pParticles, + IMatRenderContext *pRenderContext, + bool bSorted, int *pNparticles, + CParticleVisibilityData *pVisibilityData ); + +// returns # of particles +int GenerateExtendedSortedIndexList( Vector vecCamera, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, + CParticleCollection *pParticles, bool bSorted, void *pOutBuf, + ParticleFullRenderData_Scalar_View **pParticlePtrs ); + + +//------------------------------------------------------------------------------ +// CParticleSnapshot wraps a CSOAContainer, so that a particle system +// can write to it or read from it (e.g. this can be attached to a control point). +//------------------------------------------------------------------------------ +class CParticleSnapshot +{ + DECLARE_DMXELEMENT_UNPACK(); + +public: + CParticleSnapshot() { Purge(); } + ~CParticleSnapshot() { Purge(); } + + struct AttributeMap + { + AttributeMap( int nContainerAttribute, int nParticleAttribute ) : m_nContainerAttribute( nContainerAttribute ), m_nParticleAttribute( nParticleAttribute ) {} + int m_nContainerAttribute, m_nParticleAttribute; + }; + typedef CUtlVector< AttributeMap > AttributeMapVector; + + // Has the CParticleSnapshot been fully initialized? + bool IsValid( void ) { return !!m_pContainer; } + + // Initialize from a .psf DMX file: + bool Unserialize( const char *pFullPath ); + // Serialize to a .psf DMX file (NOTE: external containers will serialize out fine, but won't be external when read back in) + bool Serialize( const char *pFullPath, bool bTextMode = true ); // TODO: once this stabilizes, switch to binary mode + + // Initialize by creating a new container, with a specified attribute mapping (will clear out old data if it exists) + // - each map entry specifies container attribute and associated particle attribute + // - the mapping should be one-to-one (container attributes are 'labeled' with particle attributes), + // so each particle attribute and container field should be used *AT MOST* once + // - NOTE: many-to-one and one-to-many mappings may be implemented by particle read/write operators + bool Init( int nX, int nY, int nZ, const AttributeMapVector &attributeMaps ); + // Same as the other Init, except the attribute mapping are specified with varargs instead of a vector + // (pairs of int params specify container attribute and associated particle attribute, terminated by -1) + bool Init( int nX, int nY, int nZ, ... ); + + // Initialize by wrapping a pre-existing container, with a specified attribute mapping + // - same conditions/parameters as above + // - existing container attributes are expected to match the particle attribute data types + // - if a new attribute is added to the external container, call InitExternal again to update the snapshot + bool InitExternal( CSOAContainer *pContainer, const AttributeMapVector &attributeMaps ); + bool InitExternal( CSOAContainer *pContainer, ... ); + + // Clear the snapshot (and container) back to its initial state + void Purge( void ); + + // This provides READ-ONLY access to the snapshot's container (all write accessors should have wrappers here, to ensure + // that the container's set of attributes is not modified, which would invalidate the snapshot attribute mapping table) + const CSOAContainer *GetContainer( void ) { return m_pContainer; } + + + // Does the snapshot have data (of the appropriate type) for the given particle attribute? + bool HasAttribute( int nParticleAttribute, EAttributeDataType nDataType ) const + { + Assert( ( nParticleAttribute >= 0 ) && ( nParticleAttribute < MAX_PARTICLE_ATTRIBUTES ) ); + int nContainerIndex = m_ParticleAttributeToContainerAttribute[ nParticleAttribute ]; + return ( ( nContainerIndex != -1 ) && ( m_pContainer->GetAttributeType( nContainerIndex ) == nDataType ) ); + } + + // ---------- Wrappers for CSOAContainer members ---------- + int NumCols( void ) { return m_pContainer->NumCols(); } + int NumRows( void ) { return m_pContainer->NumRows(); } + int NumSlices( void ) { return m_pContainer->NumSlices(); } + // Read data from the container for the given particle attribute, at index (nIndex,0,0) + template T *ElementPointer( int nParticleAttribute, int nX = 0, int nY = 0, int nZ = 0 ) const + { + Assert( ( nParticleAttribute >= 0 ) && ( nParticleAttribute < MAX_PARTICLE_ATTRIBUTES ) ); + Assert( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != -1 ); + return m_pContainer->ElementPointer( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ], nX, nY, nZ ); + } + FourVectors *ElementPointer4V( int nParticleAttribute, int nX = 0, int nY = 0, int nZ = 0 ) const + { + Assert( ( nParticleAttribute >= 0 ) && ( nParticleAttribute < MAX_PARTICLE_ATTRIBUTES ) ); + Assert( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != -1 ); + return m_pContainer->ElementPointer4V( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ], nX, nY, nZ ); + } + // ---------- Wrappers for CSOAContainer members ---------- + + +private: + + static const int PARTICLE_SNAPSHOT_DMX_VERSION = 1; + + // Whether we're using an external container (as opposed to our embedded one) + bool UsingExternalContainer( void ) { return ( m_pContainer && ( m_pContainer != &m_Container ) ); } + + // Utility function used by the Init methods to add+validate an attribute mapping pair: + bool AddAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc ); + // Utility function used by the Init methods to validate data types for an attribute mapping pair: + bool ValidateAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc ); + + // Utility function to validate the embedded container after it is unserialized + bool EmbeddedContainerIsValid( void ); + + // Check whether the particle system's defined attributes have been changed (this won't compile if they have), so we can update the serialization code if need be + void CheckParticleAttributesForChanges( void ); + + + CSOAContainer m_Container; // Embedded container + CSOAContainer * m_pContainer; // Pointer either to the embedded container or an external one + + // For each particle attribute, this contains the index of the corresponding container attribute (-1 means 'none') + int m_ParticleAttributeToContainerAttribute[ MAX_PARTICLE_ATTRIBUTES ]; + int m_ContainerAttributeToParticleAttribute[ MAX_SOA_FIELDS ]; // Reverse mapping (used for error-checking) +}; + + +//------------------------------------------------------------------------------ +// structure describing the parameter block used by operators which use the path between two points to +// control particles. +//------------------------------------------------------------------------------ +struct CPathParameters +{ + int m_nStartControlPointNumber; + int m_nEndControlPointNumber; + int m_nBulgeControl; + float m_flBulge; + float m_flMidPoint; + + void ClampControlPointIndices( void ) + { + m_nStartControlPointNumber = MAX(0, MIN( MAX_PARTICLE_CONTROL_POINTS-1, m_nStartControlPointNumber ) ); + m_nEndControlPointNumber = MAX(0, MIN( MAX_PARTICLE_CONTROL_POINTS-1, m_nEndControlPointNumber ) ); + } +}; + + +struct CParticleControlPoint +{ + Vector m_Position; + Vector m_PrevPosition; + + // orientation + Vector m_ForwardVector; + Vector m_UpVector; + Vector m_RightVector; + + // reference to entity or whatever this control point comes from + void *m_pObject; + + // parent for hierarchies + int m_nParent; + + // CParticleSnapshot which particles can read data from or write data to: + CParticleSnapshot *m_pSnapshot; +}; + +struct CParticleCPInfo +{ + CParticleControlPoint m_ControlPoint; + CModelHitBoxesInfo m_CPHitBox; +}; + + +// struct for simd xform to transform a point from an identitiy coordinate system to that of the control point +struct CParticleSIMDTransformation +{ + FourVectors m_v4Origin; + FourVectors m_v4Fwd; + FourVectors m_v4Up; + FourVectors m_v4Right; + + + FORCEINLINE void VectorRotate( FourVectors &InPnt ) + { + fltx4 fl4OutX = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.x ), MulSIMD( InPnt.z, m_v4Up.x ) ), MulSIMD( InPnt.y, m_v4Right.x ) ); + fltx4 fl4OutY = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.y ), MulSIMD( InPnt.z, m_v4Up.y ) ), MulSIMD( InPnt.y, m_v4Right.y ) ); + InPnt.z = SubSIMD( AddSIMD( MulSIMD( InPnt.x, m_v4Fwd.z ), MulSIMD( InPnt.z, m_v4Up.z ) ), MulSIMD( InPnt.y, m_v4Right.z ) ); + InPnt.x = fl4OutX; + InPnt.y = fl4OutY; + } + + FORCEINLINE void VectorTransform( FourVectors &InPnt ) + { + VectorRotate( InPnt ); + InPnt.x = AddSIMD( InPnt.x, m_v4Origin.x ); + InPnt.y = AddSIMD( InPnt.y, m_v4Origin.y ); + InPnt.z = AddSIMD( InPnt.z, m_v4Origin.z ); + } +}; + +#define NUM_COLLISION_CACHE_MODES 4 + +//----------------------------------------------------------------------------- +// +// CParticleCollection +// +//----------------------------------------------------------------------------- + +enum EParticleRestartMode_t +{ + RESTART_NORMAL, // just reset emitters + RESTART_RESET_AND_MAKE_SURE_EMITS_HAPPEN, // reset emitters. If another restart has already happened, emit particles right now to handle multiple resets per frame. + +}; + +struct CParticleAttributeAddressTable +{ + float *m_pAttributes[MAX_PARTICLE_ATTRIBUTES]; + size_t m_nFloatStrides[MAX_PARTICLE_ATTRIBUTES]; + + FORCEINLINE size_t Stride( int nAttr ) const + { + return m_nFloatStrides[nAttr]; + } + + FORCEINLINE float *Address( int nAttr ) const + { + return m_pAttributes[nAttr]; + } + + FORCEINLINE uint8 *ByteAddress( int nAttr ) const + { + return ( uint8 * ) m_pAttributes[nAttr]; + } + + FORCEINLINE float *FloatAttributePtr( int nAttribute, int nParticleNumber ) const + { + int block_ofs = nParticleNumber / 4; + return m_pAttributes[ nAttribute ] + + m_nFloatStrides[ nAttribute ] * block_ofs + + ( nParticleNumber & 3 ); + } + + void CopyParticleAttributes( int nSrcIndex, int nDestIndex ) const; +}; + + +class CParticleCollection +{ +public: + ~CParticleCollection( void ); + + // Restarts the particle collection, stopping all non-continuous emitters + void Restart( EParticleRestartMode_t eMode = RESTART_NORMAL ); + + // compute bounds from particle list + void RecomputeBounds( void ); + + + void SetControlPoint( int nWhichPoint, const Vector &v ); + void SetControlPointObject( int nWhichPoint, void *pObject ); + + void SetControlPointOrientation( int nWhichPoint, const Vector &forward, + const Vector &right, const Vector &up ); + void SetControlPointForwardVector( int nWhichPoint, const Vector &v ); + void SetControlPointUpVector( int nWhichPoint, const Vector &v ); + void SetControlPointRightVector( int nWhichPoint, const Vector &v ); + void SetControlPointParent( int nWhichPoint, int n ); + void SetControlPointSnapshot( int nWhichPoint, CParticleSnapshot *pSnapshot ); + + void SetControlPointOrientation( int nWhichPoint, const Quaternion &q ); + + // get the pointer to an attribute for a given particle. + // !!speed!! if you find yourself calling this anywhere that matters, + // you're not handling the simd-ness of the particle system well + // and will have bad perf. + const float *GetFloatAttributePtr( int nAttribute, int nParticleNumber ) const; + const int *GetIntAttributePtr( int nAttribute, int nParticleNumber ) const; + const fltx4 *GetM128AttributePtr( int nAttribute, size_t *pStrideOut ) const; + const FourVectors *Get4VAttributePtr( int nAttribute, size_t *pStrideOut ) const; + const FourInts *Get4IAttributePtr( int nAttribute, size_t *pStrideOut ) const; + const int *GetIntAttributePtr( int nAttribute, size_t *pStrideOut ) const; + + Vector GetVectorAttributeValue( int nAttribute, int nParticleNumber ) const; + + + int *GetIntAttributePtrForWrite( int nAttribute, int nParticleNumber ); + + float *GetFloatAttributePtrForWrite( int nAttribute, int nParticleNumber ); + fltx4 *GetM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut ); + FourVectors *Get4VAttributePtrForWrite( int nAttribute, size_t *pStrideOut ); + + const float *GetInitialFloatAttributePtr( int nAttribute, int nParticleNumber ) const; + const fltx4 *GetInitialM128AttributePtr( int nAttribute, size_t *pStrideOut ) const; + const FourVectors *GetInitial4VAttributePtr( int nAttribute, size_t *pStrideOut ) const; + float *GetInitialFloatAttributePtrForWrite( int nAttribute, int nParticleNumber ); + fltx4 *GetInitialM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut ); + + void Simulate( float dt ); + void SkipToTime( float t ); + + // the camera objetc may be compared for equality against control point objects + void Render( IMatRenderContext *pRenderContext, const Vector4D &vecDiffuseModulation, bool bTranslucentOnly = false, void *pCameraObject = NULL ); + + bool IsValid( void ) const { return m_pDef != NULL; } + + // this system and all children are valid + bool IsFullyValid( void ) const; + + const char *GetName() const; + + bool DependsOnSystem( const char *pName ) const; + + // IsFinished returns true when a system has no particles and won't be creating any more + bool IsFinished( void ) const; + + // Used to make sure we're accessing valid memory + bool IsValidAttributePtr( int nAttribute, const void *pPtr ) const; + + void SwapPosAndPrevPos( void ); + + void SetNActiveParticles( int nCount ); + void KillParticle(int nPidx, unsigned int nFlags = 0); + + void StopEmission( bool bInfiniteOnly = false, bool bRemoveAllParticles = false, bool bWakeOnStop = false, bool bPlayEndCap = false ); + void StartEmission( bool bInfiniteOnly = false ); + void SetDormant( bool bDormant ); + bool IsEmitting() const; + + const Vector& GetControlPointAtCurrentTime( int nControlPoint ) const; + void GetControlPointOrientationAtCurrentTime( int nControlPoint, Vector *pForward, Vector *pRight, Vector *pUp ) const; + void GetControlPointTransformAtCurrentTime( int nControlPoint, matrix3x4_t *pMat ); + void GetControlPointTransformAtCurrentTime( int nControlPoint, VMatrix *pMat ); + int GetControlPointParent( int nControlPoint ) const; + CParticleSnapshot *GetControlPointSnapshot( int nWhichPoint ) const; + + // Used to retrieve the position of a control point + // somewhere between m_fCurTime and m_fCurTime - m_fPreviousDT + void GetControlPointAtTime( int nControlPoint, float flTime, Vector *pControlPoint ); + void GetControlPointAtPrevTime( int nControlPoint, Vector *pControlPoint ); + void GetControlPointOrientationAtTime( int nControlPoint, float flTime, Vector *pForward, Vector *pRight, Vector *pUp ); + void GetControlPointTransformAtTime( int nControlPoint, float flTime, matrix3x4_t *pMat ); + void GetControlPointTransformAtTime( int nControlPoint, float flTime, VMatrix *pMat ); + void GetControlPointTransformAtTime( int nControlPoint, float flTime, CParticleSIMDTransformation *pXForm ); + int GetHighestControlPoint( void ) const; + + // Control point accessed: + // NOTE: Unlike the definition's version of these methods, + // these OR-in the masks of their children. + bool ReadsControlPoint( int nPoint ) const; + bool IsNonPositionalControlPoint( int nPoint ) const; + + // Used by particle systems to generate random numbers. Do not call these methods - use sse + // code + int RandomInt( int nMin, int nMax ); + float RandomFloat( float flMin, float flMax ); + float RandomFloatExp( float flMin, float flMax, float flExponent ); + void RandomVector( float flMin, float flMax, Vector *pVector ); + void RandomVector( const Vector &vecMin, const Vector &vecMax, Vector *pVector ); + float RandomVectorInUnitSphere( Vector *pVector ); // Returns the length sqr of the vector + + // NOTE: These versions will produce the *same random numbers* if you give it the same random + // sample id. do not use these methods. + int RandomInt( int nRandomSampleId, int nMin, int nMax ); + float RandomFloat( int nRandomSampleId, float flMin, float flMax ); + float RandomFloatExp( int nRandomSampleId, float flMin, float flMax, float flExponent ); + void RandomVector( int nRandomSampleId, float flMin, float flMax, Vector *pVector ); + void RandomVector( int nRandomSampleId, const Vector &vecMin, const Vector &vecMax, Vector *pVector ); + float RandomVectorInUnitSphere( int nRandomSampleId, Vector *pVector ); // Returns the length sqr of the vector + + fltx4 RandomFloat( const FourInts &ParticleID, int nRandomSampleOffset ); + + + // Random number offset (for use in getting Random #s in operators) + int OperatorRandomSampleOffset() const; + + // Returns the render bounds + void GetBounds( Vector *pMin, Vector *pMax ); + + // Visualize operators (for editing/debugging) + void VisualizeOperator( const DmObjectId_t *pOpId = NULL ); + + // Does the particle system use the power of two frame buffer texture (refraction?) + bool UsesPowerOfTwoFrameBufferTexture( bool bThisFrame ) const; + + // Does the particle system use the full frame buffer texture (soft particles) + bool UsesFullFrameBufferTexture( bool bThisFrame ) const; + + // Is the particle system translucent? + bool IsTranslucent() const; + + // Is the particle system two-pass? + bool IsTwoPass() const; + + // Is the particle system batchable? + bool IsBatchable() const; + + // Is the order of the particles important + bool IsOrderImportant() const; + + // Should this system be run want to read its parent's kill list inside ApplyKillList? + bool ShouldRunForParentApplyKillList( void ) const; + + // Renderer iteration + int GetRendererCount() const; + CParticleOperatorInstance *GetRenderer( int i ); + void *GetRendererContext( int i ); + + bool CheckIfOperatorShouldRun( CParticleOperatorInstance const * op, float *pflCurStrength, bool bApplyingParentKillList = false ); + + Vector TransformAxis( const Vector &SrcAxis, bool bLocalSpace, int nControlPointNumber = 0); + + // return backwards-sorted particle list. use --addressing + const ParticleRenderData_t *GetRenderList( IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData ); + + // calculate the points of a curve for a path + void CalculatePathValues( CPathParameters const &PathIn, + float flTimeStamp, + Vector *pStartPnt, + Vector *pMidPnt, + Vector *pEndPnt + ); + + int GetGroupID() const; + + void InitializeNewParticles( int nFirstParticle, int nParticleCount, uint32 nInittedMask, bool bApplyingParentKillList = false ); + + // update hit boxes for control point if not updated yet for this sim step + void UpdateHitBoxInfo( int nControlPointNumber ); + + // Used by particle system definitions to manage particle collection lists + void UnlinkFromDefList( ); + + FORCEINLINE uint8 const *GetPrevAttributeMemory( void ) const + { + return m_pPreviousAttributeMemory; + } + + FORCEINLINE uint8 const *GetAttributeMemory( void ) const + { + return m_pParticleMemory; + } + + FORCEINLINE bool IsUsingInterpolatedRendering( void ) const + { + return ( + ( m_flTargetDrawTime < m_flCurTime ) && + ( m_flTargetDrawTime >= m_flPrevSimTime ) && + ( GetPrevAttributeMemory() ) && + ( ! m_bFrozen ) ); + } + + // render helpers + int GenerateCulledSortedIndexList( ParticleRenderData_t *pOut, Vector vecCamera, Vector vecFwd, CParticleVisibilityData *pVisibilityData, bool bSorted ); + int GenerateSortedIndexList( ParticleRenderData_t *pOut, Vector vecCameraPos, CParticleVisibilityData *pVisibilityData, bool bSorted ); + + CParticleCollection *GetNextCollectionUsingSameDef() { return m_pNextDef; } + + CUtlReference< CSheet > m_Sheet; + + + +protected: + CParticleCollection( ); + + // Used by client code + bool Init( const char *pParticleSystemName ); + bool Init( CParticleSystemDefinition *pDef ); + + // Bloat the bounding box by bounds around the control point + void BloatBoundsUsingControlPoint(); + + // to run emitters on restart, out of main sim. + void RunRestartedEmitters( void ); + + void SetRenderable( void *pRenderable ); + + +private: + + + + void Init( CParticleSystemDefinition *pDef, float flDelay, int nRandomSeed ); + void InitStorage( CParticleSystemDefinition *pDef ); + void InitParticleCreationTime( int nFirstParticle, int nNumToInit ); + void CopyInitialAttributeValues( int nStartParticle, int nNumParticles ); + void ApplyKillList( void ); + void SetAttributeToConstant( int nAttribute, float fValue ); + void SetAttributeToConstant( int nAttribute, float fValueX, float fValueY, float fValueZ ); + void InitParticleAttributes( int nStartParticle, int nNumParticles, int nAttrsLeftToInit ); + + // call emitter and initializer operators on the specified system + // NOTE: this may be called from ApplyKillList, so the child can access about-to-be-killed particles + static void EmitAndInit( CParticleCollection *pCollection, bool bApplyingParentKillList = false ); + + // initialize this attribute for all active particles + void FillAttributeWithConstant( int nAttribute, float fValue ); + + // Updates the previous control points + void UpdatePrevControlPoints( float dt ); + + // Returns the memory for a particular constant attribute + float *GetConstantAttributeMemory( int nAttribute ); + + // Swaps two particles in the particle list + void SwapAdjacentParticles( int hParticle ); + + // Unlinks a particle from the list + void UnlinkParticle( int hParticle ); + + // Inserts a particle before another particle in the list + void InsertParticleBefore( int hParticle, int hBefore ); + + // Move a particle from one index to another + void MoveParticle( int nInitialIndex, int nNewIndex ); + + // Computes the sq distance to a particle position + float ComputeSqrDistanceToParticle( int hParticle, const Vector &vecPosition ) const; + + // Grows the dist sq range for all particles + void GrowDistSqrBounds( float flDistSqr ); + + // Simulates the first frame + void SimulateFirstFrame( ); + + bool SystemContainsParticlesWithBoolSet( bool CParticleCollection::*pField ) const; + // Does the particle collection contain opaque particle systems + bool ContainsOpaqueCollections(); + bool ComputeUsesPowerOfTwoFrameBufferTexture(); + bool ComputeUsesFullFrameBufferTexture(); + bool ComputeIsTranslucent(); + bool ComputeIsTwoPass(); + bool ComputeIsBatchable(); + bool ComputeIsOrderImportant(); + bool ComputeRunForParentApplyKillList(); + + void LabelTextureUsage( void ); + + void LinkIntoDefList( ); + + // Return the number of particle systems sharing the same definition + int GetCurrentParticleDefCount( CParticleSystemDefinition* pDef ); + + void CopyParticleAttributesToPreviousAttributes( void ) const; + +public: + fltx4 m_fl4CurTime; // accumulated time + + int m_nPaddedActiveParticles; // # of groups of 4 particles + float m_flCurTime; // accumulated time + + // support for simulating particles at < the frame rate and interpolating. + // for a system simulating at lower frame rate, flDrawTime will be < m_flCurTime and >=m_flPrevSimTime + float m_flPrevSimTime; // the time of the previous sim + float m_flTargetDrawTime; // the timestamp for drawing + + int m_nActiveParticles; // # of active particles + float m_flDt; + float m_flPreviousDt; + float m_flNextSleepTime; // time to go to sleep if not drawn + + + CUtlReference< CParticleSystemDefinition > m_pDef; + int m_nAllocatedParticles; + int m_nMaxAllowedParticles; + bool m_bDormant; + bool m_bEmissionStopped; + bool m_bPendingRestart; + bool m_bQueuedStartEmission; + bool m_bFrozen; + bool m_bInEndCap; + + int m_LocalLightingCP; + Color m_LocalLighting; + + // control point data. Don't set these directly, or they won't propagate down to children + // particle control points can act as emitter centers, repulsions points, etc. what they are + // used for depends on what operators and parameters your system has. + int m_nNumControlPointsAllocated; + CParticleCPInfo *m_pCPInfo; + + FORCEINLINE CParticleControlPoint &ControlPoint( int nIdx ) const; + + FORCEINLINE CModelHitBoxesInfo &ControlPointHitBox( int nIdx ) const; + + + // public so people can call methods + uint8 *m_pOperatorContextData; + CParticleCollection *m_pNext; // for linking children together + CParticleCollection *m_pPrev; // for linking children together + + struct CWorldCollideContextData *m_pCollisionCacheData[NUM_COLLISION_CACHE_MODES]; // children can share collision caches w/ parent + + CParticleCollection *m_pParent; + + CUtlIntrusiveDList m_Children; // list for all child particle systems + Vector m_Center; // average of particle centers + void *m_pRenderable; // for use by client + + + void *operator new(size_t nSize); + void *operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ); + void operator delete(void *pData); + void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ); + + +protected: + // current bounds for the particle system + bool m_bBoundsValid; + Vector m_MinBounds; + Vector m_MaxBounds; + int m_nHighestCP; //Highest CP set externally. Needs to assert if a system calls to an unassigned CP. + +private: + + + int m_nAttributeMemorySize; + unsigned char *m_pParticleMemory; // fixed size at initialization. Must be aligned for SSE + unsigned char *m_pParticleInitialMemory; // fixed size at initialization. Must be aligned for SSE + unsigned char *m_pConstantMemory; + uint8 *m_pPreviousAttributeMemory; // for simulating at less than the display rate + + + int m_nPerParticleInitializedAttributeMask; + int m_nPerParticleUpdatedAttributeMask; + int m_nPerParticleReadInitialAttributeMask; // What fields do operators want to see initial attribute values for? + + CParticleAttributeAddressTable m_ParticleAttributes; + CParticleAttributeAddressTable m_ParticleInitialAttributes; + CParticleAttributeAddressTable m_PreviousFrameAttributes; + + float *m_pConstantAttributes; + + uint64 m_nControlPointReadMask; // Mask indicating which control points have been accessed + uint64 m_nControlPointNonPositionalMask; // Mask indicating which control points are non-positional (ie shouldn't be transformed) + int m_nParticleFlags; // PCFLAGS_xxx + bool m_bIsScrubbable : 1; + bool m_bIsRunningInitializers : 1; + bool m_bIsRunningOperators : 1; + bool m_bIsTranslucent : 1; + bool m_bIsTwoPass : 1; + bool m_bAnyUsesPowerOfTwoFrameBufferTexture : 1; // whether or not we or any children use this + bool m_bAnyUsesFullFrameBufferTexture : 1; + bool m_bIsBatchable : 1; + bool m_bIsOrderImportant : 1; // is order important when deleting + bool m_bRunForParentApplyKillList : 1; // see ShouldRunForParentApplyKillList() + + bool m_bUsesPowerOfTwoFrameBufferTexture; // whether or not we use this, _not_ our children + bool m_bUsesFullFrameBufferTexture; + + // How many frames have we drawn? + int m_nDrawnFrames; + + + // Used to assign unique ids to each particle + int m_nUniqueParticleId; + + // Used to generate random numbers + int m_nRandomQueryCount; + int m_nRandomSeed; + int m_nOperatorRandomSampleOffset; + + float m_flMinDistSqr; + float m_flMaxDistSqr; + float m_flOOMaxDistSqr; + Vector m_vecLastCameraPos; + float m_flLastMinDistSqr; + float m_flLastMaxDistSqr; + + // Particle collection kill list. set up by particle system mgr + int m_nNumParticlesToKill; + KillListItem_t *m_pParticleKillList; + + // Used to build a list of all particle collections that have the same particle def + CParticleCollection *m_pNextDef; + CParticleCollection *m_pPrevDef; + + void LoanKillListTo( CParticleCollection *pBorrower ) const; + bool HasAttachedKillList( void ) const; + + + // For debugging + CParticleOperatorInstance *m_pRenderOp; + friend class CParticleSystemMgr; + friend class CParticleOperatorInstance; + friend class CParticleSystemDefinition; + friend class C4VInterpolatedAttributeIterator; + friend class CM128InterpolatedAttributeIterator; +}; + + + +class CM128InitialAttributeIterator : public CStridedConstPtr +{ +public: + FORCEINLINE CM128InitialAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->GetInitialM128AttributePtr( nAttribute, &m_nStride ); + } +}; + + +class CM128AttributeIterator : public CStridedConstPtr +{ +public: + FORCEINLINE void Init( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->GetM128AttributePtr( nAttribute, &m_nStride ); + } + FORCEINLINE CM128AttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } + CM128AttributeIterator( void ) + { + } + + FORCEINLINE fltx4 operator()( fltx4 fl4T ) const + { + return *( m_pData ); + } +}; + +class C4IAttributeIterator : public CStridedConstPtr +{ +public: + FORCEINLINE void Init( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->Get4IAttributePtr( nAttribute, &m_nStride ); + } + FORCEINLINE C4IAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } + FORCEINLINE C4IAttributeIterator( void ) + { + } +}; + +class CM128AttributeWriteIterator : public CStridedPtr +{ +public: + FORCEINLINE CM128AttributeWriteIterator( void ) + { + } + FORCEINLINE void Init ( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->GetM128AttributePtrForWrite( nAttribute, &m_nStride ); + } + FORCEINLINE CM128AttributeWriteIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } +}; + +class C4VAttributeIterator : public CStridedConstPtr +{ +public: + FORCEINLINE void Init( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->Get4VAttributePtr( nAttribute, &m_nStride ); + } + FORCEINLINE C4VAttributeIterator( void ) + { + } + + FORCEINLINE C4VAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } + + FORCEINLINE fltx4 X( fltx4 T ) + { + return m_pData->x; + } + + FORCEINLINE fltx4 Y( fltx4 T ) + { + return m_pData->y; + } + + FORCEINLINE fltx4 Z( fltx4 T ) + { + return m_pData->z; + } + +}; + +class CM128InterpolatedAttributeIterator : public CM128AttributeIterator +{ +protected: + intp m_nOldDataOffset; + + FORCEINLINE fltx4 const *PreviousData( void ) const + { + return ( fltx4 const * ) ( ( ( uint8 const * ) m_pData ) + m_nOldDataOffset ); + } + +public: + FORCEINLINE void Init( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->GetM128AttributePtr( nAttribute, &m_nStride ); + Assert( pParticles->GetPrevAttributeMemory() ); + if ( m_nStride ) + { + m_nOldDataOffset = + pParticles->m_PreviousFrameAttributes.ByteAddress( nAttribute ) - pParticles->m_ParticleAttributes.ByteAddress( nAttribute ); + } + else + { + m_nOldDataOffset = 0; + } + + } + FORCEINLINE CM128InterpolatedAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } + + CM128InterpolatedAttributeIterator( void ) + { + } + + FORCEINLINE fltx4 operator()( fltx4 fl4T ) const + { + fltx4 fl4Ret = *( PreviousData() ); + return AddSIMD( fl4Ret, MulSIMD( fl4T, SubSIMD( *m_pData, fl4Ret ) ) ); + } +}; + +class C4VInterpolatedAttributeIterator : public C4VAttributeIterator +{ +protected: + int m_nOldDataOffset; + + FORCEINLINE FourVectors const *PreviousData( void ) const + { + return ( FourVectors const * ) ( ( ( uint8 const * ) m_pData ) + m_nOldDataOffset ); + } + +public: + void Init( int nAttribute, CParticleCollection *pParticles ); + + + FORCEINLINE C4VInterpolatedAttributeIterator( void ) + { + } + + FORCEINLINE C4VInterpolatedAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + Init( nAttribute, pParticles ); + } + + FORCEINLINE fltx4 X( fltx4 fl4T ) const + { + fltx4 fl4Ret = PreviousData()->x; + return AddSIMD( fl4Ret, MulSIMD( fl4T, SubSIMD( m_pData->x, fl4Ret ) ) ); + } + + FORCEINLINE fltx4 Y( fltx4 fl4T ) const + { + fltx4 fl4Ret = PreviousData()->y; + return AddSIMD( fl4Ret, MulSIMD( fl4T, SubSIMD( m_pData->y, fl4Ret ) ) ); + } + + FORCEINLINE fltx4 Z( fltx4 fl4T ) const + { + fltx4 fl4Ret = PreviousData()->z; + return AddSIMD( fl4Ret, MulSIMD( fl4T, SubSIMD( m_pData->z, fl4Ret ) ) ); + } + +}; + +class C4VInitialAttributeIterator : public CStridedConstPtr +{ +public: + FORCEINLINE C4VInitialAttributeIterator( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->GetInitial4VAttributePtr( nAttribute, &m_nStride ); + } +}; + +class C4VAttributeWriteIterator : public CStridedPtr +{ +public: + FORCEINLINE C4VAttributeWriteIterator( int nAttribute, CParticleCollection *pParticles ) + { + m_pData = pParticles->Get4VAttributePtrForWrite( nAttribute, &m_nStride ); + } +}; + + + + + +//----------------------------------------------------------------------------- +// Inline methods of CParticleCollection +//----------------------------------------------------------------------------- + +inline bool CParticleCollection::HasAttachedKillList( void ) const +{ + return m_pParticleKillList != NULL; +} + +inline bool CParticleCollection::ReadsControlPoint( int nPoint ) const +{ + return ( m_nControlPointReadMask & ( 1ULL << nPoint ) ) != 0; +} + +inline bool CParticleCollection::IsNonPositionalControlPoint( int nPoint ) const +{ + return ( m_nControlPointNonPositionalMask & ( 1ULL << nPoint ) ) != 0; +} + +inline void CParticleCollection::SetNActiveParticles( int nCount ) +{ + Assert( nCount >= 0 && nCount <= m_nMaxAllowedParticles ); + m_nActiveParticles = nCount; + m_nPaddedActiveParticles = ( nCount+3 )/4; +} + +inline void CParticleCollection::SwapPosAndPrevPos( void ) +{ + // strides better be the same! + Assert( m_ParticleAttributes.Stride( PARTICLE_ATTRIBUTE_XYZ ) == m_ParticleAttributes.Stride( PARTICLE_ATTRIBUTE_PREV_XYZ ) ); + V_swap( m_ParticleAttributes.m_pAttributes[ PARTICLE_ATTRIBUTE_XYZ ], m_ParticleAttributes.m_pAttributes[ PARTICLE_ATTRIBUTE_PREV_XYZ ] ); +} + +FORCEINLINE CParticleControlPoint &CParticleCollection::ControlPoint( int nIdx ) const +{ + Assert( nIdx < m_nNumControlPointsAllocated ); + return m_pCPInfo[MIN( nIdx, m_nNumControlPointsAllocated -1 )].m_ControlPoint; +} + +FORCEINLINE CModelHitBoxesInfo &CParticleCollection::ControlPointHitBox( int nIdx ) const +{ + return m_pCPInfo[MIN( nIdx, m_nNumControlPointsAllocated -1 )].m_CPHitBox; +} + +inline void CParticleCollection::LoanKillListTo( CParticleCollection *pBorrower ) const +{ + Assert(! pBorrower->m_pParticleKillList ); + pBorrower->m_nNumParticlesToKill = 0; + pBorrower->m_pParticleKillList = m_pParticleKillList; +} + +inline void CParticleCollection::SetAttributeToConstant( int nAttribute, float fValue ) +{ + float *fconst = m_pConstantAttributes + 4*3*nAttribute; + fconst[0] = fconst[1] = fconst[2] = fconst[3] = fValue; +} + +inline void CParticleCollection::SetAttributeToConstant( int nAttribute, float fValueX, float fValueY, float fValueZ ) +{ + float *fconst = m_pConstantAttributes + 4*3*nAttribute; + fconst[0] = fconst[1] = fconst[2] = fconst[3] = fValueX; + fconst[4] = fconst[5] = fconst[6] = fconst[7] = fValueY; + fconst[8] = fconst[9] = fconst[10] = fconst[11] = fValueZ; +} + +inline void CParticleCollection::SetControlPoint( int nWhichPoint, const Vector &v ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_Position = v; + m_nHighestCP = MAX( m_nHighestCP, nWhichPoint ); + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPoint( nWhichPoint, v ); + } +} + +inline void CParticleCollection::SetControlPointObject( int nWhichPoint, void *pObject ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_pObject = pObject; + m_nHighestCP = MAX( m_nHighestCP, nWhichPoint ); + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointObject( nWhichPoint, pObject ); + } +} + +inline void CParticleCollection::SetControlPointOrientation( int nWhichPoint, const Vector &forward, + const Vector &right, const Vector &up ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + + // check perpendicular + Assert( fabs( DotProduct( forward, up ) ) <= 0.1f ); + Assert( fabs( DotProduct( forward, right ) ) <= 0.1f ); + Assert( fabs( DotProduct( right, up ) ) <= 0.1f ); + + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_ForwardVector = forward; + ControlPoint( nWhichPoint ).m_UpVector = up; + ControlPoint( nWhichPoint ).m_RightVector = right; + m_nHighestCP = MAX( m_nHighestCP, nWhichPoint ); + } + // make sure all children are finished + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointOrientation( nWhichPoint, forward, right, up ); + } +} + +inline Vector CParticleCollection::TransformAxis( const Vector &SrcAxis, bool bLocalSpace, + int nControlPointNumber) +{ + if ( bLocalSpace ) + { + return // mxmul + ( SrcAxis.x * ControlPoint( nControlPointNumber ).m_RightVector )+ + ( SrcAxis.y * ControlPoint( nControlPointNumber ).m_ForwardVector )+ + ( SrcAxis.z * ControlPoint( nControlPointNumber ).m_UpVector ); + } + else + return SrcAxis; +} + + +inline void CParticleCollection::SetControlPointOrientation( int nWhichPoint, const Quaternion &q ) +{ + matrix3x4_t mat; + Vector vecForward, vecUp, vecRight; + QuaternionMatrix( q, mat ); + MatrixVectors( mat, &vecForward, &vecRight, &vecUp ); + SetControlPointOrientation( nWhichPoint, vecForward, vecRight, vecUp ); +} + +inline void CParticleCollection::SetControlPointForwardVector( int nWhichPoint, const Vector &v ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_ForwardVector = v; + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointForwardVector( nWhichPoint, v ); + } +} + +inline void CParticleCollection::SetControlPointUpVector( int nWhichPoint, const Vector &v ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_UpVector = v; + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointUpVector( nWhichPoint, v ); + } +} + +inline void CParticleCollection::SetControlPointRightVector( int nWhichPoint, const Vector &v) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_RightVector = v; + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointRightVector( nWhichPoint, v ); + } +} + +inline void CParticleCollection::SetControlPointParent( int nWhichPoint, int n ) +{ + Assert( ( nWhichPoint >= 0) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_nParent = n; + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointParent( nWhichPoint, n ); + } +} + +inline void CParticleCollection::SetControlPointSnapshot( int nWhichPoint, CParticleSnapshot *pSnapshot ) +{ + Assert( ( nWhichPoint >= 0 ) && ( nWhichPoint < MAX_PARTICLE_CONTROL_POINTS ) ); + if ( nWhichPoint < m_nNumControlPointsAllocated ) + { + ControlPoint( nWhichPoint ).m_pSnapshot = pSnapshot; + } + for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext ) + { + i->SetControlPointSnapshot( nWhichPoint, pSnapshot ); + } +} + + +// Returns the memory for a particular constant attribute +inline float *CParticleCollection::GetConstantAttributeMemory( int nAttribute ) +{ + return m_pConstantAttributes + 3 * 4 * nAttribute; +} + +// Random number offset (for use in getting Random #s in operators) +inline int CParticleCollection::OperatorRandomSampleOffset() const +{ + return m_nOperatorRandomSampleOffset; +} + +// Used by particle systems to generate random numbers +inline int CParticleCollection::RandomInt( int nRandomSampleId, int nMin, int nMax ) +{ + // do not call + float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ]; + flRand *= ( nMax + 1 - nMin ); + int nRand = (int)flRand + nMin; + return nRand; +} + +inline float CParticleCollection::RandomFloat( int nRandomSampleId, float flMin, float flMax ) +{ + // do not call + float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ]; + flRand *= ( flMax - flMin ); + flRand += flMin; + return flRand; +} + +inline fltx4 CParticleCollection::RandomFloat( const FourInts &ParticleID, int nRandomSampleOffset ) +{ + fltx4 Retval; + int nOfs=m_nRandomSeed+nRandomSampleOffset; + SubFloat( Retval, 0 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[0] ) & RANDOM_FLOAT_MASK ]; + SubFloat( Retval, 1 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[1] ) & RANDOM_FLOAT_MASK ]; + SubFloat( Retval, 2 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[2] ) & RANDOM_FLOAT_MASK ]; + SubFloat( Retval, 3 ) = s_pRandomFloats[ ( nOfs + ParticleID.m_nValue[3] ) & RANDOM_FLOAT_MASK ]; + return Retval; +} + + +inline float CParticleCollection::RandomFloatExp( int nRandomSampleId, float flMin, float flMax, float flExponent ) +{ + // do not call + float flRand = s_pRandomFloats[ ( m_nRandomSeed + nRandomSampleId ) & RANDOM_FLOAT_MASK ]; + flRand = powf( flRand, flExponent ); + flRand *= ( flMax - flMin ); + flRand += flMin; + return flRand; +} + +inline void CParticleCollection::RandomVector( int nRandomSampleId, float flMin, float flMax, Vector *pVector ) +{ + // do not call + float flDelta = flMax - flMin; + int nBaseId = m_nRandomSeed + nRandomSampleId; + + pVector->x = s_pRandomFloats[ nBaseId & RANDOM_FLOAT_MASK ]; + pVector->x *= flDelta; + pVector->x += flMin; + + pVector->y = s_pRandomFloats[ ( nBaseId + 1 ) & RANDOM_FLOAT_MASK ]; + pVector->y *= flDelta; + pVector->y += flMin; + + pVector->z = s_pRandomFloats[ ( nBaseId + 2 ) & RANDOM_FLOAT_MASK ]; + pVector->z *= flDelta; + pVector->z += flMin; +} + +inline void CParticleCollection::RandomVector( int nRandomSampleId, const Vector &vecMin, const Vector &vecMax, Vector *pVector ) +{ + // do not call + int nBaseId = m_nRandomSeed + nRandomSampleId; + pVector->x = RandomFloat( nBaseId, vecMin.x, vecMax.x ); + pVector->y = RandomFloat( nBaseId + 1, vecMin.y, vecMax.y ); + pVector->z = RandomFloat( nBaseId + 2, vecMin.z, vecMax.z ); +} + +// Used by particle systems to generate random numbers +inline int CParticleCollection::RandomInt( int nMin, int nMax ) +{ + // do not call + return RandomInt( m_nRandomQueryCount++, nMin, nMax ); +} + +inline float CParticleCollection::RandomFloat( float flMin, float flMax ) +{ + // do not call + return RandomFloat( m_nRandomQueryCount++, flMin, flMax ); +} + +inline float CParticleCollection::RandomFloatExp( float flMin, float flMax, float flExponent ) +{ + // do not call + return RandomFloatExp( m_nRandomQueryCount++, flMin, flMax, flExponent ); +} + +inline void CParticleCollection::RandomVector( float flMin, float flMax, Vector *pVector ) +{ + // do not call + RandomVector( m_nRandomQueryCount, flMin, flMax, pVector ); + m_nRandomQueryCount +=3; +} + +inline void CParticleCollection::RandomVector( const Vector &vecMin, const Vector &vecMax, Vector *pVector ) +{ + // do not call + RandomVector( m_nRandomQueryCount, vecMin, vecMax, pVector ); + m_nRandomQueryCount +=3; +} + +inline float CParticleCollection::RandomVectorInUnitSphere( Vector *pVector ) +{ + // do not call + float flUnitSphere = RandomVectorInUnitSphere( m_nRandomQueryCount, pVector ); + m_nRandomQueryCount +=3; + return flUnitSphere; +} + + +// get the pointer to an attribute for a given particle. !!speed!! if you find yourself +// calling this anywhere that matters, you're not handling the simd-ness of the particle system +// well and will have bad perf. +inline const float *CParticleCollection::GetFloatAttributePtr( int nAttribute, int nParticleNumber ) const +{ + Assert( nParticleNumber < m_nAllocatedParticles ); + return m_ParticleAttributes.FloatAttributePtr( nAttribute, nParticleNumber ); +} + +inline int *CParticleCollection::GetIntAttributePtrForWrite( int nAttribute, int nParticleNumber ) +{ + return reinterpret_cast< int* >( GetFloatAttributePtrForWrite( nAttribute, nParticleNumber ) ); +} + +inline const int *CParticleCollection::GetIntAttributePtr( int nAttribute, int nParticleNumber ) const +{ + return (int*)GetFloatAttributePtr( nAttribute, nParticleNumber ); +} + +inline const fltx4 *CParticleCollection::GetM128AttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *(pStrideOut) = m_ParticleAttributes.Stride( nAttribute ) / 4; + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline const FourInts *CParticleCollection::Get4IAttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *(pStrideOut) = m_ParticleAttributes.Stride( nAttribute ) / 4; + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline const int32 *CParticleCollection::GetIntAttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *(pStrideOut) = m_ParticleAttributes.Stride( nAttribute ); + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline const FourVectors *CParticleCollection::Get4VAttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *(pStrideOut) = m_ParticleAttributes.Stride( nAttribute ) / 12; + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline FourVectors *CParticleCollection::Get4VAttributePtrForWrite( int nAttribute, size_t *pStrideOut ) +{ + *( pStrideOut ) = m_ParticleAttributes.Stride( nAttribute ) / 12; + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline const FourVectors *CParticleCollection::GetInitial4VAttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *(pStrideOut) = m_ParticleInitialAttributes.Stride( nAttribute )/12; + return reinterpret_cast( m_ParticleInitialAttributes.Address( nAttribute ) ); +} + +inline Vector CParticleCollection::GetVectorAttributeValue( int nAttribute, int nParticleNumber ) const +{ + Assert( nParticleNumber < m_nAllocatedParticles ); + size_t nStride; + float const *pData = ( float const * ) Get4VAttributePtr( nAttribute, &nStride ); + int nOfs = nParticleNumber / 4; + int nRemainder = nParticleNumber & 3; + pData += 12 * nOfs + nRemainder; + Vector vecRet( pData[0], pData[4], pData[8] ); + return vecRet; +} + + +inline float *CParticleCollection::GetFloatAttributePtrForWrite( int nAttribute, int nParticleNumber ) +{ + // NOTE: If you hit this assertion, it means your particle operator isn't returning + // the appropriate fields in the RequiredAttributesMask call + Assert( !m_bIsRunningInitializers || ( m_nPerParticleInitializedAttributeMask & (1 << nAttribute) ) ); + Assert( !m_bIsRunningOperators || ( m_nPerParticleUpdatedAttributeMask & (1 << nAttribute) ) ); + + Assert( m_ParticleAttributes.Stride( nAttribute ) != 0 ); + + Assert( nParticleNumber < m_nAllocatedParticles ); + return m_ParticleAttributes.FloatAttributePtr( nAttribute, nParticleNumber ); +} + +inline fltx4 *CParticleCollection::GetM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut ) +{ + // NOTE: If you hit this assertion, it means your particle operator isn't returning + // the appropriate fields in the RequiredAttributesMask call + Assert( !m_bIsRunningInitializers || ( m_nPerParticleInitializedAttributeMask & (1 << nAttribute) ) ); + Assert( !m_bIsRunningOperators || ( m_nPerParticleUpdatedAttributeMask & (1 << nAttribute) ) ); + Assert( m_ParticleAttributes.Stride( nAttribute ) != 0 ); + + *( pStrideOut ) = m_ParticleAttributes.Stride( nAttribute ) / 4; + return reinterpret_cast( m_ParticleAttributes.Address( nAttribute ) ); +} + +inline const float *CParticleCollection::GetInitialFloatAttributePtr( int nAttribute, int nParticleNumber ) const +{ + Assert( nParticleNumber < m_nAllocatedParticles ); + return m_ParticleInitialAttributes.FloatAttributePtr( nAttribute, nParticleNumber ); +} + +inline const fltx4 *CParticleCollection::GetInitialM128AttributePtr( int nAttribute, size_t *pStrideOut ) const +{ + *( pStrideOut ) = m_ParticleInitialAttributes.Stride( nAttribute ) / 4; + return reinterpret_cast( m_ParticleInitialAttributes.Address( nAttribute ) ); +} + +inline float *CParticleCollection::GetInitialFloatAttributePtrForWrite( int nAttribute, int nParticleNumber ) +{ + Assert( nParticleNumber < m_nAllocatedParticles ); + Assert( m_nPerParticleReadInitialAttributeMask & ( 1 << nAttribute ) ); + return m_ParticleInitialAttributes.FloatAttributePtr( nAttribute, nParticleNumber ); +} + +inline fltx4 *CParticleCollection::GetInitialM128AttributePtrForWrite( int nAttribute, size_t *pStrideOut ) +{ + Assert( m_nPerParticleReadInitialAttributeMask & ( 1 << nAttribute ) ); + *( pStrideOut ) = m_ParticleInitialAttributes.Stride( nAttribute ) / 4; + return reinterpret_cast( m_ParticleInitialAttributes.Address( nAttribute ) ); +} + +// Used to make sure we're accessing valid memory +inline bool CParticleCollection::IsValidAttributePtr( int nAttribute, const void *pPtr ) const +{ + if ( pPtr < m_ParticleAttributes.Address( nAttribute ) ) + return false; + + size_t nArraySize = m_ParticleAttributes.Stride( nAttribute ) * m_nAllocatedParticles / 4; + void *pMaxPtr = m_ParticleAttributes.Address( nAttribute ) + nArraySize; + return ( pPtr <= pMaxPtr ); +} + + +FORCEINLINE void CParticleCollection::KillParticle( int nPidx, unsigned int nKillFlags ) +{ + // add a particle to the sorted kill list. entries must be added in sorted order. + // within a particle operator, this is safe to call. Outside of one, you have to call + // the ApplyKillList() method yourself. The storage for the kill list is global between + // all particle systems, so you can't kill a particle in 2 different CParticleCollections + // w/o calling ApplyKillList + Assert( !m_nNumParticlesToKill || ( nPidx > (int)m_pParticleKillList[ m_nNumParticlesToKill - 1 ].nIndex ) ); + + // note that it is permissible to kill particles with indices>the number of active + // particles, in order to faciliate easy sse coding (that said, we only expect the + // particle index to be at most more than 3 larger than the particle count) + Assert( nPidx < m_nActiveParticles + 4 ); + + COMPILE_TIME_ASSERT( ( sizeof( KillListItem_t ) == 4 ) && ( MAX_PARTICLES_IN_A_SYSTEM < ( 1 << KILL_LIST_INDEX_BITS ) ) ); + Assert( !( nPidx & ~KILL_LIST_INDEX_MASK ) && !( nKillFlags & ~KILL_LIST_FLAGS_MASK ) ); + KillListItem_t killItem = { nPidx, nKillFlags }; + + Assert( m_nNumParticlesToKill < MAX_PARTICLES_IN_A_SYSTEM ); + m_pParticleKillList[ m_nNumParticlesToKill++ ] = killItem; +} + +// initialize this attribute for all active particles +inline void CParticleCollection::FillAttributeWithConstant( int nAttribute, float fValue ) +{ + size_t stride; + fltx4 *pAttr = GetM128AttributePtrForWrite( nAttribute, &stride ); + fltx4 fill=ReplicateX4( fValue ); + for( int i = 0; i < m_nPaddedActiveParticles; i++ ) + { + *(pAttr) = fill; + pAttr += stride; + } +} + + +//----------------------------------------------------------------------------- +// Helper to set vector attribute values +//----------------------------------------------------------------------------- +FORCEINLINE void SetVectorAttribute( float *pAttribute, float x, float y, float z ) +{ + pAttribute[0] = x; + pAttribute[4] = y; + pAttribute[8] = z; +} + +FORCEINLINE void SetVectorAttribute( float *pAttribute, const Vector &v ) +{ + pAttribute[0] = v.x; + pAttribute[4] = v.y; + pAttribute[8] = v.z; +} + +FORCEINLINE void SetVectorFromAttribute( Vector &v, const float *pAttribute ) +{ + v.x = pAttribute[0]; + v.y = pAttribute[4]; + v.z = pAttribute[8]; +} + + +//----------------------------------------------------------------------------- +// Computes the sq distance to a particle position +//----------------------------------------------------------------------------- +FORCEINLINE float CParticleCollection::ComputeSqrDistanceToParticle( int hParticle, const Vector &vecPosition ) const +{ + const float *xyz = GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, hParticle ); + Vector vecParticlePosition( xyz[0], xyz[4], xyz[8] ); + return vecParticlePosition.DistToSqr( vecPosition ); +} + + +//----------------------------------------------------------------------------- +// Grows the dist sq range for all particles +//----------------------------------------------------------------------------- +FORCEINLINE void CParticleCollection::GrowDistSqrBounds( float flDistSqr ) +{ + if ( m_flLastMinDistSqr > flDistSqr ) + { + m_flLastMinDistSqr = flDistSqr; + } + else if ( m_flLastMaxDistSqr < flDistSqr ) + { + m_flLastMaxDistSqr = flDistSqr; + } +} + + + + +//----------------------------------------------------------------------------- +// Data associated with children particle systems +//----------------------------------------------------------------------------- +struct ParticleChildrenInfo_t +{ + DmObjectId_t m_Id; + ParticleSystemHandle_t m_Name; + bool m_bUseNameBasedLookup; + float m_flDelay; // How much to delay this system after the parent starts + bool m_bEndCap; // This child only plays when an effect is stopped with endcap effects. +}; + + +//----------------------------------------------------------------------------- +// A template describing how a particle system will function +//----------------------------------------------------------------------------- +class CParticleSystemDefinition +{ + DECLARE_DMXELEMENT_UNPACK(); + DECLARE_REFERENCED_CLASS( CParticleSystemDefinition ); + + +public: + CParticleSystemDefinition( void ); + ~CParticleSystemDefinition( void ); + + // Serialization, unserialization + void Read( CDmxElement *pElement ); + CDmxElement *Write(); + + const char *MaterialName() const; + IMaterial *GetMaterial(); + const char *GetName() const; + const DmObjectId_t& GetId() const; + + // Does the particle system use the power of two frame buffer texture (refraction?) + bool UsesPowerOfTwoFrameBufferTexture(); + + // Does the particle system use the full frame buffer texture (soft particles) + bool UsesFullFrameBufferTexture(); + + // Should we always precache this? + bool ShouldAlwaysPrecache() const; + + // Should we batch particle collections using this definition up? + bool ShouldBatch() const; + + // Is the particle system rendered on the viewmodel? + bool IsViewModelEffect() const; + + bool IsScreenSpaceEffect() const; + + void SetDrawThroughLeafSystem( bool bDraw ) { m_bDrawThroughLeafSystem = bDraw; } + bool IsDrawnThroughLeafSystem( void ) const { return m_bDrawThroughLeafSystem; } + + // Used to iterate over all particle collections using the same def + CParticleCollection *FirstCollection(); + + // What's the effective cull size + fill cost? + // Used for early retirement + float GetCullRadius() const; + float GetCullFillCost() const; + int GetCullControlPoint() const; + const char *GetCullReplacementDefinition() const; + + // Retirement + bool HasRetirementBeenChecked( int nFrame ) const; + void MarkRetirementCheck( int nFrame ); + + bool HasFallback() const; + CParticleSystemDefinition *GetFallbackReplacementDefinition() const; + + int GetMinCPULevel() const; + int GetMinGPULevel() const; + + // Control point read + void MarkReadsControlPoint( int nPoint ); + bool ReadsControlPoint( int nPoint ) const; + bool IsNonPositionalControlPoint( int nPoint ) const; + + float GetMaxTailLength() const; + void SetMaxTailLength( float flMaxTailLength ); + // Sheet symbols (used to avoid string->symbol conversions when effects are created) + void InvalidateSheetSymbol(); + void CacheSheetSymbol( CUtlSymbol sheetSymbol ); + bool IsSheetSymbolCached() const; + CUtlSymbol GetSheetSymbol() const; + + +private: + void Precache(); + void Uncache(); + bool IsPrecached(); + + void UnlinkAllCollections(); + + void SetupContextData( ); + void ParseChildren( CDmxElement *pElement ); + void ParseOperators( const char *pszName, ParticleFunctionType_t nFunctionType, + CDmxElement *pElement, CUtlVector &out_list ); + void WriteChildren( CDmxElement *pElement ); + void WriteOperators( CDmxElement *pElement, const char *pOpKeyName, + const CUtlVector &inList ); + CUtlVector *GetOperatorList( ParticleFunctionType_t type ); + CParticleOperatorInstance *FindOperatorById( ParticleFunctionType_t type, const DmObjectId_t &id ); + CParticleOperatorInstance *FindOperatorByName( const char *pOperatorName ); // SLOW! + +private: + int m_nInitialParticles; + int m_nPerParticleUpdatedAttributeMask; + int m_nPerParticleInitializedAttributeMask; + int m_nInitialAttributeReadMask; + int m_nAttributeReadMask; + uint64 m_nControlPointReadMask; + uint64 m_nControlPointNonPositionalMask; + Vector m_BoundingBoxMin; + Vector m_BoundingBoxMax; + char m_pszMaterialName[MAX_PATH]; + CMaterialReference m_Material; + Vector4D m_vecMaterialModulation; + CParticleCollection *m_pFirstCollection; + char m_pszCullReplacementName[128]; + float m_flCullRadius; + float m_flCullFillCost; + int m_nCullControlPoint; + int m_nRetireCheckFrame; + float m_flMaxTailLength; + + // Fallbacks for exceeding maximum number of the same type of system at once. + char m_pszFallbackReplacementName[128]; + int m_nFallbackMaxCount; + int m_nFallbackCurrentCount; + CUtlReference< CParticleSystemDefinition > m_pFallback; + + // Default attribute values + Color m_ConstantColor; + Vector m_ConstantNormal; + float m_flConstantRadius; + float m_flConstantRotation; + float m_flConstantRotationSpeed; + int m_nConstantSequenceNumber; + int m_nConstantSequenceNumber1; + int m_nGroupID; + float m_flMaximumTimeStep; + float m_flMaximumSimTime; // maximum time to sim before drawing first frame. + float m_flMinimumSimTime; // minimum time to sim before drawing first frame - prevents all + // capped particles from drawing at 0 time. + float m_flMinimumTimeStep; // for simulating at < frame rate + + int m_nMinimumFrames; // number of frames to apply max/min simulation times + + int m_nMinCPULevel; // minimum CPU/GPU levels for a + int m_nMinGPULevel; // particle system to be allowed to spawn + + + // Is the particle system rendered on the viewmodel? + CUtlSymbol m_SheetSymbol; + bool m_bViewModelEffect; + bool m_bScreenSpaceEffect; + bool m_bDrawThroughLeafSystem; + bool m_bSheetSymbolCached; + + size_t m_nContextDataSize; + DmObjectId_t m_Id; + +public: + float m_flMaxDrawDistance; // distance at which to not draw. + float m_flNoDrawTimeToGoToSleep; // after not beeing seen for this long, the system will sleep + + int m_nMaxParticles; + int m_nSkipRenderControlPoint; // if the camera is attached to the + // object associated with this control + // point, don't render the system + + int m_nAllowRenderControlPoint; // if the camera is attached to the + // object associated with this control + // point, render the system, otherwise, don't + + + int m_nAggregationMinAvailableParticles; // only aggregate if their are this many free particles + float m_flAggregateRadius; // aggregate particles if this system is within radius n + float m_flStopSimulationAfterTime; // stop ( freeze ) simulation after this time + + CUtlString m_Name; + + CUtlVector m_Operators; + CUtlVector m_Renderers; + CUtlVector m_Initializers; + CUtlVector m_Emitters; + CUtlVector m_ForceGenerators; + CUtlVector m_Constraints; + CUtlVector m_Children; + + CUtlVector m_nOperatorsCtxOffsets; + CUtlVector m_nRenderersCtxOffsets; + CUtlVector m_nInitializersCtxOffsets; + CUtlVector m_nEmittersCtxOffsets; + CUtlVector m_nForceGeneratorsCtxOffsets; + CUtlVector m_nConstraintsCtxOffsets; + + +#if MEASURE_PARTICLE_PERF + float m_flTotalSimTime; + float m_flUncomittedTotalSimTime; + float m_flMaxMeasuredSimTime; + float m_flTotalRenderTime; + float m_flMaxMeasuredRenderTime; + float m_flUncomittedTotalRenderTime; +#endif + + uint m_nPerParticleOutlineMaterialVarToken; + + CInterlockedInt m_nNumIntersectionTests; // the number of particle intersectio queries done + CInterlockedInt m_nNumActualRayTraces; // the total number of ray intersections done. + + int m_nMaximumActiveParticles; + bool m_bShouldSort; + bool m_bShouldBatch; + bool m_bIsPrecached : 1; + bool m_bAlwaysPrecache : 1; + + friend class CParticleCollection; + friend class CParticleSystemMgr; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline CParticleSystemDefinition::CParticleSystemDefinition( void ) +{ + m_vecMaterialModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f ); + m_SheetSymbol = UTL_INVAL_SYMBOL; + m_bSheetSymbolCached = false; + m_nControlPointReadMask = 0; + m_nControlPointNonPositionalMask = 0; + m_nInitialAttributeReadMask = 0; + m_nPerParticleInitializedAttributeMask = 0; + m_nPerParticleUpdatedAttributeMask = 0; + m_nAttributeReadMask = 0; + m_nNumIntersectionTests = 0; + m_nNumActualRayTraces = 0; +#if MEASURE_PARTICLE_PERF + m_flTotalSimTime = 0.0; + m_flMaxMeasuredSimTime = 0.0; + m_flMaxMeasuredRenderTime = 0.0f; + m_flTotalRenderTime = 0.0f; + m_flUncomittedTotalRenderTime = 0.0f; + m_flUncomittedTotalSimTime = 0.0f; +#endif + m_nMaximumActiveParticles = 0; + m_bIsPrecached = false; + m_bAlwaysPrecache = false; + m_bShouldBatch = false; + m_bShouldSort = true; + m_pFirstCollection = NULL; + m_flCullRadius = 0.0f; + m_flCullFillCost = 1.0f; + m_nRetireCheckFrame = 0; + m_nFallbackCurrentCount = 0; + m_bDrawThroughLeafSystem = true; + m_flMaxTailLength = 0.0f; + m_flMinimumTimeStep = 0; + m_nPerParticleOutlineMaterialVarToken = 0; +} + +inline CParticleSystemDefinition::~CParticleSystemDefinition( void ) +{ + UnlinkAllCollections(); + m_Operators.PurgeAndDeleteElements(); + m_Renderers.PurgeAndDeleteElements(); + m_Initializers.PurgeAndDeleteElements(); + m_Emitters.PurgeAndDeleteElements(); + m_ForceGenerators.PurgeAndDeleteElements(); + m_Constraints.PurgeAndDeleteElements(); +} + +// Used to iterate over all particle collections using the same def +inline CParticleCollection *CParticleSystemDefinition::FirstCollection() +{ + return m_pFirstCollection; +} + +inline float CParticleSystemDefinition::GetCullRadius() const +{ + return m_flCullRadius; +} + +inline float CParticleSystemDefinition::GetCullFillCost() const +{ + return m_flCullFillCost; +} + +inline const char *CParticleSystemDefinition::GetCullReplacementDefinition() const +{ + return m_pszCullReplacementName; +} + +inline int CParticleSystemDefinition::GetCullControlPoint() const +{ + return m_nCullControlPoint; +} + +inline bool CParticleSystemDefinition::HasFallback() const +{ + return ( m_nFallbackMaxCount > 0 ); +} + +inline int CParticleSystemDefinition::GetMinCPULevel() const +{ + return m_nMinCPULevel; +} + +inline int CParticleSystemDefinition::GetMinGPULevel() const +{ + return m_nMinGPULevel; +} + +inline void CParticleSystemDefinition::MarkReadsControlPoint( int nPoint ) +{ + m_nControlPointReadMask |= ( 1ULL << nPoint ); +} + +inline bool CParticleSystemDefinition::IsNonPositionalControlPoint( int nPoint ) const +{ + return ( m_nControlPointNonPositionalMask & ( 1ULL << nPoint ) ) != 0; +} + +inline bool CParticleSystemDefinition::ReadsControlPoint( int nPoint ) const +{ + return ( m_nControlPointReadMask & ( 1ULL << nPoint ) ) != 0; +} + +// Retirement +inline bool CParticleSystemDefinition::HasRetirementBeenChecked( int nFrame ) const +{ + return m_nRetireCheckFrame == nFrame; +} + +inline void CParticleSystemDefinition::MarkRetirementCheck( int nFrame ) +{ + m_nRetireCheckFrame = nFrame; +} + +inline bool CParticleSystemDefinition::ShouldBatch() const +{ + return m_bShouldBatch; +} + +inline bool CParticleSystemDefinition::IsViewModelEffect() const +{ + return m_bViewModelEffect; +} + +inline bool CParticleSystemDefinition::IsScreenSpaceEffect() const +{ + return m_bScreenSpaceEffect; +} + + +inline float CParticleSystemDefinition::GetMaxTailLength() const +{ + return m_flMaxTailLength; +} + +inline void CParticleSystemDefinition::SetMaxTailLength( float flMaxTailLength ) +{ + m_flMaxTailLength = flMaxTailLength; +} + +inline const char *CParticleSystemDefinition::MaterialName() const +{ + return m_pszMaterialName; +} + +inline const DmObjectId_t& CParticleSystemDefinition::GetId() const +{ + return m_Id; +} + +inline int CParticleCollection::GetGroupID( void ) const +{ + return m_pDef->m_nGroupID; +} + +FORCEINLINE const Vector& CParticleCollection::GetControlPointAtCurrentTime( int nControlPoint ) const +{ + Assert( !m_pDef || m_pDef->ReadsControlPoint( nControlPoint ) ); + return ControlPoint( nControlPoint ).m_Position; +} + +FORCEINLINE void CParticleCollection::GetControlPointOrientationAtCurrentTime( int nControlPoint, Vector *pForward, Vector *pRight, Vector *pUp ) const +{ + Assert( nControlPoint <= GetHighestControlPoint() ); + Assert( !m_pDef || m_pDef->ReadsControlPoint( nControlPoint ) ); + + // FIXME: Use quaternion lerp to get control point transform at time + *pForward = ControlPoint( nControlPoint).m_ForwardVector; + *pRight = ControlPoint( nControlPoint ).m_RightVector; + *pUp = ControlPoint( nControlPoint ).m_UpVector; +} + +FORCEINLINE int CParticleCollection::GetControlPointParent( int nControlPoint ) const +{ + Assert( nControlPoint <= GetHighestControlPoint() ); + Assert( !m_pDef || m_pDef->ReadsControlPoint( nControlPoint ) ); + return ControlPoint( nControlPoint ).m_nParent; +} + +FORCEINLINE CParticleSnapshot *CParticleCollection::GetControlPointSnapshot( int nControlPoint ) const +{ + Assert( nControlPoint <= GetHighestControlPoint() ); + if ( nControlPoint == -1 ) + return NULL; + return ControlPoint( nControlPoint ).m_pSnapshot; +} + +#endif // PARTICLES_H diff --git a/public/phonemeconverter.cpp b/public/phonemeconverter.cpp new file mode 100644 index 0000000..933b057 --- /dev/null +++ b/public/phonemeconverter.cpp @@ -0,0 +1,229 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include +#include +#include "tier0/dbg.h" + +struct PhonemeMap_t +{ + const char *string; + int code; + unsigned char byteCode; // needs to be unique + float weight; + bool isStandard; + const char *desc; +}; + +static PhonemeMap_t g_Phonemes[] = +{ + { "b", 'b', 0x01, 0.8f, true, "Big : voiced alveolar stop" }, + { "m", 'm', 0x02, 1.0f, true, "Mat : voiced bilabial nasal" }, + { "p", 'p', 0x03, 0.8f, true, "Put; voiceless alveolar stop" }, + { "w", 'w', 0x04, 1.0f, true, "With : voiced labial-velar approximant" }, + { "f", 'f', 0x05, 0.8f, true, "Fork : voiceless labiodental fricative" }, + { "v", 'v', 0x06, 0.8f, true, "Val : voiced labialdental fricative" }, + { "r", 0x0279, 0x07, 1.0f, true, "Red : voiced alveolar approximant" }, + { "r2", 'r', 0x08, 1.0f, true, "Red : voiced alveolar trill" }, + { "r3", 0x027b, 0x09, 1.0f, true, "Red : voiced retroflex approximant" }, + { "er", 0x025a, 0x0A, 1.2f, true, "URn : rhotacized schwa" }, + { "er2", 0x025d, 0x0B, 1.2f, true, "URn : rhotacized lower-mid central vowel" }, + { "dh", 0x00f0, 0x0C, 1.0f, true, "THen : voiced dental fricative" }, + { "th", 0x03b8, 0x0D, 1.0f, true, "THin : voiceless dental fricative" }, + { "sh", 0x0283, 0x0E, 1.0f, true, "SHe : voiceless postalveolar fricative" }, + { "jh", 0x02a4, 0x0F, 1.0f, true, "Joy : voiced postalveolar afficate" }, + { "ch", 0x02a7, 0x10, 1.0f, true, "CHin : voiceless postalveolar affricate" }, + { "s", 's', 0x11, 0.8f, true, "Sit : voiceless alveolar fricative" }, + { "z", 'z', 0x12, 0.8f, true, "Zap : voiced alveolar fricative" }, + { "d", 'd', 0x13, 0.8f, true, "Dig : voiced bilabial stop" }, + { "d2", 0x027e, 0x14, 0.8f, true, "Dig : voiced alveolar flap or tap" }, + { "l", 'l', 0x15, 0.8f, true, "Lid : voiced alveolar lateral approximant" }, + { "l2", 0x026b, 0x16, 0.8f, true, "Lid : velarized voiced alveolar lateral approximant" }, + { "n", 'n', 0x17, 0.8f, true, "No : voiced alveolar nasal" }, + { "t", 't', 0x18, 0.8f, true, "Talk : voiceless bilabial stop" }, + { "ow", 'o', 0x19, 1.2f, true, "gO : upper-mid back rounded vowel" }, + { "uw", 'u', 0x1A, 1.2f, true, "tOO : high back rounded vowel" }, + { "ey", 'e', 0x1B, 1.0f, true, "Ate : upper-mid front unrounded vowel" }, + { "ae", 0x00e6, 0x1C, 1.0f, true, "cAt : semi-low front unrounded vowel" }, + { "aa", 0x0251, 0x1D, 1.0f, true, "fAther : low back unrounded vowel" }, + { "aa2", 'a', 0x1E, 1.0f, true, "fAther : low front unrounded vowel" }, + { "iy", 'i', 0x1F, 1.0f, true, "fEEl : high front unrounded vowel" }, + { "y", 'j', 0x20, 0.7f, true, "Yacht : voiced palatal approximant" }, + { "ah", 0x028c, 0x21, 1.0f, true, "cUt : lower-mid back unrounded vowel" }, + { "ao", 0x0254, 0x22, 1.2f, true, "dOg : lower-mid back rounded vowel" }, + { "ax", 0x0259, 0x23, 1.0f, true, "Ago : mid-central unrounded vowel" }, + { "ax2", 0x025c, 0x24, 1.0f, true, "Ago : lower-mid central unrounded vowel" }, + { "eh", 0x025b, 0x25, 1.0f, true, "pEt : lower-mid front unrounded vowel"}, + { "ih", 0x026a, 0x26, 1.0f, true, "fIll : semi-high front unrounded vowel" }, + { "ih2", 0x0268, 0x27, 1.0f, true, "fIll : high central unrounded vowel" }, + { "uh", 0x028a, 0x28, 1.0f, true, "bOOk : semi-high back rounded vowel" }, + { "g", 'g', 0x29, 0.8f, true, "taG : voiced velar stop" }, + { "g2", 0x0261, 0x2A, 1.0f, true, "taG : voiced velar stop" }, + { "hh", 'h', 0x2B, 0.8f, true, "Help : voiceless glottal fricative" }, + { "hh2", 0x0266, 0x2C, 0.8f, true, "Help : breathy-voiced glottal fricative" }, + { "c", 'k', 0x2D, 0.6f, true, "Cut : voiceless velar stop" }, + { "nx", 0x014b, 0x2E, 1.0f, true, "siNG : voiced velar nasal" }, + { "zh", 0x0292, 0x2F, 1.0f, true, "aZure : voiced postalveolar fricative" }, + + // Added + { "h", 'h', 0x30, 0.8f, false, "Help : voiceless glottal fricative" }, + { "k", 'k', 0x31, 0.6f, false, "Cut : voiceless velar stop" }, + { "ay", 0x0251, 0x32, 1.0f, false, "fAther : low back unrounded vowel" }, // or possibly +0x026a (ih) + { "ng", 0x014b, 0x33, 1.0f, false, "siNG : voiced velar nasal" }, // nx + { "aw", 0x0251, 0x34, 1.2f, false, "fAther : low back unrounded vowel" }, // // vOWel, // aa + uh??? + { "oy", 'u', 0x35, 1.2f, false, "tOO : high back rounded vowel" }, + + // Silence + { "", '_', 0x00, 1.0f, true, "silence" }, +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : code - +// Output : const char +//----------------------------------------------------------------------------- +const char *ConvertPhoneme( int code ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + PhonemeMap_t *test = &g_Phonemes[ i ]; + if ( test->code == code ) + return test->string; + } + + Warning( "Unrecognized phoneme code %i\n", code ); + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *text - +// Output : int +//----------------------------------------------------------------------------- +int TextToPhoneme( const char *text ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + PhonemeMap_t *test = &g_Phonemes[ i ]; + if ( !stricmp( test->string, text ) ) + return test->code; + } + + Warning( "Unrecognized phoneme %s\n", text ); + return '_'; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : code - +// Output : float +//----------------------------------------------------------------------------- +float WeightForPhonemeCode( int code ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + PhonemeMap_t *test = &g_Phonemes[ i ]; + if ( test->code == code ) + return test->weight; + } + + Warning( "Unrecognized phoneme code %i\n", code ); + return 1.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *text - +// Output : float +//----------------------------------------------------------------------------- +float WeightForPhoneme( char *text ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + PhonemeMap_t *test = &g_Phonemes[ i ]; + if ( !stricmp( test->string, text ) ) + return test->weight; + } + + Warning( "WeightForPhoneme:: Unrecognized phoneme %s\n", text ); + return 1.0f; +} + +int NumPhonemes() +{ + return ARRAYSIZE( g_Phonemes ); +} + +const char *NameForPhonemeByIndex( int index ) +{ + Assert( index >= 0 && index < NumPhonemes() ); + return g_Phonemes[ index ].string; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *text - +// Output : int +//----------------------------------------------------------------------------- +int TextToPhonemeIndex( const char *text ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + PhonemeMap_t *test = &g_Phonemes[ i ]; + if ( !stricmp( test->string, text ) ) + return i; + } + + return -1; +} + +int CodeForPhonemeByIndex( int index ) +{ + if ( index < 0 || index >= NumPhonemes() ) + return '_'; + return g_Phonemes[ index ].code; +} + +bool IsStandardPhoneme( int index ) +{ + if ( index < 0 || index >= NumPhonemes() ) + return false; + return g_Phonemes[ index ].isStandard; +} + +const char *DescForPhonemeByIndex( int index ) +{ + if ( index < 0 || index >= NumPhonemes() ) + return NULL; + return g_Phonemes[ index ].desc; +} + +unsigned char CodeToByteCode( int code ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + if ( g_Phonemes[ i ].code == code ) + { + return g_Phonemes[ i ].byteCode; + } + } + + return 0x00; +} + +int ByteCodeToCode( unsigned char byteCode ) +{ + for ( int i = 0; i < ARRAYSIZE( g_Phonemes ); ++i ) + { + if ( g_Phonemes[ i ].byteCode == byteCode ) + { + return g_Phonemes[ i ].code; + } + } + + return '_'; +} diff --git a/public/phonemeconverter.h b/public/phonemeconverter.h new file mode 100644 index 0000000..d11256b --- /dev/null +++ b/public/phonemeconverter.h @@ -0,0 +1,28 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PHONEMECONVERTER_H +#define PHONEMECONVERTER_H +#ifdef _WIN32 +#pragma once +#endif + +const char *ConvertPhoneme( int code ); +int TextToPhoneme( const char *text ); +float WeightForPhonemeCode( int code ); +float WeightForPhoneme( char *text ); + +int NumPhonemes(); +int TextToPhonemeIndex( const char *text ); +const char *NameForPhonemeByIndex( int index ); +int CodeForPhonemeByIndex( int index ); +const char *DescForPhonemeByIndex( int index ); +bool IsStandardPhoneme( int index ); +unsigned char CodeToByteCode( int code ); +int ByteCodeToCode( unsigned char byteCode ); + +#endif // PHONEMECONVERTER_H diff --git a/public/phonemeextractor/phonemeextractor.h b/public/phonemeextractor/phonemeextractor.h new file mode 100644 index 0000000..01ea4be --- /dev/null +++ b/public/phonemeextractor/phonemeextractor.h @@ -0,0 +1,50 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PHONEMEEXTRACTOR_H +#define PHONEMEEXTRACTOR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +class CSentence; + +typedef enum +{ + SPEECH_API_SAPI = 0, + SPEECH_API_LIPSINC, +} PE_APITYPE; + +typedef enum +{ + SR_RESULT_NORESULT = 0, + SR_RESULT_ERROR, + SR_RESULT_SUCCESS, + SR_RESULT_FAILED +} SR_RESULT; + +abstract_class IPhonemeExtractor +{ +public: + virtual PE_APITYPE GetAPIType() const = 0; + + // Used for menus, etc + virtual char const *GetName() const = 0; + + virtual SR_RESULT Extract( + const char *wavfile, + int numsamples, + void (*pfnPrint)( const char *fmt, ... ), + CSentence& inwords, + CSentence& outwords ) = 0; +}; + +#define VPHONEME_EXTRACTOR_INTERFACE "PHONEME_EXTRACTOR_001" + +#endif // PHONEMEEXTRACTOR_H diff --git a/public/phyfile.h b/public/phyfile.h new file mode 100644 index 0000000..c3e8930 --- /dev/null +++ b/public/phyfile.h @@ -0,0 +1,23 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PHYFILE_H +#define PHYFILE_H +#pragma once + +#include "datamap.h" + +typedef struct phyheader_s +{ + DECLARE_BYTESWAP_DATADESC(); + int size; + int id; + int solidCount; + long checkSum; // checksum of source .mdl file +} phyheader_t; + +#endif // PHYFILE_H diff --git a/public/pixelwriter.h b/public/pixelwriter.h new file mode 100644 index 0000000..596d787 --- /dev/null +++ b/public/pixelwriter.h @@ -0,0 +1,875 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef PIXELWRITER_H +#define PIXELWRITER_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _WIN32 +#define FORCEINLINE_PIXEL FORCEINLINE +#elif POSIX +#define FORCEINLINE_PIXEL inline +#else +#error "implement me" +#endif + +#include "bitmap/imageformat.h" +#include "tier0/dbg.h" +#include "mathlib/compressed_vector.h" +#include "mathlib/ssemath.h" + +//----------------------------------------------------------------------------- +// Color writing class +//----------------------------------------------------------------------------- + +class CPixelWriter +{ +public: + FORCEINLINE void SetPixelMemory( ImageFormat format, void* pMemory, int stride ); + FORCEINLINE void *GetPixelMemory() { return m_pBase; } + + // this is no longer used: +#if 0 // defined( _X360 ) + // set after SetPixelMemory() + FORCEINLINE void ActivateByteSwapping( bool bSwap ); +#endif + + FORCEINLINE void Seek( int x, int y ); + FORCEINLINE void* SkipBytes( int n ); + FORCEINLINE void SkipPixels( int n ); + FORCEINLINE void WritePixel( int r, int g, int b, int a = 255 ); + FORCEINLINE void WritePixelNoAdvance( int r, int g, int b, int a = 255 ); + FORCEINLINE void WritePixelSigned( int r, int g, int b, int a = 255 ); + FORCEINLINE void WritePixelNoAdvanceSigned( int r, int g, int b, int a = 255 ); + FORCEINLINE void ReadPixelNoAdvance( int &r, int &g, int &b, int &a ); + + // Floating point formats + FORCEINLINE void WritePixelNoAdvanceF( float r, float g, float b, float a = 1.0f ); + FORCEINLINE void WritePixelF( float r, float g, float b, float a = 1.0f ); + + // SIMD formats + FORCEINLINE void WritePixel( FLTX4 rgba ); + FORCEINLINE void WritePixelNoAdvance( FLTX4 rgba ); +#ifdef _X360 + // here are some explicit formats so we can avoid the switch: + FORCEINLINE void WritePixelNoAdvance_RGBA8888( FLTX4 rgba ); + FORCEINLINE void WritePixelNoAdvance_BGRA8888( FLTX4 rgba ); + // as above, but with m_pBits passed in to avoid a LHS + FORCEINLINE void WritePixelNoAdvance_BGRA8888( FLTX4 rgba, void *pBits ); + // for writing entire SIMD registers at once when they have + // already been packed, and when m_pBits is vector-aligned + // (which is a requirement for write-combined memory) + // offset is added to m_pBits (saving you from the obligatory + // LHS of a SkipBytes) + FORCEINLINE void WriteFourPixelsExplicitLocation_BGRA8888( FLTX4 rgba, int offset ); +#endif + + + FORCEINLINE unsigned char GetPixelSize() { return m_Size; } + + FORCEINLINE bool IsUsingFloatFormat() const; + FORCEINLINE unsigned char *GetCurrentPixel() { return m_pBits; } + +private: + enum + { + PIXELWRITER_USING_FLOAT_FORMAT = 0x01, + PIXELWRITER_USING_16BIT_FLOAT_FORMAT = 0x02, + PIXELWRITER_SWAPBYTES = 0x04, + }; + + unsigned char* m_pBase; + unsigned char* m_pBits; + unsigned short m_BytesPerRow; + unsigned char m_Size; + unsigned char m_nFlags; + signed short m_RShift; + signed short m_GShift; + signed short m_BShift; + signed short m_AShift; + unsigned int m_RMask; + unsigned int m_GMask; + unsigned int m_BMask; + unsigned int m_AMask; + +#ifdef _X360 + ImageFormat m_Format; +public: + inline const ImageFormat &GetFormat() { return m_Format; } +private: +#endif +}; + +FORCEINLINE_PIXEL bool CPixelWriter::IsUsingFloatFormat() const +{ + return (m_nFlags & PIXELWRITER_USING_FLOAT_FORMAT) != 0; +} + +FORCEINLINE_PIXEL void CPixelWriter::SetPixelMemory( ImageFormat format, void* pMemory, int stride ) +{ + m_pBits = (unsigned char*)pMemory; + m_pBase = m_pBits; + m_BytesPerRow = (unsigned short)stride; + m_nFlags = 0; +#ifdef _X360 + m_Format = format; +#endif + + switch ( format ) + { + case IMAGE_FORMAT_R32F: // NOTE! : the low order bits are first in this naming convention. + m_Size = 4; + m_RShift = 0; + m_GShift = 0; + m_BShift = 0; + m_AShift = 0; + m_RMask = 0xFFFFFFFF; + m_GMask = 0x0; + m_BMask = 0x0; + m_AMask = 0x0; + m_nFlags |= PIXELWRITER_USING_FLOAT_FORMAT; + break; + + case IMAGE_FORMAT_RGBA32323232F: + m_Size = 16; + m_RShift = 0; + m_GShift = 32; + m_BShift = 64; + m_AShift = 96; + m_RMask = 0xFFFFFFFF; + m_GMask = 0xFFFFFFFF; + m_BMask = 0xFFFFFFFF; + m_AMask = 0xFFFFFFFF; + m_nFlags |= PIXELWRITER_USING_FLOAT_FORMAT; + break; + + case IMAGE_FORMAT_RGBA16161616F: + m_Size = 8; + m_RShift = 0; + m_GShift = 16; + m_BShift = 32; + m_AShift = 48; + m_RMask = 0xFFFF; + m_GMask = 0xFFFF; + m_BMask = 0xFFFF; + m_AMask = 0xFFFF; + m_nFlags |= PIXELWRITER_USING_FLOAT_FORMAT | PIXELWRITER_USING_16BIT_FLOAT_FORMAT; + break; + + case IMAGE_FORMAT_RGBA8888: +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_RGBA8888: +#endif + m_Size = 4; + m_RShift = 0; + m_GShift = 8; + m_BShift = 16; + m_AShift = 24; + m_RMask = 0xFF; + m_GMask = 0xFF; + m_BMask = 0xFF; + m_AMask = 0xFF; + break; + + case IMAGE_FORMAT_BGRA8888: // NOTE! : the low order bits are first in this naming convention. +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_BGRA8888: +#endif + m_Size = 4; + m_RShift = 16; + m_GShift = 8; + m_BShift = 0; + m_AShift = 24; + m_RMask = 0xFF; + m_GMask = 0xFF; + m_BMask = 0xFF; + m_AMask = 0xFF; + break; + + case IMAGE_FORMAT_BGRX8888: +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_BGRX8888: +#endif + m_Size = 4; + m_RShift = 16; + m_GShift = 8; + m_BShift = 0; + m_AShift = 24; + m_RMask = 0xFF; + m_GMask = 0xFF; + m_BMask = 0xFF; + m_AMask = 0x00; + break; + + case IMAGE_FORMAT_BGRA4444: + m_Size = 2; + m_RShift = 4; + m_GShift = 0; + m_BShift = -4; + m_AShift = 8; + m_RMask = 0xF0; + m_GMask = 0xF0; + m_BMask = 0xF0; + m_AMask = 0xF0; + break; + + case IMAGE_FORMAT_BGR888: + m_Size = 3; + m_RShift = 16; + m_GShift = 8; + m_BShift = 0; + m_AShift = 0; + m_RMask = 0xFF; + m_GMask = 0xFF; + m_BMask = 0xFF; + m_AMask = 0x00; + break; + + case IMAGE_FORMAT_BGR565: + m_Size = 2; + m_RShift = 8; + m_GShift = 3; + m_BShift = -3; + m_AShift = 0; + m_RMask = 0xF8; + m_GMask = 0xFC; + m_BMask = 0xF8; + m_AMask = 0x00; + break; + + case IMAGE_FORMAT_BGRA5551: + case IMAGE_FORMAT_BGRX5551: + m_Size = 2; + m_RShift = 7; + m_GShift = 2; + m_BShift = -3; + m_AShift = 8; + m_RMask = 0xF8; + m_GMask = 0xF8; + m_BMask = 0xF8; + m_AMask = 0x80; + break; + + // GR - alpha format for HDR support + case IMAGE_FORMAT_A8: + m_Size = 1; + m_RShift = 0; + m_GShift = 0; + m_BShift = 0; + m_AShift = 0; + m_RMask = 0x00; + m_GMask = 0x00; + m_BMask = 0x00; + m_AMask = 0xFF; + break; + + case IMAGE_FORMAT_UVWQ8888: + m_Size = 4; + m_RShift = 0; + m_GShift = 8; + m_BShift = 16; + m_AShift = 24; + m_RMask = 0xFF; + m_GMask = 0xFF; + m_BMask = 0xFF; + m_AMask = 0xFF; + break; + + case IMAGE_FORMAT_RGBA16161616: +#if defined( _X360 ) + case IMAGE_FORMAT_LINEAR_RGBA16161616: +#endif + m_Size = 8; + if ( !IsX360() ) + { + m_RShift = 0; + m_GShift = 16; + m_BShift = 32; + m_AShift = 48; + } + else + { + m_RShift = 48; + m_GShift = 32; + m_BShift = 16; + m_AShift = 0; + } + m_RMask = 0xFFFF; + m_GMask = 0xFFFF; + m_BMask = 0xFFFF; + m_AMask = 0xFFFF; + break; + + case IMAGE_FORMAT_I8: + // whatever goes into R is considered the intensity. + m_Size = 1; + m_RShift = 0; + m_GShift = 0; + m_BShift = 0; + m_AShift = 0; + m_RMask = 0xFF; + m_GMask = 0x00; + m_BMask = 0x00; + m_AMask = 0x00; + break; + // FIXME: Add more color formats as need arises + default: + { + static bool format_error_printed[NUM_IMAGE_FORMATS]; + if ( !format_error_printed[format] ) + { + Assert( 0 ); + Msg( "CPixelWriter::SetPixelMemory: Unsupported image format %i\n", format ); + format_error_printed[format] = true; + } + m_Size = 0; // set to zero so that we don't stomp memory for formats that we don't understand. + } + break; + } +} + +#if 0 // defined( _X360 ) +FORCEINLINE void CPixelWriter::ActivateByteSwapping( bool bSwap ) +{ + // X360TBD: Who is trying to use this? + // Purposely not hooked up because PixelWriter has been ported to read/write native pixels only + Assert( 0 ); + + if ( bSwap && !(m_nFlags & PIXELWRITER_SWAPBYTES ) ) + { + m_nFlags |= PIXELWRITER_SWAPBYTES; + + // only tested with 4 byte formats + Assert( m_Size == 4 ); + } + else if ( !bSwap && (m_nFlags & PIXELWRITER_SWAPBYTES ) ) + { + m_nFlags &= ~PIXELWRITER_SWAPBYTES; + } + else + { + // same state + return; + } + + // swap the shifts + m_RShift = 24-m_RShift; + m_GShift = 24-m_GShift; + m_BShift = 24-m_BShift; + m_AShift = 24-m_AShift; +} +#endif + +//----------------------------------------------------------------------------- +// Sets where we're writing to +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::Seek( int x, int y ) +{ + m_pBits = m_pBase + y * m_BytesPerRow + x * m_Size; +} + + +//----------------------------------------------------------------------------- +// Skips n bytes: +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void* CPixelWriter::SkipBytes( int n ) RESTRICT +{ + m_pBits += n; + return m_pBits; +} + + +//----------------------------------------------------------------------------- +// Skips n pixels: +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::SkipPixels( int n ) +{ + SkipBytes( n * m_Size ); +} + + +//----------------------------------------------------------------------------- +// Writes a pixel without advancing the index PC ONLY +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixelNoAdvanceF( float r, float g, float b, float a ) +{ + Assert( IsUsingFloatFormat() ); + + // X360TBD: Not ported + Assert( IsPC() ); + + if (PIXELWRITER_USING_16BIT_FLOAT_FORMAT & m_nFlags) + { + float16 fp16[4]; + fp16[0].SetFloat( r ); + fp16[1].SetFloat( g ); + fp16[2].SetFloat( b ); + fp16[3].SetFloat( a ); + // fp16 + unsigned short pBuf[4] = { 0, 0, 0, 0 }; + pBuf[ m_RShift >> 4 ] |= (fp16[0].GetBits() & m_RMask) << ( m_RShift & 0xF ); + pBuf[ m_GShift >> 4 ] |= (fp16[1].GetBits() & m_GMask) << ( m_GShift & 0xF ); + pBuf[ m_BShift >> 4 ] |= (fp16[2].GetBits() & m_BMask) << ( m_BShift & 0xF ); + pBuf[ m_AShift >> 4 ] |= (fp16[3].GetBits() & m_AMask) << ( m_AShift & 0xF ); + memcpy( m_pBits, pBuf, m_Size ); + } + else + { + // fp32 + int pBuf[4] = { 0, 0, 0, 0 }; + pBuf[ m_RShift >> 5 ] |= (FloatBits(r) & m_RMask) << ( m_RShift & 0x1F ); + pBuf[ m_GShift >> 5 ] |= (FloatBits(g) & m_GMask) << ( m_GShift & 0x1F ); + pBuf[ m_BShift >> 5 ] |= (FloatBits(b) & m_BMask) << ( m_BShift & 0x1F ); + pBuf[ m_AShift >> 5 ] |= (FloatBits(a) & m_AMask) << ( m_AShift & 0x1F ); + memcpy( m_pBits, pBuf, m_Size ); + } +} + +//----------------------------------------------------------------------------- +// Writes a pixel, advances the write index +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixelF( float r, float g, float b, float a ) +{ + WritePixelNoAdvanceF(r, g, b, a); + m_pBits += m_Size; +} + + +//----------------------------------------------------------------------------- +// Writes a pixel, advances the write index +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixel( int r, int g, int b, int a ) +{ + WritePixelNoAdvance(r,g,b,a); + m_pBits += m_Size; +} + +//----------------------------------------------------------------------------- +// Writes a pixel, advances the write index +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixelSigned( int r, int g, int b, int a ) +{ + WritePixelNoAdvanceSigned(r,g,b,a); + m_pBits += m_Size; +} + + +//----------------------------------------------------------------------------- +// Writes a pixel without advancing the index +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixelNoAdvance( int r, int g, int b, int a ) +{ + Assert( !IsUsingFloatFormat() ); + + if ( m_Size <= 0 ) + { + return; + } + if ( m_Size < 5 ) + { + unsigned int val = (r & m_RMask) << m_RShift; + val |= (g & m_GMask) << m_GShift; + val |= (m_BShift > 0) ? ((b & m_BMask) << m_BShift) : ((b & m_BMask) >> -m_BShift); + val |= (a & m_AMask) << m_AShift; + + switch( m_Size ) + { + default: + Assert( 0 ); + return; + case 1: + { + m_pBits[0] = (unsigned char)((val & 0xff)); + return; + } + case 2: + { + ((unsigned short *)m_pBits)[0] = (unsigned short)((val & 0xffff)); + return; + } + case 3: + { + if ( IsPC() || !IsX360() ) + { + ((unsigned short *)m_pBits)[0] = (unsigned short)((val & 0xffff)); + m_pBits[2] = (unsigned char)((val >> 16) & 0xff); + } + else + { + m_pBits[0] = (unsigned char)(((val >> 16) & 0xff)); + m_pBits[1] = (unsigned char)(((val >> 8 ) & 0xff)); + m_pBits[2] = (unsigned char)(val & 0xff); + } + return; + } + case 4: + { + ((unsigned int *)m_pBits)[0] = val; + return; + } + } + } + else // RGBA32323232 or RGBA16161616 -- PC only. + { + AssertMsg(!IsX360(), "Unsupported lightmap format used in WritePixelNoAdvance(). This is a severe performance fault.\n"); + + int64 val = ( ( int64 )(r & m_RMask) ) << m_RShift; + val |= ( ( int64 )(g & m_GMask) ) << m_GShift; + val |= (m_BShift > 0) ? ((( int64 )( b & m_BMask)) << m_BShift) : (((int64)( b & m_BMask)) >> -m_BShift); + val |= ( ( int64 )(a & m_AMask) ) << m_AShift; + + switch( m_Size ) + { + case 6: + { + if ( IsPC() || !IsX360() ) + { + ((unsigned int *)m_pBits)[0] = val & 0xffffffff; + ((unsigned short *)m_pBits)[2] = (unsigned short)( ( val >> 32 ) & 0xffff ); + } + else + { + ((unsigned int *)m_pBits)[0] = (val >> 16) & 0xffffffff; + ((unsigned short *)m_pBits)[2] = (unsigned short)( val & 0xffff ); + } + return; + } + case 8: + { + if ( IsPC() || !IsX360() ) + { + ((unsigned int *)m_pBits)[0] = val & 0xffffffff; + ((unsigned int *)m_pBits)[1] = ( val >> 32 ) & 0xffffffff; + } + else + { + ((unsigned int *)m_pBits)[0] = ( val >> 32 ) & 0xffffffff; + ((unsigned int *)m_pBits)[1] = val & 0xffffffff; + } + return; + } + default: + Assert( 0 ); + return; + } + } +} + +#ifdef _X360 +// There isn't a PC port of these because of the many varied +// pixel formats the PC deals with. If you write SSE versions +// of all the various necessary packers, then this can be made +// to work on PC. + +//----------------------------------------------------------------------------- +// Writes a pixel, advances the write index +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixel( FLTX4 rgba ) RESTRICT +{ + WritePixelNoAdvance(rgba); + m_pBits += m_Size; +} + +//----------------------------------------------------------------------------- +// Writes a pixel without advancing the index +// rgba are four float values, each on the range 0..255 (though they may leak +// fractionally over 255 due to numerical errors earlier) +//----------------------------------------------------------------------------- +FORCEINLINE_PIXEL void CPixelWriter::WritePixelNoAdvance( FLTX4 rgba ) RESTRICT +{ + Assert( !IsUsingFloatFormat() ); + + switch (m_Size) + { + case 0: + return; + case 4: + { + AssertMsg((reinterpret_cast(m_pBits) & 0x03) == 0,"Unaligned m_pBits in WritePixelNoAdvance!"); + switch ( m_Format ) + { + // note: format names are low-order-byte first. + case IMAGE_FORMAT_RGBA8888: + case IMAGE_FORMAT_LINEAR_RGBA8888: + WritePixelNoAdvance_RGBA8888(rgba); + break; + + case IMAGE_FORMAT_BGRA8888: // NOTE! : the low order bits are first in this naming convention. + case IMAGE_FORMAT_LINEAR_BGRA8888: + WritePixelNoAdvance_BGRA8888(rgba); + break; + + + default: + AssertMsg1(false, "Unknown four-byte pixel format %d in lightmap write.\n", m_Format); + } + break; + } + + default: + AssertMsg1(false, "WritePixelNoAdvance on unsupported 360 %d-byte format\n", m_Size); + break; + } + +} + + +// here are some explicit formats so we can avoid the switch: +FORCEINLINE void CPixelWriter::WritePixelNoAdvance_RGBA8888( FLTX4 rgba ) +{ + // it's easier to do tiered convert-saturates here + // than the d3d color convertor op + + // first permute + const static fltx4 permReverse = XMVectorPermuteControl(3,2,1,0); + fltx4 N = XMVectorPermute(rgba, rgba, permReverse); + + N = __vctuxs(N, 0); // convert to unsigned fixed point 0 w/ saturate + N = __vpkuwus(N, N); // convert to halfword saturate + N = __vpkuhus(N, N); // convert to byte saturate + N = __vspltw(N, 0); // splat w-word to all four + + __stvewx(N, m_pBits, 0); // store whatever word happens to be aligned with m_pBits to that word +} + +FORCEINLINE void CPixelWriter::WritePixelNoAdvance_BGRA8888( FLTX4 rgba ) +{ + WritePixelNoAdvance_BGRA8888( rgba, m_pBits ); +} + +FORCEINLINE void CPixelWriter::WritePixelNoAdvance_BGRA8888( FLTX4 rgba, void * RESTRICT pBits ) RESTRICT +{ + // this happens to be in an order such that we can use the handy builtin packing op + // clamp to 0..255 (coz it might have leaked over) + static const fltx4 vTwoFiftyFive = {255.0f, 255.0f, 255.0f, 255.0f}; + fltx4 N = MinSIMD(vTwoFiftyFive, rgba); + + // the magic number such that when mul-accummulated against rbga, + // gets us a representation 3.0 + (r)*2^-22 -- puts the bits at + // the bottom of the float + static CONST XMVECTOR PackScale = { (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22)), (1.0f / (FLOAT)(1 << 22))}; // 255.0f / (FLOAT)(1 << 22) + static const XMVECTOR Three = {3.0f, 3.0f, 3.0f, 3.0f}; + + N = __vmaddfp(N, PackScale, Three); + N = __vpkd3d(N, N, VPACK_D3DCOLOR, VPACK_32, 3); // pack to X word + N = __vspltw(N, 0); // splat X + + // this is a nasty thing to work around the April XDK bug in __stvewx + { + void * RESTRICT copyOfPBits = pBits; + __stvewx(N, copyOfPBits, 0); + } + +} + +// for writing entire SIMD registers at once +FORCEINLINE void CPixelWriter::WriteFourPixelsExplicitLocation_BGRA8888 ( FLTX4 rgba, int offset ) +{ + Assert( (reinterpret_cast(m_pBits) & 15) == 0 ); // assert alignment + XMStoreVector4A( m_pBits + offset , rgba ); +} + + +#endif + +//----------------------------------------------------------------------------- +// Writes a signed pixel without advancing the index +//----------------------------------------------------------------------------- + +FORCEINLINE_PIXEL void CPixelWriter::WritePixelNoAdvanceSigned( int r, int g, int b, int a ) +{ + Assert( !IsUsingFloatFormat() ); + + if ( m_Size <= 0 ) + { + return; + } + + if ( m_Size < 5 ) + { + int val = (r & m_RMask) << m_RShift; + val |= (g & m_GMask) << m_GShift; + val |= (m_BShift > 0) ? ((b & m_BMask) << m_BShift) : ((b & m_BMask) >> -m_BShift); + val |= (a & m_AMask) << m_AShift; + signed char *pSignedBits = (signed char *)m_pBits; + + if ( IsPC() || !IsX360() ) + { + switch ( m_Size ) + { + case 4: + pSignedBits[3] = (signed char)((val >> 24) & 0xff); + // fall through intentionally. + case 3: + pSignedBits[2] = (signed char)((val >> 16) & 0xff); + // fall through intentionally. + case 2: + pSignedBits[1] = (signed char)((val >> 8) & 0xff); + // fall through intentionally. + case 1: + pSignedBits[0] = (signed char)((val & 0xff)); + // fall through intentionally. + return; + } + } + else + { + switch ( m_Size ) + { + case 4: + pSignedBits[0] = (signed char)((val >> 24) & 0xff); + pSignedBits[1] = (signed char)((val >> 16) & 0xff); + pSignedBits[2] = (signed char)((val >> 8) & 0xff); + pSignedBits[3] = (signed char)(val & 0xff); + break; + case 3: + pSignedBits[0] = (signed char)((val >> 16) & 0xff); + pSignedBits[1] = (signed char)((val >> 8) & 0xff); + pSignedBits[2] = (signed char)(val & 0xff); + break; + case 2: + pSignedBits[0] = (signed char)((val >> 8) & 0xff); + pSignedBits[1] = (signed char)(val & 0xff); + break; + case 1: + pSignedBits[0] = (signed char)(val & 0xff); + break; + } + } + } + else + { + int64 val = ( ( int64 )(r & m_RMask) ) << m_RShift; + val |= ( ( int64 )(g & m_GMask) ) << m_GShift; + val |= (m_BShift > 0) ? ((( int64 )( b & m_BMask)) << m_BShift) : (((int64)( b & m_BMask)) >> -m_BShift); + val |= ( ( int64 )(a & m_AMask) ) << m_AShift; + signed char *pSignedBits = ( signed char * )m_pBits; + + if ( IsPC() || !IsX360() ) + { + switch( m_Size ) + { + case 8: + pSignedBits[7] = (signed char)((val >> 56) & 0xff); + pSignedBits[6] = (signed char)((val >> 48) & 0xff); + // fall through intentionally. + case 6: + pSignedBits[5] = (signed char)((val >> 40) & 0xff); + pSignedBits[4] = (signed char)((val >> 32) & 0xff); + // fall through intentionally. + case 4: + pSignedBits[3] = (signed char)((val >> 24) & 0xff); + // fall through intentionally. + case 3: + pSignedBits[2] = (signed char)((val >> 16) & 0xff); + // fall through intentionally. + case 2: + pSignedBits[1] = (signed char)((val >> 8) & 0xff); + // fall through intentionally. + case 1: + pSignedBits[0] = (signed char)((val & 0xff)); + break; + default: + Assert( 0 ); + return; + } + } + else + { + switch( m_Size ) + { + case 8: + pSignedBits[0] = (signed char)((val >> 56) & 0xff); + pSignedBits[1] = (signed char)((val >> 48) & 0xff); + pSignedBits[2] = (signed char)((val >> 40) & 0xff); + pSignedBits[3] = (signed char)((val >> 32) & 0xff); + pSignedBits[4] = (signed char)((val >> 24) & 0xff); + pSignedBits[5] = (signed char)((val >> 16) & 0xff); + pSignedBits[6] = (signed char)((val >> 8) & 0xff); + pSignedBits[7] = (signed char)(val & 0xff); + break; + case 6: + pSignedBits[0] = (signed char)((val >> 40) & 0xff); + pSignedBits[1] = (signed char)((val >> 32) & 0xff); + pSignedBits[2] = (signed char)((val >> 24) & 0xff); + pSignedBits[3] = (signed char)((val >> 16) & 0xff); + pSignedBits[4] = (signed char)((val >> 8) & 0xff); + pSignedBits[5] = (signed char)(val & 0xff); + break; + case 4: + pSignedBits[0] = (signed char)((val >> 24) & 0xff); + pSignedBits[1] = (signed char)((val >> 16) & 0xff); + pSignedBits[2] = (signed char)((val >> 8) & 0xff); + pSignedBits[3] = (signed char)(val & 0xff); + break; + case 3: + pSignedBits[0] = (signed char)((val >> 16) & 0xff); + pSignedBits[1] = (signed char)((val >> 8) & 0xff); + pSignedBits[2] = (signed char)(val & 0xff); + break; + case 2: + pSignedBits[0] = (signed char)((val >> 8) & 0xff); + pSignedBits[1] = (signed char)(val & 0xff); + break; + case 1: + pSignedBits[0] = (signed char)(val & 0xff); + break; + default: + Assert( 0 ); + return; + } + } + } +} + +FORCEINLINE_PIXEL void CPixelWriter::ReadPixelNoAdvance( int &r, int &g, int &b, int &a ) +{ + Assert( !IsUsingFloatFormat() ); + + int val = m_pBits[0]; + if ( m_Size > 1 ) + { + if ( IsPC() || !IsX360() ) + { + val |= (int)m_pBits[1] << 8; + if ( m_Size > 2 ) + { + val |= (int)m_pBits[2] << 16; + if ( m_Size > 3 ) + { + val |= (int)m_pBits[3] << 24; + } + } + } + else + { + val <<= 8; + val |= (int)m_pBits[1]; + if ( m_Size > 2 ) + { + val <<= 8; + val |= (int)m_pBits[2]; + if ( m_Size > 3 ) + { + val <<= 8; + val |= (int)m_pBits[3]; + } + } + } + } + + r = (val>>m_RShift) & m_RMask; + g = (val>>m_GShift) & m_GMask; + b = (val>>m_BShift) & m_BMask; + a = (val>>m_AShift) & m_AMask; +} + +#endif // PIXELWRITER_H; diff --git a/public/posedebugger.cpp b/public/posedebugger.cpp new file mode 100644 index 0000000..23f6af0 --- /dev/null +++ b/public/posedebugger.cpp @@ -0,0 +1,666 @@ +//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "mathlib/mathlib.h" +#include "tier0/tslist.h" +#include "tier1/utlmap.h" +#include "tier1/convar.h" + +#include "bone_setup.h" + +#include "con_nprint.h" +#include "cdll_int.h" +#include "globalvars_base.h" + +#include "posedebugger.h" + +#include "iclientnetworkable.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +extern IVEngineClient *engine; +extern CGlobalVarsBase *gpGlobals; + +static ConVar ui_posedebug_fade_in_time( "ui_posedebug_fade_in_time", "0.2", + FCVAR_CHEAT | FCVAR_DONTRECORD, + "Time during which a new pose activity layer is shown in green in +posedebug UI" ); +static ConVar ui_posedebug_fade_out_time( "ui_posedebug_fade_out_time", "0.8", + FCVAR_CHEAT | FCVAR_DONTRECORD, + "Time to keep a no longer active pose activity layer in red until removing it from +posedebug UI" ); + + +////////////////////////////////////////////////////////////////////////// +// +// CPoseDebuggerStub : IPoseDebugger +// empty interface implementation +// +////////////////////////////////////////////////////////////////////////// + +class CPoseDebuggerStub : public IPoseDebugger +{ +public: + virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) { } + virtual void AccumulatePose( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) { } +}; + +static CPoseDebuggerStub s_PoseDebuggerStub; +IPoseDebugger *g_pPoseDebugger = &s_PoseDebuggerStub; + +////////////////////////////////////////////////////////////////////////// +// +// CPoseDebuggerImpl : IPoseDebugger +// Purpose: Main implementation of the pose debugger +// Declaration +// +////////////////////////////////////////////////////////////////////////// + +class ModelPoseDebugInfo +{ +public: + ModelPoseDebugInfo() : m_iEntNum( 0 ), m_iCurrentText( 0 ) { } + + +public: + // Entity number + int m_iEntNum; + + // Currently processed text + int m_iCurrentText; + + // Info Text Flags + enum InfoTextFlags + { + F_SEEN_THIS_FRAME = 1 << 0, + F_SEEN_LAST_FRAME = 1 << 1, + }; + + struct InfoText + { + InfoText() { memset( this, 0, sizeof( *this ) ); } + + // Flags + uint32 m_uiFlags; + + // Time seen + float m_flTimeToLive, m_flTimeAlive; + + // Activity/label + int m_iActivity; + char m_chActivity[100]; + char m_chLabel[100]; + + // Text + char m_chTextLines[4][256]; + enum + { + MAX_TEXT_LINES = 4 + }; + }; + + CCopyableUtlVector< InfoText > m_arrTxt; + + +public: + // Add an info text + void AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld ); + + // Lookup an info text + InfoText *LookupInfoText( InfoText *x ); + + // Print pending info text + void PrintPendingInfoText( int &rnPosPrint ); +}; + +void ModelPoseDebugInfo::AddInfoText( InfoText *x, ModelPoseDebugInfo *pOld ) +{ + if ( x ) + { + // Try to set the proper flags on the info text + x->m_uiFlags &= ~F_SEEN_LAST_FRAME; + x->m_uiFlags |= F_SEEN_THIS_FRAME; + } + + // If we have smth to compare against + if ( pOld ) + { + // Search for the same activity/label in the other model pose debug info + ModelPoseDebugInfo &o = *pOld; + int k = o.m_iCurrentText; + if ( x ) + { + for ( ; k < o.m_arrTxt.Count(); ++ k ) + { + InfoText &txt = o.m_arrTxt[k]; + if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) && + !stricmp( x->m_chActivity, txt.m_chActivity ) && + !stricmp( x->m_chLabel, txt.m_chLabel ) && + ( x->m_iActivity == txt.m_iActivity ) ) + { + x->m_flTimeAlive = txt.m_flTimeAlive; + break; + } + } + } + else + { + k = o.m_arrTxt.Count(); + } + + // Range of finished activities + int iFinishedRange[2] = { o.m_iCurrentText, k }; + + // Check whether this is a new message + if ( k == o.m_arrTxt.Count() ) + { + if ( !x ) + { + o.m_iCurrentText = k; + } + else + { + // Don't update the current when insertion happens and don't have finished commands + iFinishedRange[1] = iFinishedRange[0]; + } + } + else + { + o.m_iCurrentText = k + 1; + if ( x ) + { + x->m_uiFlags |= F_SEEN_LAST_FRAME; + x->m_flTimeAlive += gpGlobals->frametime; + } + } + + // Everything before finished + for ( int iFinished = iFinishedRange[0]; iFinished < iFinishedRange[1]; ++ iFinished ) + { + InfoText &txtFinished = o.m_arrTxt[ iFinished ]; + + if ( txtFinished.m_uiFlags & F_SEEN_THIS_FRAME ) + txtFinished.m_uiFlags |= F_SEEN_LAST_FRAME; + + txtFinished.m_uiFlags &= ~F_SEEN_THIS_FRAME; + + txtFinished.m_flTimeToLive -= gpGlobals->frametime; + txtFinished.m_flTimeAlive += gpGlobals->frametime; + + if ( txtFinished.m_flTimeToLive >= 0.0f ) + m_arrTxt.AddToTail( txtFinished ); + } + } + + if ( x ) + { + // Now add it to the array + x->m_flTimeToLive = ui_posedebug_fade_out_time.GetFloat(); + m_arrTxt.AddToTail( *x ); + } +} + +ModelPoseDebugInfo::InfoText * ModelPoseDebugInfo::LookupInfoText( InfoText *x ) +{ + int k = m_iCurrentText; + if ( x ) + { + for ( ; k < m_arrTxt.Count(); ++ k ) + { + InfoText &txt = m_arrTxt[k]; + if ( ( txt.m_uiFlags & F_SEEN_THIS_FRAME ) && + !stricmp( x->m_chActivity, txt.m_chActivity ) && + !stricmp( x->m_chLabel, txt.m_chLabel ) && + ( x->m_iActivity == txt.m_iActivity ) ) + { + return &txt; + } + } + } + return NULL; +} + +void ModelPoseDebugInfo::PrintPendingInfoText( int &rnPosPrint ) +{ + con_nprint_s nxPrn = { 0 }; + nxPrn.time_to_live = -1; + nxPrn.color[0] = 1.0f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 1.0f; + nxPrn.fixed_width_font = true; + + float const flFadeInTime = ui_posedebug_fade_in_time.GetFloat(); + float const flFadeOutTime = ui_posedebug_fade_out_time.GetFloat(); + + // Now print all the accumulated spew + for ( int k = m_iCurrentText; k < m_arrTxt.Count(); ++ k ) + { + InfoText &prntxt = m_arrTxt[k]; + + switch( prntxt.m_uiFlags & ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) ) + { + case ( F_SEEN_LAST_FRAME | F_SEEN_THIS_FRAME ) : + nxPrn.color[0] = 1.f; + nxPrn.color[1] = 1.f; + nxPrn.color[2] = 1.f; + if ( prntxt.m_flTimeAlive > flFadeInTime ) + break; + else + NULL; // Fall-through to keep showing in green + case F_SEEN_THIS_FRAME : + if ( flFadeInTime > 0.f ) + { + nxPrn.color[0] = 1.f * prntxt.m_flTimeAlive / flFadeInTime; + nxPrn.color[1] = 1.f; + nxPrn.color[2] = 1.f * prntxt.m_flTimeAlive / flFadeInTime; + } + else + { + nxPrn.color[0] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f; + nxPrn.color[1] = 1.f; + nxPrn.color[2] = ( prntxt.m_flTimeAlive > 0.0f ) ? 1.f : 0.f; + } + break; + case F_SEEN_LAST_FRAME : + case 0: + if ( flFadeOutTime > 0.f ) + nxPrn.color[0] = 1.f * prntxt.m_flTimeToLive / flFadeOutTime; + else + nxPrn.color[0] = ( prntxt.m_flTimeToLive > 0.0f ) ? 1.f : 0.f; + nxPrn.color[1] = 0.f; + nxPrn.color[2] = 0.f; + break; + } + + nxPrn.index = ( rnPosPrint += 1 ); + engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[0] ); + + for ( int iLine = 1; iLine < ModelPoseDebugInfo::InfoText::MAX_TEXT_LINES; ++ iLine) + { + if ( !prntxt.m_chTextLines[iLine][0] ) + break; + + nxPrn.index = ( rnPosPrint += 1 ); + engine->Con_NXPrintf( &nxPrn, "%s", prntxt.m_chTextLines[iLine] ); + } + } + + m_iCurrentText = m_arrTxt.Count(); +} + + + +class CPoseDebuggerImpl : public IPoseDebugger +{ +public: + CPoseDebuggerImpl(); + ~CPoseDebuggerImpl(); + +public: + void ShowAllModels( bool bShow ); + void ShowModel( int iEntNum, bool bShow ); + bool IsModelShown( int iEntNum ) const; + +public: + virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ); + virtual void AccumulatePose( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ); + +protected: + typedef CUtlMap< CStudioHdr const *, ModelPoseDebugInfo > MapModel; + MapModel m_mapModel, m_mapModelOld; + int m_nPosPrint; + + CBitVec< MAX_EDICTS > m_uiMaskShowModels; + + CStudioHdr const *m_pLastModel; +}; + +static CPoseDebuggerImpl s_PoseDebuggerImpl; + +////////////////////////////////////////////////////////////////////////// +// +// CPoseDebuggerImpl +// Implementation +// +////////////////////////////////////////////////////////////////////////// + +CPoseDebuggerImpl::CPoseDebuggerImpl() : + m_mapModel( DefLessFunc( CStudioHdr const * ) ), + m_mapModelOld( DefLessFunc( CStudioHdr const * ) ), + m_nPosPrint( 0 ), + m_pLastModel( NULL ) +{ + m_uiMaskShowModels.SetAll(); +} + +CPoseDebuggerImpl::~CPoseDebuggerImpl() +{ + // g_pPoseDebugger = &s_PoseDebuggerStub; +} + +void CPoseDebuggerImpl::ShowAllModels( bool bShow ) +{ + bShow ? m_uiMaskShowModels.SetAll() : m_uiMaskShowModels.ClearAll(); +} + +void CPoseDebuggerImpl::ShowModel( int iEntNum, bool bShow ) +{ + Assert( iEntNum < MAX_EDICTS ); + if ( iEntNum < MAX_EDICTS ) + m_uiMaskShowModels.Set( iEntNum, bShow ); +} + +bool CPoseDebuggerImpl::IsModelShown( int iEntNum ) const +{ + Assert( iEntNum < MAX_EDICTS ); + if ( iEntNum >= 0 && iEntNum < MAX_EDICTS ) + return m_uiMaskShowModels.IsBitSet( iEntNum ); + else + return false; +} + +void CPoseDebuggerImpl::StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) +{ +// virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel(); +// if ( !pVMdl ) +// return; + + if ( !ThreadInMainThread() ) + { + ExecuteOnce( "Turn of threading when using pose debugger\n" ); + return; + } + + // If we are starting a new model then finalize the previous one + if ( pStudioHdr != m_pLastModel && m_pLastModel ) + { + MapModel::IndexType_t idx = m_mapModel.Find( m_pLastModel ); + if ( idx != m_mapModel.InvalidIndex() ) + { + ModelPoseDebugInfo &mpi = m_mapModel.Element( idx ); + ModelPoseDebugInfo *pMpiOld = NULL; + MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( m_pLastModel ); + if ( idxMapModelOld != m_mapModelOld.InvalidIndex() ) + { + pMpiOld = &m_mapModelOld.Element( idxMapModelOld ); + } + mpi.AddInfoText( NULL, pMpiOld ); + mpi.PrintPendingInfoText( m_nPosPrint ); + } + } + m_pLastModel = pStudioHdr; + + // Go ahead with the new model + studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr(); + if ( !pRMdl || + !pRMdl->numincludemodels ) + return; + + // Entity number + int iEntNum = pEntity->entindex(); + if ( !IsModelShown( iEntNum ) ) + return; + + // Check if we saw the model + if ( m_mapModel.Find( pStudioHdr ) != m_mapModel.InvalidIndex() ) + { + // Initialize the printing position + m_nPosPrint = 9; + + // Swap the maps + m_mapModelOld.RemoveAll(); + m_mapModelOld.Swap( m_mapModel ); + + // Zero out the text on the old map + for ( int k = m_mapModelOld.FirstInorder(); + k != m_mapModelOld.InvalidIndex(); + k = m_mapModelOld.NextInorder( k ) ) + { + ModelPoseDebugInfo &mpi = m_mapModelOld[k]; + mpi.m_iCurrentText = 0; + } + } + else + { + // Next model + m_nPosPrint += 3; + } + + ModelPoseDebugInfo mpi; + mpi.m_iEntNum = iEntNum; + m_mapModel.Insert( pStudioHdr, mpi ); + + con_nprint_s nxPrn = { 0 }; + nxPrn.index = m_nPosPrint; + nxPrn.time_to_live = -1; + nxPrn.color[0] = 0.9f, nxPrn.color[1] = 1.0f, nxPrn.color[2] = 0.9f; + nxPrn.fixed_width_font = false; + + engine->Con_NXPrintf( &nxPrn, "[ %2d ] Model: %s", iEntNum, pRMdl->pszName() ); + m_nPosPrint += 3; +} + +void CPoseDebuggerImpl::AccumulatePose( const CStudioHdr *pStudioHdr, CIKContext *pIKContext, + Vector pos[], Quaternion q[], int sequence, float cycle, + const float poseParameter[], int boneMask, + float flWeight, float flTime ) +{ +// virtualmodel_t const *pVMdl = pStudioHdr->GetVirtualModel(); +// if ( !pVMdl ) +// return; + + if ( !ThreadInMainThread() ) + { + return; + } + + studiohdr_t const *pRMdl = pStudioHdr->GetRenderHdr(); + if ( !pRMdl || + !pRMdl->numincludemodels ) + return; + + MapModel::IndexType_t idxMapModel = m_mapModel.Find( pStudioHdr ); + if ( idxMapModel == m_mapModel.InvalidIndex() ) + return; + + ModelPoseDebugInfo &mpi = m_mapModel.Element( idxMapModel ); + if ( !IsModelShown( mpi.m_iEntNum ) ) + return; + + ModelPoseDebugInfo *pMpiOld = NULL; + MapModel::IndexType_t idxMapModelOld = m_mapModelOld.Find( pStudioHdr ); + if ( idxMapModelOld != m_mapModelOld.InvalidIndex() ) + { + pMpiOld = &m_mapModelOld.Element( idxMapModelOld ); + } + + + // + // Actual processing + // + + mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence ); + + if ( sequence >= pStudioHdr->GetNumSeq() ) + { + sequence = 0; + seqdesc = ((CStudioHdr *)pStudioHdr)->pSeqdesc( sequence ); + } + + enum + { + widthActivity = 35, + widthLayer = 35, + widthIks = 60, + widthPercent = 6, + }; + + // Prepare the text + char chBuffer[256]; + ModelPoseDebugInfo::InfoText txt; + int numLines = 0; + + txt.m_iActivity = seqdesc.activity; + sprintf( txt.m_chActivity, "%s", seqdesc.pszActivityName() ); + sprintf( txt.m_chLabel, "%s", seqdesc.pszLabel() ); + + if ( !txt.m_chActivity[0] ) + { + // Try to find the last seen activity and re-use it + for ( int iLast = mpi.m_arrTxt.Count(); iLast --> 0; ) + { + ModelPoseDebugInfo::InfoText &lastSeenTxt = mpi.m_arrTxt[iLast]; + if ( lastSeenTxt.m_uiFlags & ModelPoseDebugInfo::F_SEEN_THIS_FRAME && + lastSeenTxt.m_chActivity[0] ) + { + sprintf( txt.m_chActivity, "%s", lastSeenTxt.m_chActivity ); + break; + } + } + } + + // The layer information + ModelPoseDebugInfo::InfoText *pOldTxt = pMpiOld ? pMpiOld->LookupInfoText( &txt ) : NULL; + sprintf( txt.m_chTextLines[numLines], + "%-*s %-*s %*.2f %*.1f/%-*d %*.0f%% ", + widthActivity, + seqdesc.pszActivityName(), + widthLayer, + seqdesc.pszLabel(), + 7, + pOldTxt ? pOldTxt->m_flTimeAlive : 0.f, + 5, + cycle * ( ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes - 1 ), + 3, + ((CStudioHdr *)pStudioHdr)->pAnimdesc( seqdesc.anim( 0, 0 ) ).numframes, + widthPercent, + flWeight * 100.0f + ); + ++ numLines; + + if ( seqdesc.numiklocks ) + { + sprintf( chBuffer, + "iklocks : %-2d : ", + seqdesc.numiklocks ); + + for ( int k = 0; k < seqdesc.numiklocks; ++ k ) + { + mstudioiklock_t *plock = seqdesc.pIKLock( k ); + mstudioikchain_t *pchain = pStudioHdr->pIKChain( plock->chain ); + + sprintf( chBuffer + strlen( chBuffer ), "%s ", pchain->pszName() ); + // plock->flPosWeight; + // plock->flLocalQWeight; + } + + sprintf( txt.m_chTextLines[numLines], + "%-*s", + widthIks, + chBuffer + ); + ++ numLines; + } + + if ( seqdesc.numikrules ) + { + sprintf( chBuffer, "ikrules : %-2d", + seqdesc.numikrules ); + + sprintf( txt.m_chTextLines[numLines], + "%-*s", + widthIks, + chBuffer + ); + ++ numLines; + } + + + // Now add the accumulated text into the container + mpi.AddInfoText( &txt, pMpiOld ); + mpi.PrintPendingInfoText( m_nPosPrint ); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Con-commands +// +////////////////////////////////////////////////////////////////////////// + +static void IN_PoseDebuggerStart( const CCommand &args ) +{ + if ( args.ArgC() <= 1 ) + { + // No args, enable all + s_PoseDebuggerImpl.ShowAllModels( true ); + } + else + { + // If explicitly showing the pose debugger when it was disabled + if ( g_pPoseDebugger != &s_PoseDebuggerImpl ) + { + s_PoseDebuggerImpl.ShowAllModels( false ); + } + + // Show only specific models + for ( int k = 1; k < args.ArgC(); ++ k ) + { + int iEntNum = atoi( args.Arg( k ) ); + s_PoseDebuggerImpl.ShowModel( iEntNum, true ); + } + } + + g_pPoseDebugger = &s_PoseDebuggerImpl; +} + +static void IN_PoseDebuggerEnd( const CCommand &args ) +{ + if ( args.ArgC() <= 1 ) + { + // No args, disable all + s_PoseDebuggerImpl.ShowAllModels( false ); + + // Set the stub pointer + g_pPoseDebugger = &s_PoseDebuggerStub; + } + else + { + // Hide only specific models + for ( int k = 1; k < args.ArgC(); ++ k ) + { + int iEntNum = atoi( args.Arg( k ) ); + s_PoseDebuggerImpl.ShowModel( iEntNum, false ); + } + } +} + +static ConCommand posedebuggerstart( "+posedebug", IN_PoseDebuggerStart, "Turn on pose debugger or add ents to pose debugger UI", FCVAR_CHEAT ); +static ConCommand posedebuggerend ( "-posedebug", IN_PoseDebuggerEnd, "Turn off pose debugger or hide ents from pose debugger UI", FCVAR_CHEAT ); diff --git a/public/posedebugger.h b/public/posedebugger.h new file mode 100644 index 0000000..fb5e95c --- /dev/null +++ b/public/posedebugger.h @@ -0,0 +1,38 @@ +//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef POSEDEBUGGER_H +#define POSEDEBUGGER_H +#ifdef _WIN32 +#pragma once +#endif + +class IClientNetworkable; + +abstract_class IPoseDebugger +{ +public: + virtual void StartBlending( IClientNetworkable *pEntity, const CStudioHdr *pStudioHdr ) = 0; + + virtual void AccumulatePose( + const CStudioHdr *pStudioHdr, + CIKContext *pIKContext, + Vector pos[], + Quaternion q[], + int sequence, + float cycle, + const float poseParameter[], + int boneMask, + float flWeight, + float flTime + ) = 0; +}; + +extern IPoseDebugger *g_pPoseDebugger; + +#endif // #ifndef POSEDEBUGGER_H diff --git a/public/r_efx.h b/public/r_efx.h new file mode 100644 index 0000000..4954902 --- /dev/null +++ b/public/r_efx.h @@ -0,0 +1,38 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#if !defined ( EFXH ) +#define EFXH +#ifdef _WIN32 +#pragma once +#endif + +#include "iefx.h" + +class IMaterial; +struct dlight_t; + +class CVEfx : public IVEfx +{ +public: + virtual ~CVEfx() {} + + virtual int Draw_DecalIndexFromName ( char *name ); + virtual void DecalShoot ( int textureIndex, int entity, const model_t *model, const Vector& model_origin, const QAngle& model_angles, const Vector& position, const Vector *saxis, int flags, const Vector *pNormal = NULL ); + virtual void DecalColorShoot ( int textureIndex, int entity, const model_t *model, const Vector& model_origin, const QAngle& model_angles, const Vector& position, const Vector *saxis, int flags, const color32 &rgbaColor, const Vector *pNormal = NULL ); + virtual void PlayerDecalShoot ( IMaterial *material, void *userdata, int entity, const model_t *model, const Vector& model_origin, const QAngle& model_angles, + const Vector& position, const Vector *saxis, int flags, const color32 &rgbaColor ); + virtual dlight_t *CL_AllocDlight ( int key ); + virtual dlight_t *CL_AllocElight ( int key ); + virtual int CL_GetActiveDLights ( dlight_t *pList[MAX_DLIGHTS] ); + virtual const char *Draw_DecalNameFromIndex ( int nIndex ); + virtual dlight_t *GetElightByKey ( int key ); +}; + +extern CVEfx *g_pEfx; + +#endif \ No newline at end of file diff --git a/public/raytrace.h b/public/raytrace.h new file mode 100644 index 0000000..c083e2e --- /dev/null +++ b/public/raytrace.h @@ -0,0 +1,420 @@ +// $Id:$ + +#ifndef RAYTRACE_H +#define RAYTRACE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// fast SSE-ONLY ray tracing module. Based upon various "real time ray tracing" research. +//#define DEBUG_RAYTRACE 1 + +class FourRays +{ +public: + FourVectors origin; + FourVectors direction; + + inline void Check(void) const + { + // in order to be valid to trace as a group, all four rays must have the same signs in all + // of their direction components +#ifndef NDEBUG + for(int c=1;c<4;c++) + { + Assert(direction.X(0)*direction.X(c)>=0); + Assert(direction.Y(0)*direction.Y(c)>=0); + Assert(direction.Z(0)*direction.Z(c)>=0); + } +#endif + } + // returns direction sign mask for 4 rays. returns -1 if the rays can not be traced as a + // bundle. + int CalculateDirectionSignMask(void) const; + +}; + +/// The format a triangle is stored in for intersections. size of this structure is important. +/// This structure can be in one of two forms. Before the ray tracing environment is set up, the +/// ProjectedEdgeEquations hold the coordinates of the 3 vertices, for facilitating bounding box +/// checks needed while building the tree. afterwards, they are changed into the projected ege +/// equations for intersection purposes. +enum triangleflags +{ + FCACHETRI_TRANSPARENT = 0x01, + FCACHETRI_NEGATIVE_NORMAL = 0x02, +}; + + +struct TriIntersectData_t +{ + // this structure is 16longs=64 bytes for cache line packing. + float m_flNx, m_flNy, m_flNz; // plane equation + float m_flD; + + int32 m_nTriangleID; // id of the triangle. + + float m_ProjectedEdgeEquations[6]; // A,B,C for each edge equation. a + // point is inside the triangle if + // a*c1+b*c2+c is negative for all 3 + // edges. + + uint8 m_nCoordSelect0,m_nCoordSelect1; // the triangle is projected onto a 2d + // plane for edge testing. These are + // the indices (0..2) of the + // coordinates preserved in the + // projection + + uint8 m_nFlags; // triangle flags + uint8 m_unused0; // no longer used +}; + + +struct TriGeometryData_t +{ + int32 m_nTriangleID; // id of the triangle. + + float m_VertexCoordData[9]; // can't use a vector in a union + + uint8 m_nFlags; // triangle flags + signed char m_nTmpData0; // used by kd-tree builder + signed char m_nTmpData1; // used by kd-tree builder + + + // accessors to get around union annoyance + FORCEINLINE Vector &Vertex(int idx) + { + return * ( reinterpret_cast ( m_VertexCoordData+3*idx ) ); + } + +}; + + +struct CacheOptimizedTriangle +{ + + union + { + TriIntersectData_t m_IntersectData; + TriGeometryData_t m_GeometryData; + } m_Data; + + // accessors to get around union annoyance + FORCEINLINE Vector &Vertex(int idx) + { + return * ( reinterpret_cast (m_Data.m_GeometryData.m_VertexCoordData+3*idx ) ); + } + + FORCEINLINE const Vector &Vertex(int idx) const + { + return * ( reinterpret_cast (m_Data.m_GeometryData.m_VertexCoordData+3*idx ) ); + } + + void ChangeIntoIntersectionFormat(void); // change information storage format for + // computing intersections. + + int ClassifyAgainstAxisSplit(int split_plane, float split_value); // PLANECHECK_xxx below + + // Debug - take a triangle that has been converted to intersection format and extract the vertices + // from by intersecting the planes + void ExtractVerticesFromIntersectionFormat( Vector &v0, Vector &v1, Vector &v2 ) const; +}; + +#define PLANECHECK_POSITIVE 1 +#define PLANECHECK_NEGATIVE -1 +#define PLANECHECK_STRADDLING 0 + +#define KDNODE_STATE_XSPLIT 0 // this node is an x split +#define KDNODE_STATE_YSPLIT 1 // this node is a ysplit +#define KDNODE_STATE_ZSPLIT 2 // this node is a zsplit +#define KDNODE_STATE_LEAF 3 // this node is a leaf + +struct CacheOptimizedKDNode +{ + // this is the cache intensive data structure. "Tricks" are used to fit it into 8 bytes: + // + // A) the right child is always stored after the left child, which means we only need one + // pointer + // B) The type of node (KDNODE_xx) is stored in the lower 2 bits of the pointer. + // C) for leaf nodes, we store the number of triangles in the leaf in the same place as the floating + // point splitting parameter is stored in a non-leaf node + + int32 Children; // child idx, or'ed with flags above + float SplittingPlaneValue; // for non-leaf nodes, the nodes on the + // "high" side of the splitting plane + // are on the right + +#ifdef DEBUG_RAYTRACE + Vector vecMins; + Vector vecMaxs; +#endif + + inline int NodeType(void) const + + { + return Children & 3; + } + + inline int32 TriangleIndexStart(void) const + { + assert(NodeType()==KDNODE_STATE_LEAF); + return Children>>2; + } + + inline int LeftChild(void) const + { + assert(NodeType()!=KDNODE_STATE_LEAF); + return Children>>2; + } + + inline int RightChild(void) const + { + return LeftChild()+1; + } + + inline int NumberOfTrianglesInLeaf(void) const + { + assert(NodeType()==KDNODE_STATE_LEAF); + return *((int32 *) &SplittingPlaneValue); + } + + inline void SetNumberOfTrianglesInLeafNode(int n) + { + *((int32 *) &SplittingPlaneValue)=n; + } + +protected: + + +}; + + +struct RayTracingSingleResult +{ + Vector surface_normal; // surface normal at intersection + int32 HitID; // -1=no hit. otherwise, triangle index + float HitDistance; // distance to intersection + float ray_length; // leng of initial ray +}; + +struct RayTracingResult +{ + FourVectors surface_normal; // surface normal at intersection + ALIGN16 int32 HitIds[4] ALIGN16_POST; // -1=no hit. otherwise, triangle index + fltx4 HitDistance; // distance to intersection +}; + + +class RayTraceLight +{ +public: + FourVectors Position; + FourVectors Intensity; +}; + + +#define RTE_FLAGS_FAST_TREE_GENERATION 1 +#define RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS 2 // saves memory if not needed +#define RTE_FLAGS_DONT_STORE_TRIANGLE_MATERIALS 4 + +enum RayTraceLightingMode_t { + DIRECT_LIGHTING, // just dot product lighting + DIRECT_LIGHTING_WITH_SHADOWS, // with shadows + GLOBAL_LIGHTING // global light w/ shadows +}; + + +class RayStream +{ + friend class RayTracingEnvironment; + + RayTracingSingleResult *PendingStreamOutputs[8][4]; + int n_in_stream[8]; + FourRays PendingRays[8]; + +public: + RayStream(void) + { + memset(n_in_stream,0,sizeof(n_in_stream)); + } +}; + +// When transparent triangles are in the list, the caller can provide a callback that will get called at each triangle +// allowing the callback to stop processing if desired. +// UNDONE: This is not currently SIMD - it really only supports single rays +// Also for efficiency FourRays really needs some kind of active mask for the cases where rays get unbundled +class ITransparentTriangleCallback +{ +public: + virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *hitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID ) = 0; +}; + +enum RTECullMode_t +{ + RTE_CULL_NONE = 0, + RTE_CULL_FRONT, + RTE_CULL_BACK +}; + +// serialization flag bits and defines +#define RT_ENV_SERIALIZE_COLORS 1 + +class RayTracingEnvironment +{ +public: + uint32 Flags; // RTE_FLAGS_xxx above + Vector m_MinBound; + Vector m_MaxBound; + + FourVectors BackgroundColor; //< color where no intersection + CUtlVector OptimizedKDTree; //< the packed kdtree. root is 0 + CUtlBlockVector OptimizedTriangleList; //< the packed triangles + CUtlVector TriangleIndexList; //< the list of triangle indices. + CUtlVector LightList; //< the list of lights + CUtlVector TriangleColors; //< color of tries + CUtlVector TriangleMaterials; //< material index of tries + +public: + RayTracingEnvironment() : OptimizedTriangleList( 1024 ) + { + BackgroundColor.DuplicateVector(Vector(1,0,0)); // red + Flags=0; + } + +#if !( defined ( _DEBUG ) && defined ( HAMMER_RAYTRACE ) ) + inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_( "RayTracingEnvironment" ); return MemAlloc_AllocAligned( size, 16 ); } + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_( "RayTracingEnvironment" ); return MemAlloc_AllocAligned( size, 16 ); } + inline void operator delete( void* p ) { MemAlloc_FreeAligned( p ); } + inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { MemAlloc_FreeAligned( p ); } +#endif + + // call AddTriangle to set up the world + void AddTriangle(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, + const Vector &color); + + // Adds a triangle with flags & material + void AddTriangle(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, + const Vector &color, uint16 flags, int32 materialIndex); + + + void AddQuad(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, + const Vector &v4, // specify vertices in cw or ccw order + const Vector &color); + + // for ease of testing. + void AddAxisAlignedRectangularSolid(int id,Vector mincoord, Vector Maxcoord, + const Vector &color); + + + // SetupAccelerationStructure to prepare for tracing + void SetupAccelerationStructure(void); + + // lowest level intersection routine - fire 4 rays through the scene. all 4 rays must pass the + // Check() function, and t extents must be initialized. skipid can be set to exclude a + // particular id (such as the origin surface). This function finds the closest intersection. + template + void Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax,int DirectionSignMask, + RayTracingResult *rslt_out, + int32 skip_id=-1, ITransparentTriangleCallback *pCallback = NULL ); + + // wrapper for the low level trace4 rays routine + void Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax,int DirectionSignMask, + RayTracingResult *rslt_out, + int32 skip_id=-1, ITransparentTriangleCallback *pCallback = NULL, RTECullMode_t cullMode = RTE_CULL_NONE ); + + // higher level intersection routine that handles computing the mask and handling rays which do not match in direciton sign + void Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, + RayTracingResult *rslt_out, + int32 skip_id=-1, ITransparentTriangleCallback *pCallback = NULL, RTECullMode_t cullMode = RTE_CULL_NONE ); + + // compute virtual light sources to model inter-reflection + void ComputeVirtualLightSources(void); + + + // high level interface - pass viewing parameters, rendering flags, and a destination frame + // buffer, and get a ray traced scene in 32-bit rgba format + void RenderScene(int width, int height, // width and height of desired rendering + int stride, // actual width in pixels of target buffer + uint32 *output_buffer, // pointer to destination + Vector CameraOrigin, // eye position + Vector ULCorner, // word space coordinates of upper left + // monitor corner + Vector URCorner, // top right corner + Vector LLCorner, // lower left + Vector LRCorner, // lower right + RayTraceLightingMode_t lightmode=DIRECT_LIGHTING); + + + /// raytracing stream - lets you trace an array of rays by feeding them to this function. + /// results will not be returned until FinishStream is called. This function handles sorting + /// the rays by direction, tracing them 4 at a time, and de-interleaving the results. + + void AddToRayStream(RayStream &s, + Vector const &start,Vector const &end,RayTracingSingleResult *rslt_out, + RTECullMode_t cullMode = RTE_CULL_NONE); + + inline void FlushStreamEntry(RayStream &s, int msk, RTECullMode_t cullMode = RTE_CULL_NONE); + + /// call this when you are done. handles all cleanup. After this is called, all rslt ptrs + /// previously passed to AddToRaySteam will have been filled in. + void FinishRayStream(RayStream &s, RTECullMode_t cullMode = RTE_CULL_NONE); + + + int MakeLeafNode(int first_tri, int last_tri); + + + float CalculateCostsOfSplit( + int split_plane,int32 const *tri_list,int ntris, + Vector MinBound,Vector MaxBound, float &split_value, + int &nleft, int &nright, int &nboth); + + void RefineNode(int node_number,int32 const *tri_list,int ntris, + Vector MinBound,Vector MaxBound, int depth); + + void CalculateTriangleListBounds(int32 const *tris,int ntris, + Vector &minout, Vector &maxout); + + void AddInfinitePointLight(Vector position, // light center + Vector intensity); // rgb amount + + // use the global variables set by LoadBSPFile to populated the RayTracingEnvironment with + // faces. + void InitializeFromLoadedBSP(void); + + void AddBSPFace(int id,dface_t const &face); + + // MakeRoomForTriangles - a hint telling it how many triangles we are going to add so that + // the utl vectors used can be pre-allocated + void MakeRoomForTriangles( int ntriangles ); + + const CacheOptimizedTriangle &GetTriangle( int32 triID ) + { + return OptimizedTriangleList[triID]; + } + + int32 GetTriangleMaterial( int32 triID ) + { + return TriangleMaterials[triID]; + } + const Vector &GetTriangleColor( int triID ) + { + return TriangleColors[triID]; + } + + // (un)serialization + size_t GetSerializationNumBytes( uint32 nSerializationFlags = 0 ) const; + void Serialize( CUtlBuffer &outbuf, uint32 nSerializationFlags = 0 ) const; + void UnSerialize( CUtlBuffer &inbuf ); + +}; + + + +#endif diff --git a/public/registry.cpp b/public/registry.cpp new file mode 100644 index 0000000..cdbd07b --- /dev/null +++ b/public/registry.cpp @@ -0,0 +1,420 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#if defined( WIN32 ) && !defined( _X360 ) +#include +#endif +#include "tier0/platform.h" +#include "iregistry.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: Exposes registry interface to rest of launcher +//----------------------------------------------------------------------------- +class CRegistry : public IRegistry +{ +public: + CRegistry( void ); + virtual ~CRegistry( void ); + + virtual bool Init( const char *platformName ); + virtual bool DirectInit( const char *subDirectoryUnderValve ); + virtual void Shutdown( void ); + + virtual int ReadInt( const char *key, int defaultValue = 0); + virtual void WriteInt( const char *key, int value ); + + virtual const char *ReadString( const char *key, const char *defaultValue = NULL ); + virtual void WriteString( const char *key, const char *value ); + + // Read/write helper methods + virtual int ReadInt( const char *pKeyBase, const char *pKey, int defaultValue = 0 ); + virtual void WriteInt( const char *pKeyBase, const char *key, int value ); + virtual const char *ReadString( const char *pKeyBase, const char *key, const char *defaultValue ); + virtual void WriteString( const char *pKeyBase, const char *key, const char *value ); + +private: + bool m_bValid; +#ifdef WIN32 + HKEY m_hKey; +#endif +}; + +// Creates it and calls Init +IRegistry *InstanceRegistry( char const *subDirectoryUnderValve ) +{ + CRegistry *instance = new CRegistry(); + instance->DirectInit( subDirectoryUnderValve ); + return instance; +} + +// Calls Shutdown and deletes it +void ReleaseInstancedRegistry( IRegistry *reg ) +{ + if ( !reg ) + { + Assert( !"ReleaseInstancedRegistry( reg == NULL )!" ); + return; + } + + reg->Shutdown(); + delete reg; +} + +// Expose to launcher +static CRegistry g_Registry; +IRegistry *registry = ( IRegistry * )&g_Registry; + +//----------------------------------------------------------------------------- +// Read/write helper methods +//----------------------------------------------------------------------------- +int CRegistry::ReadInt( const char *pKeyBase, const char *pKey, int defaultValue ) +{ + int nLen = strlen( pKeyBase ); + int nKeyLen = strlen( pKey ); + char *pFullKey = (char*)_alloca( nLen + nKeyLen + 2 ); + Q_snprintf( pFullKey, nLen + nKeyLen + 2, "%s\\%s", pKeyBase, pKey ); + return ReadInt( pFullKey, defaultValue ); +} + +void CRegistry::WriteInt( const char *pKeyBase, const char *pKey, int value ) +{ + int nLen = strlen( pKeyBase ); + int nKeyLen = strlen( pKey ); + char *pFullKey = (char*)_alloca( nLen + nKeyLen + 2 ); + Q_snprintf( pFullKey, nLen + nKeyLen + 2, "%s\\%s", pKeyBase, pKey ); + WriteInt( pFullKey, value ); +} + +const char *CRegistry::ReadString( const char *pKeyBase, const char *pKey, const char *defaultValue ) +{ + int nLen = strlen( pKeyBase ); + int nKeyLen = strlen( pKey ); + char *pFullKey = (char*)_alloca( nLen + nKeyLen + 2 ); + Q_snprintf( pFullKey, nLen + nKeyLen + 2, "%s\\%s", pKeyBase, pKey ); + return ReadString( pFullKey, defaultValue ); +} + +void CRegistry::WriteString( const char *pKeyBase, const char *pKey, const char *value ) +{ + int nLen = strlen( pKeyBase ); + int nKeyLen = strlen( pKey ); + char *pFullKey = (char*)_alloca( nLen + nKeyLen + 2 ); + Q_snprintf( pFullKey, nLen + nKeyLen + 2, "%s\\%s", pKeyBase, pKey ); + WriteString( pFullKey, value ); +} + +#ifndef POSIX + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRegistry::CRegistry( void ) +{ + // Assume failure + m_bValid = false; + m_hKey = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRegistry::~CRegistry( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Read integer from registry +// Input : *key - +// defaultValue - +// Output : int +//----------------------------------------------------------------------------- +int CRegistry::ReadInt( const char *key, int defaultValue /*= 0*/ ) +{ + LONG lResult; // Registry function result code + DWORD dwType; // Type of key + DWORD dwSize; // Size of element data + + int value; + + if ( !m_bValid ) + { + return defaultValue; + } + + dwSize = sizeof( DWORD ); + + lResult = RegQueryValueEx( + m_hKey, // handle to key + key, // value name + 0, // reserved + &dwType, // type buffer + (LPBYTE)&value, // data buffer + &dwSize ); // size of data buffer + + if (lResult != ERROR_SUCCESS) // Failure + return defaultValue; + + if (dwType != REG_DWORD) + return defaultValue; + + return value; +} + +//----------------------------------------------------------------------------- +// Purpose: Save integer to registry +// Input : *key - +// value - +//----------------------------------------------------------------------------- +void CRegistry::WriteInt( const char *key, int value ) +{ + // Size of element data + DWORD dwSize; + + if ( !m_bValid ) + { + return; + } + + dwSize = sizeof( DWORD ); + + RegSetValueEx( + m_hKey, // handle to key + key, // value name + 0, // reserved + REG_DWORD, // type buffer + (LPBYTE)&value, // data buffer + dwSize ); // size of data buffer +} + +//----------------------------------------------------------------------------- +// Purpose: Read string value from registry +// Input : *key - +// *defaultValue - +// Output : const char +//----------------------------------------------------------------------------- +const char *CRegistry::ReadString( const char *key, const char *defaultValue /* = NULL */ ) +{ + LONG lResult; + // Type of key + DWORD dwType; + // Size of element data + DWORD dwSize = 512; + + static char value[ 512 ]; + + value[0] = 0; + + if ( !m_bValid ) + { + return defaultValue; + } + + lResult = RegQueryValueEx( + m_hKey, // handle to key + key, // value name + 0, // reserved + &dwType, // type buffer + (unsigned char *)value, // data buffer + &dwSize ); // size of data buffer + + if ( lResult != ERROR_SUCCESS ) + { + return defaultValue; + } + + if ( dwType != REG_SZ ) + { + return defaultValue; + } + + return value; +} + +//----------------------------------------------------------------------------- +// Purpose: Save string to registry +// Input : *key - +// *value - +//----------------------------------------------------------------------------- +void CRegistry::WriteString( const char *key, const char *value ) +{ + DWORD dwSize; // Size of element data + + if ( !m_bValid ) + { + return; + } + + dwSize = (DWORD)( strlen( value ) + 1 ); + + RegSetValueEx( + m_hKey, // handle to key + key, // value name + 0, // reserved + REG_SZ, // type buffer + (LPBYTE)value, // data buffer + dwSize ); // size of data buffer +} + + + + +bool CRegistry::DirectInit( const char *subDirectoryUnderValve ) +{ + LONG lResult; // Registry function result code + ULONG dwDisposition; // Type of key opening event + + char szModelKey[ 1024 ]; + wsprintf( szModelKey, "Software\\Valve\\%s", subDirectoryUnderValve ); + + lResult = RegCreateKeyEx( + HKEY_CURRENT_USER, // handle of open key + szModelKey, // address of name of subkey to open + 0ul, // DWORD ulOptions, // reserved + NULL, // Type of value + REG_OPTION_NON_VOLATILE, // Store permanently in reg. + KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask + NULL, + &m_hKey, // Key we are creating + &dwDisposition ); // Type of creation + + if ( lResult != ERROR_SUCCESS ) + { + m_bValid = false; + return false; + } + + // Success + m_bValid = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Open default launcher key based on game directory +//----------------------------------------------------------------------------- +bool CRegistry::Init( const char *platformName ) +{ + char subDir[ 512 ]; + wsprintf( subDir, "%s\\Settings", platformName ); + return DirectInit( subDir ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRegistry::Shutdown( void ) +{ + if ( !m_bValid ) + return; + + // Make invalid + m_bValid = false; + RegCloseKey( m_hKey ); +} + +#else + + + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRegistry::CRegistry( void ) +{ + // Assume failure + m_bValid = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRegistry::~CRegistry( void ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Read integer from registry +// Input : *key - +// defaultValue - +// Output : int +//----------------------------------------------------------------------------- +int CRegistry::ReadInt( const char *key, int defaultValue /*= 0*/ ) +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Save integer to registry +// Input : *key - +// value - +//----------------------------------------------------------------------------- +void CRegistry::WriteInt( const char *key, int value ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Read string value from registry +// Input : *key - +// *defaultValue - +// Output : const char +//----------------------------------------------------------------------------- +const char *CRegistry::ReadString( const char *key, const char *defaultValue /* = NULL */ ) +{ + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Save string to registry +// Input : *key - +// *value - +//----------------------------------------------------------------------------- +void CRegistry::WriteString( const char *key, const char *value ) +{ +} + + + +bool CRegistry::DirectInit( const char *subDirectoryUnderValve ) +{ + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Open default launcher key based on game directory +//----------------------------------------------------------------------------- +bool CRegistry::Init( const char *platformName ) +{ + char subDir[ 512 ]; + snprintf( subDir, sizeof(subDir), "%s\\Settings", platformName ); + return DirectInit( subDir ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRegistry::Shutdown( void ) +{ + if ( !m_bValid ) + return; + + // Make invalid + m_bValid = false; +} +#endif // POSIX + diff --git a/public/renderparm.h b/public/renderparm.h new file mode 100644 index 0000000..8734934 --- /dev/null +++ b/public/renderparm.h @@ -0,0 +1,77 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: defines constants to use for the materialsystem and shaderapi +// SetxxxRenderingParameter functions +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef RENDERPARM_H +#define RENDERPARM_H + +#ifndef _WIN32 +#pragma once +#endif + +enum RenderParamInt_t +{ + INT_RENDERPARM_ENABLE_FIXED_LIGHTING = 0, + INT_RENDERPARM_MORPH_ACCUMULATOR_X_OFFSET, + INT_RENDERPARM_MORPH_ACCUMULATOR_Y_OFFSET, + INT_RENDERPARM_MORPH_ACCUMULATOR_SUBRECT_WIDTH, + INT_RENDERPARM_MORPH_ACCUMULATOR_SUBRECT_HEIGHT, + INT_RENDERPARM_MORPH_ACCUMULATOR_4TUPLE_COUNT, + + INT_RENDERPARM_MORPH_WEIGHT_X_OFFSET, + INT_RENDERPARM_MORPH_WEIGHT_Y_OFFSET, + INT_RENDERPARM_MORPH_WEIGHT_SUBRECT_WIDTH, + INT_RENDERPARM_MORPH_WEIGHT_SUBRECT_HEIGHT, + + INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, + + INT_RENDERPARM_BACK_BUFFER_INDEX, + + MAX_INT_RENDER_PARMS = 20 +}; + +// for INT_RENDERPARM_BACK_BUFFER_INDEX +#define BACK_BUFFER_INDEX_DEFAULT 0 +#define BACK_BUFFER_INDEX_HDR 1 + +enum RenderParamFloat_t +{ + FLOAT_RENDERPARM_DEST_ALPHA_DEPTH_SCALE = 0, + FLOAT_RENDERPARM_SPECULAR_POWER, + + MAX_FLOAT_RENDER_PARMS = 20 +}; + +enum RenderParamTexture_t +{ + TEXTURE_RENDERPARM_AMBIENT_OCCLUSION = 0, + TEXTURE_RENDERPARM_FOW = 1, + + MAX_TEXTURE_RENDER_PARMS = 2 +}; + + +enum RenderParamVector_t +{ + VECTOR_RENDERPARM_GLOBAL_LIGHT_DIRECTION = 0, + VECTOR_RENDERPARM_GLOBAL_FOW_MINS, + VECTOR_RENDERPARM_GLOBAL_FOW_MAXS, + VECTOR_RENDERPARM_WIND_DIRECTION, + + MAX_VECTOR_RENDER_PARMS = 20 +}; + +// ENABLE_FIXED_LIGHTING modes: +#define ENABLE_FIXED_LIGHTING_NONE 0 +#define ENABLE_FIXED_LIGHTING_BASICLIGHT 1 +#define ENABLE_FIXED_LIGHTING_OUTPUTMRTS_FOR_DEFERRED_LIGHTING 2 +#define ENABLE_FIXED_LIGHTING_OUTPUTNORMAL_AND_DEPTH 3 + + + +#endif // RENDERPARM_H diff --git a/public/responserules/response_host_interface.h b/public/responserules/response_host_interface.h new file mode 100644 index 0000000..86eedc8 --- /dev/null +++ b/public/responserules/response_host_interface.h @@ -0,0 +1,61 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_HOST_INTERFACE_H +#define RESPONSE_HOST_INTERFACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "filesystem.h" +class IUniformRandomStream; +class ICommandLine; + +namespace ResponseRules +{ + // FUNCTIONS YOU MUST IMPLEMENT IN THE HOST EXECUTABLE: + // These are functions that are mentioned in the header, but need their bodies implemented + // in the .dll that links against this lib. + // This is to wrap functions that previously came from the engine interface + // back when the response rules were inside the server.dll . Now that the rules + // are included into a standalone editor, we don't necessarily have an engine around, + // so there needs to be some other implementation. + abstract_class IEngineEmulator + { + public: + /// Given an input text buffer data pointer, parses a single token into the variable token and returns the new + /// reading position + virtual const char *ParseFile( const char *data, char *token, int maxlen ) = 0; + + /// Return a pointer to an IFileSystem we can use to read and process scripts. + virtual IFileSystem *GetFilesystem() = 0; + + /// Return a pointer to an instance of an IUniformRandomStream + virtual IUniformRandomStream *GetRandomStream() = 0 ; + + /// Return a pointer to a tier0 ICommandLine + virtual ICommandLine *GetCommandLine() = 0; + + /// Emulates the server's UTIL_LoadFileForMe + virtual byte *LoadFileForMe( const char *filename, int *pLength ) = 0; + + /// Emulates the server's UTIL_FreeFile + virtual void FreeFile( byte *buffer ) = 0; + + + /// Somewhere in the host executable you should define this symbol and + /// point it at a singleton instance. + static IEngineEmulator *s_pSingleton; + + // this is just a function that returns the pointer above -- just in + // case we need to define it differently. And I get asserts this way. + static IEngineEmulator *Get(); + }; +}; + + +#endif \ No newline at end of file diff --git a/public/responserules/response_types.h b/public/responserules/response_types.h new file mode 100644 index 0000000..d23f414 --- /dev/null +++ b/public/responserules/response_types.h @@ -0,0 +1,418 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Core types for the response rules -- criteria, responses, rules, and matchers. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RESPONSE_TYPES_H +#define RESPONSE_TYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlrbtree.h" +#include "tier1/utlsymbol.h" +#include "tier2/interval.h" +#include "mathlib/compressed_vector.h" +#include "datamap.h" +#include "soundflags.h" +#include "tier1/utlsymbol.h" + +namespace ResponseRules +{ + /// Custom symbol table for the response rules. + extern CUtlSymbolTable g_RS; +}; + +#ifdef _MANAGED +// forward declare some editor types just so we can friend them. +namespace ResponseRulesCLI +{ + ref class ResponseQueryResult; +} +#endif + +namespace ResponseRules +{ + using ::DataMapAccess; + // using ::DataMapInit; + class CResponseSystem; + +#pragma pack(push,1) + template + struct response_interval_t + { + T start; + T range; + + interval_t &ToInterval( interval_t &dest ) const { dest.start = start; dest.range = range; return dest; } + void FromInterval( const interval_t &from ) { start = from.start; range = from.range; } + float Random() const { interval_t temp = { start, range }; return RandomInterval( temp ); } + }; + + typedef response_interval_t responseparams_interval_t; +#pragma pack(pop) + +#pragma pack(push,1) + struct AI_ResponseFollowup + { + + + // TODO: make less wasteful of memory, by using a symbol table. + const char *followup_concept; // 12 -- next response + const char *followup_contexts; // 16 + float followup_delay; // 20 + const char *followup_target; // 24 -- to whom is this despatched? + // AIConceptHandle_t hConcept; + const char *followup_entityiotarget; //< if this rule involves firing entity io + const char *followup_entityioinput; //< if this rule involves firing entity io + float followup_entityiodelay; + bool bFired; + + inline bool IsValid( void ) const { return (followup_concept && followup_contexts); } + inline void Invalidate() { followup_concept = NULL; followup_contexts = NULL; } + inline void SetFired( bool fired ) { bFired = fired; } + inline bool HasBeenFired() { return bFired; } + + AI_ResponseFollowup( void ) : followup_concept(NULL), followup_contexts(NULL), followup_delay(0), followup_target(NULL), followup_entityiotarget(NULL), followup_entityioinput(NULL), followup_entityiodelay(0), bFired(false) + {}; + AI_ResponseFollowup( char *_followup_concept, char *_followup_contexts, float _followup_delay, char *_followup_target, + char *_followup_entityiotarget, char *_followup_entityioinput, float _followup_entityiodelay ) : + followup_concept(_followup_concept), followup_contexts(_followup_contexts), followup_delay(_followup_delay), followup_target(_followup_target), + followup_entityiotarget(_followup_entityiotarget), followup_entityioinput(_followup_entityioinput), followup_entityiodelay(_followup_entityiodelay), + bFired(false) + {}; + }; +#pragma pack(pop) + + + enum ResponseType_t + { + RESPONSE_NONE = 0, + RESPONSE_SPEAK, + RESPONSE_SENTENCE, + RESPONSE_SCENE, + RESPONSE_RESPONSE, // A reference to another response by name + RESPONSE_PRINT, + RESPONSE_ENTITYIO, // poke an input on an entity + + NUM_RESPONSES, + }; + + +#pragma pack(push,1) + struct ResponseParams + { + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + enum + { + RG_DELAYAFTERSPEAK = (1<<0), + RG_SPEAKONCE = (1<<1), + RG_ODDS = (1<<2), + RG_RESPEAKDELAY = (1<<3), + RG_SOUNDLEVEL = (1<<4), + RG_DONT_USE_SCENE = (1<<5), + RG_STOP_ON_NONIDLE = (1<<6), + RG_WEAPONDELAY = (1<<7), + RG_DELAYBEFORESPEAK = (1<<8), + }; + + ResponseParams() + { + flags = 0; + odds = 100; + delay.start = 0; + delay.range = 0; + respeakdelay.start = 0; + respeakdelay.range = 0; + weapondelay.start = 0; + weapondelay.range = 0; + soundlevel = 0; + predelay.start = 0; + predelay.range = 0; + } + responseparams_interval_t delay; //4 + responseparams_interval_t respeakdelay; //8 + responseparams_interval_t weapondelay; //12 + + short odds; //14 + + short flags; //16 + byte soundlevel; //17 + + responseparams_interval_t predelay; //21 + + ALIGN32 AI_ResponseFollowup *m_pFollowup; + + }; +#pragma pack(pop) + + class CriteriaSet + { + public: + typedef CUtlSymbol CritSymbol_t; ///< just to make it clear that some symbols come out of our special static table + public: + CriteriaSet(); + CriteriaSet( const CriteriaSet& src ); + CriteriaSet( const char *criteria, const char *value ) ; // construct initialized with a key/value pair (convenience) + ~CriteriaSet(); + + static CritSymbol_t ComputeCriteriaSymbol( const char *criteria ); + void AppendCriteria( CritSymbol_t criteria, const char *value = "", float weight = 1.0f ); + void AppendCriteria( const char *criteria, const char *value = "", float weight = 1.0f ); + void AppendCriteria( const char *criteria, float value, float weight = 1.0f ); + void RemoveCriteria( const char *criteria ); + + void Describe() const; + + int GetCount() const; + int FindCriterionIndex( CritSymbol_t criteria ) const; + int FindCriterionIndex( const char *name ) const; + inline bool IsValidIndex( int index ) const; + + CritSymbol_t GetNameSymbol( int nIndex ) const; + inline static const char *SymbolToStr( const CritSymbol_t &symbol ); + const char *GetName( int index ) const; + const char *GetValue( int index ) const; + float GetWeight( int index ) const; + + /// Merge another CriteriaSet into this one. + void Merge( const CriteriaSet *otherCriteria ); + void Merge( const char *modifiers ); // add criteria parsed from a text string + + /// add all of the contexts herein onto an entity. all durations are infinite. + void WriteToEntity( CBaseEntity *pEntity ); + + // Accessors to things that need only be done under unusual circumstances. + inline void EnsureCapacity( int num ); + void Reset(); // clear out this criteria (should not be necessary) + + /// When this is true, calls to AppendCriteria on a criteria that already exists + /// will override the existing value. (This is the default behavior). Can be temporarily + /// set false to prevent such overrides. + inline void OverrideOnAppend( bool bOverride ) { m_bOverrideOnAppend = bOverride; } + + // For iteration from beginning to end (also should not be necessary except in + // save/load) + inline int Head() const; + inline int Next( int i ) const; // use with IsValidIndex above + + const static char kAPPLYTOWORLDPREFIX = '$'; + + /// A last minute l4d2 change: deferred contexts prefixed with a '$' + /// character are actually applied to the world. This matches the + /// related hack in CBaseEntity::AppplyContext. + /// This function works IN-PLACE on the "from" parameter. + /// any $-prefixed criteria in pFrom become prefixed by "world", + /// and are also written into pSetOnWorld. + /// *IF* a response matches using the modified criteria, then and only + /// then should you write back the criteria in pSetOnWorld to the world + /// entity, subsequent to the match but BEFORE the dispatch. + /// Returns the number of contexts modified. If it returns 0, then + /// pSetOnWorld is empty. + static int InterceptWorldSetContexts( CriteriaSet * RESTRICT pFrom, + CriteriaSet * RESTRICT pSetOnWorld ); + + private: + void RemoveCriteria( int idx, bool bTestForPrefix ); + + struct CritEntry_t + { + CritEntry_t() : + criterianame( UTL_INVAL_SYMBOL ), + weight( 0.0f ) + { + value[ 0 ] = 0; + } + + CritEntry_t( const CritEntry_t& src ) + { + criterianame = src.criterianame; + value[ 0 ] = 0; + weight = src.weight; + SetValue( src.value ); + } + + CritEntry_t& operator=( const CritEntry_t& src ) + { + if ( this == &src ) + return *this; + + criterianame = src.criterianame; + weight = src.weight; + SetValue( src.value ); + + return *this; + } + + static bool LessFunc( const CritEntry_t& lhs, const CritEntry_t& rhs ) + { + return lhs.criterianame < rhs.criterianame; + } + + void SetValue( char const *str ) + { + if ( !str ) + { + value[ 0 ] = 0; + } + else + { + Q_strncpy( value, str, sizeof( value ) ); + } + } + + CritSymbol_t criterianame; + char value[ 64 ]; + float weight; + }; + + static CUtlSymbolTable sm_CriteriaSymbols; + typedef CUtlRBTree< CritEntry_t, short > Dict_t; + Dict_t m_Lookup; + int m_nNumPrefixedContexts; // number of contexts prefixed with kAPPLYTOWORLDPREFIX + bool m_bOverrideOnAppend; + }; + + inline void CriteriaSet::EnsureCapacity( int num ) + { + m_Lookup.EnsureCapacity(num); + } + + //----------------------------------------------------------------------------- + // Purpose: Generic container for a response to a match to a criteria set + // This is what searching for a response returns + //----------------------------------------------------------------------------- + + class CRR_Response + { + public: + DECLARE_SIMPLE_DATADESC_INSIDE_NAMESPACE(); + + CRR_Response(); + CRR_Response( const CRR_Response &from ); + CRR_Response &operator=( const CRR_Response &from ); + ~CRR_Response(); + private: + void operator delete(void* p); // please do not new or delete CRR_Responses. + public: + + // void Release(); // we no longer encourage new and delete on these things + + void GetName( char *buf, size_t buflen ) const; + void GetResponse( char *buf, size_t buflen ) const; + const ResponseParams *GetParams() const { return &m_Params; } + ResponseType_t GetType() const { return (ResponseType_t)m_Type; } + soundlevel_t GetSoundLevel() const; + float GetRespeakDelay() const; + float GetWeaponDelay() const; + bool GetSpeakOnce() const; + bool ShouldntUseScene( ) const; + bool ShouldBreakOnNonIdle( void ) const; + int GetOdds() const; + float GetDelay() const; + float GetPreDelay() const; + + inline bool IsEmpty() const; // true iff my response name is empty + void Invalidate() ; // wipe out my contents, mark me invalid + + // Get/set the contexts we apply to character and world after execution + void SetContext( const char *context ); + const char * GetContext( void ) const { return m_szContext; } + + // Get/set the score I matched with (under certain circumstances) + inline float GetMatchScore( void ) { return m_fMatchScore; } + inline void SetMatchScore( float f ) { m_fMatchScore = f; } + + bool IsApplyContextToWorld( void ) { return m_bApplyContextToWorld; } + + void Describe( const CriteriaSet *pDebugCriteria = NULL ); + + void Init( ResponseType_t type, + const char *responseName, + const ResponseParams& responseparams, + const char *matchingRule, + const char *applyContext, + bool bApplyContextToWorld ); + + static const char *DescribeResponse( ResponseType_t type ); + + enum + { + MAX_RESPONSE_NAME = 64, + MAX_RULE_NAME = 64 + }; + + + private: + byte m_Type; + char m_szResponseName[ MAX_RESPONSE_NAME ]; + char m_szMatchingRule[ MAX_RULE_NAME ]; + + ResponseParams m_Params; + float m_fMatchScore; // when instantiated dynamically in SpeakFindResponse, the score of the rule that matched it. + + char * m_szContext; // context data we apply to character after running + bool m_bApplyContextToWorld; + +#ifdef _MANAGED + friend ref class ResponseRulesCLI::ResponseQueryResult; +#endif + }; + + + + abstract_class IResponseFilter + { + public: + virtual bool IsValidResponse( ResponseType_t type, const char *pszValue ) = 0; + }; + + abstract_class IResponseSystem + { + public: + virtual ~IResponseSystem() {} + + virtual bool FindBestResponse( const CriteriaSet& set, CRR_Response& response, IResponseFilter *pFilter = NULL ) = 0; + virtual void GetAllResponses( CUtlVector *pResponses ) = 0; + virtual void PrecacheResponses( bool bEnable ) = 0; + }; + + + + // INLINE FUNCTIONS + + // Used as a failsafe in finding responses. + bool CRR_Response::IsEmpty() const + { + return m_szResponseName[0] == 0; + } + + inline bool CriteriaSet::IsValidIndex( int index ) const + { + return ( index >= 0 && index < ((int)(m_Lookup.Count())) ); + } + + inline int CriteriaSet::Head() const + { + return m_Lookup.FirstInorder(); + } + + inline int CriteriaSet::Next( int i ) const + { + return m_Lookup.NextInorder(i); + } + + inline const char *CriteriaSet::SymbolToStr( const CritSymbol_t &symbol ) + { + return sm_CriteriaSymbols.String(symbol); + } + +} + +#include "rr_speechconcept.h" +#include "response_host_interface.h" + +#endif diff --git a/public/responserules/rr_speechconcept.h b/public/responserules/rr_speechconcept.h new file mode 100644 index 0000000..65b1bb6 --- /dev/null +++ b/public/responserules/rr_speechconcept.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Class data for an AI Concept, an atom of response-driven dialog. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RR_SPEECHCONCEPT_H +#define RR_SPEECHCONCEPT_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "utlsymbol.h" + +#define RR_CONCEPTS_ARE_STRINGS 0 + + +typedef CUtlSymbolTable CRR_ConceptSymbolTable; + +namespace ResponseRules +{ +class CRR_Concept +{ +public: // local typedefs + typedef CUtlSymbol tGenericId; // an int-like type that can be used to refer to all concepts of this type + tGenericId m_iConcept; + +public: + CRR_Concept() {}; + // construct concept from a string. + CRR_Concept(const char *fromString); + + // Return as a string + const char *GetStringConcept() const; + static const char *GetStringForGenericId(tGenericId genericId); + + operator tGenericId() const { return m_iConcept; } + operator const char *() const { return GetStringConcept(); } + inline bool operator==(const CRR_Concept &other) // default is compare by concept ids + { + return m_iConcept == other.m_iConcept; + } + bool operator==(const char *pszConcept); + +protected: + +private: + // dupe a concept + // CRR_Concept& operator=(CRR_Concept &other); + CRR_Concept& operator=(const char *fromString); +}; +}; + + +#endif diff --git a/public/rope_physics.cpp b/public/rope_physics.cpp new file mode 100644 index 0000000..225e205 --- /dev/null +++ b/public/rope_physics.cpp @@ -0,0 +1,152 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +#include "rope_physics.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CBaseRopePhysics::CBaseRopePhysics( CSimplePhysics::CNode *pNodes, int nNodes, CRopeSpring *pSprings, float *flSpringDistsSqr ) +{ + m_pNodes = pNodes; + m_pSprings = pSprings; + m_flNodeSpringDistsSqr = flSpringDistsSqr; + m_flSpringDist = m_flSpringDistSqr = 1; + Restart(); + + // Initialize the nodes. + for ( int i=0; i < nNodes; i++ ) + { + pNodes[i].m_vPos.Init(); + pNodes[i].m_vPrevPos.Init(); + pNodes[i].m_vPredicted.Init(); + } + + SetNumNodes( nNodes ); + + m_pDelegate = NULL; +} + + +void CBaseRopePhysics::SetNumNodes( int nNodes ) +{ + m_nNodes = nNodes; + + // Setup the springs. + for( int i=0; i < NumSprings(); i++ ) + { + m_pSprings[i].m_pNode1 = &m_pNodes[i].m_vPos; + m_pSprings[i].m_pNode2 = &m_pNodes[i+1].m_vPos; + Assert( m_pSprings[i].m_pNode1->IsValid() ); + Assert( m_pSprings[i].m_pNode2->IsValid() ); + + m_flNodeSpringDistsSqr[i] = m_flSpringDistSqr / NumSprings(); + } +} + + +void CBaseRopePhysics::Restart() +{ + m_Physics.Init( 1.0 / 50 ); +} + + +void CBaseRopePhysics::ResetSpringLength( float flSpringDist ) +{ + m_flSpringDist = MAX( flSpringDist, 0 ); + m_flSpringDistSqr = m_flSpringDist * m_flSpringDist; + + for( int i=0; i < NumSprings(); i++ ) + { + m_flNodeSpringDistsSqr[i] = m_flSpringDistSqr / NumSprings(); + } +} + +float CBaseRopePhysics::GetSpringLength() const +{ + return m_flSpringDist; +} + +void CBaseRopePhysics::ResetNodeSpringLength( int iStartNode, float flSpringDist ) +{ + m_flNodeSpringDistsSqr[iStartNode] = flSpringDist * flSpringDist; +} + +void CBaseRopePhysics::SetupSimulation( float flSpringDist, CSimplePhysics::IHelper *pDelegate ) +{ + ResetSpringLength( flSpringDist ); + SetDelegate( pDelegate ); +} + + +void CBaseRopePhysics::SetDelegate( CSimplePhysics::IHelper *pDelegate ) +{ + m_pDelegate = pDelegate; +} + + +void CBaseRopePhysics::Simulate( float dt ) +{ + static float flEnergy = 0.98; + m_Physics.Simulate( m_pNodes, m_nNodes, this, dt, flEnergy ); +} + + +void CBaseRopePhysics::GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ) +{ + if( m_pDelegate ) + m_pDelegate->GetNodeForces( pNodes, iNode, pAccel ); + else + pAccel->Init( 0, 0, 0 ); +} + + +void CBaseRopePhysics::ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ) +{ + // Handle springs.. + // + // Iterate multiple times here. If we don't, then gravity tends to + // win over the constraint solver and it's impossible to get straight ropes. + static int nIterations = 3; + for( int iIteration=0; iIteration < nIterations; iIteration++ ) + { + for( int i=0; i < NumSprings(); i++ ) + { + CRopeSpring *s = &m_pSprings[i]; + + Vector vTo = *s->m_pNode1 - *s->m_pNode2; + + float flDistSqr = vTo.LengthSqr(); + + // If we don't have an overall spring distance, see if we have a per-node one + float flSpringDist = m_flSpringDistSqr; + if ( !flSpringDist ) + { + // TODO: This still isn't enough. Ropes with different spring lengths + // per-node will oscillate forever. + flSpringDist = m_flNodeSpringDistsSqr[i]; + } + + if( flDistSqr > flSpringDist ) + { + float flDist = (float)sqrt( flDistSqr ); + vTo *= 1 - (m_flSpringDist / flDist); + + *s->m_pNode1 -= vTo * 0.5f; + *s->m_pNode2 += vTo * 0.5f; + } + } + + if( m_pDelegate ) + m_pDelegate->ApplyConstraints( pNodes, nNodes ); + } +} + + diff --git a/public/rope_physics.h b/public/rope_physics.h new file mode 100644 index 0000000..94e1e9b --- /dev/null +++ b/public/rope_physics.h @@ -0,0 +1,117 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ROPE_PHYSICS_H +#define ROPE_PHYSICS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "simple_physics.h" +#include "networkvar.h" + + +class CRopeSpring +{ +public: + Vector *m_pNode1; + Vector *m_pNode2; +}; + + +class CBaseRopePhysics : public CSimplePhysics::IHelper +{ +public: + DECLARE_CLASS_NOBASE( CBaseRopePhysics ); + + CBaseRopePhysics( + CSimplePhysics::CNode *pNodes, + int nNodes, + CRopeSpring *pSprings, + float *flSpringDistsSqr ); + + // nNodes should be less than or equal to what you passed into the constructor. + void SetNumNodes( int nNodes ); + + // Restart timers and such. + void Restart(); + + void ResetSpringLength(float flSpringDist ); + float GetSpringLength() const; + void ResetNodeSpringLength( int iStartNode, float flSpringDist ); + + // Set simulation parameters. + // If you pass in a delegate, you can be called to apply constraints. + void SetupSimulation( float flSpringDist, CSimplePhysics::IHelper *pDelegate=0 ); + + // Set the physics delegate. + void SetDelegate( CSimplePhysics::IHelper *pDelegate ); + + void Simulate( float dt ); + + int NumNodes() { return m_nNodes; } + CSimplePhysics::CNode* GetNode( int iNode ) { return &m_pNodes[iNode]; } + CSimplePhysics::CNode* GetFirstNode() { return &m_pNodes[0]; } + CSimplePhysics::CNode* GetLastNode() { return &m_pNodes[ m_nNodes-1 ]; } + + + +public: + + virtual void GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel ); + virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes ); + + +private: + + int NumSprings() {return m_nNodes - 1;} + + +protected: + + CSimplePhysics::IHelper *m_pDelegate; + + CSimplePhysics::CNode *m_pNodes; + int m_nNodes; + + CRopeSpring *m_pSprings; + + float m_flSpringDist; + float m_flSpringDistSqr; + + // Spring lengths per node + float *m_flNodeSpringDistsSqr; + + CSimplePhysics m_Physics; +}; + + + + + +template< int NUM_NODES > +class CRopePhysics : public CBaseRopePhysics +{ +public: + + CRopePhysics(); + + CSimplePhysics::CNode m_Nodes[NUM_NODES]; + CRopeSpring m_Springs[NUM_NODES - 1]; + float m_SpringDistsSqr[NUM_NODES - 1]; +}; + + +template< int NUM_NODES > +CRopePhysics::CRopePhysics() : + CBaseRopePhysics( m_Nodes, NUM_NODES, m_Springs, m_SpringDistsSqr ) +{ +} + + +#endif // ROPE_PHYSICS_H diff --git a/public/rope_shared.h b/public/rope_shared.h new file mode 100644 index 0000000..488d3d5 --- /dev/null +++ b/public/rope_shared.h @@ -0,0 +1,70 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ROPE_SHARED_H +#define ROPE_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +// Shared definitions for rope. +#define ROPE_MAX_SEGMENTS 10 +#define ROPE_TYPE1_NUMSEGMENTS 4 +#define ROPE_TYPE2_NUMSEGMENTS 2 + +// Default rope gravity vector. +#define ROPE_GRAVITY 0, 0, -1500 + + +// Rope flags. +#define ROPE_RESIZE (1<<0) // Try to keep the rope dangling the same amount + // even as the rope length changes. +#define ROPE_BARBED (1<<1) // Hack option to draw like a barbed wire. +#define ROPE_COLLIDE (1<<2) // Collide with the world? +#define ROPE_SIMULATE (1<<3) // Is the rope valid? +#define ROPE_BREAKABLE (1<<4) // Can the endpoints detach? +#define ROPE_USE_WIND (1<<5) // Wind simulation on this rope. +#define ROPE_INITIAL_HANG (1<<6) // By default, ropes will simulate for a bit internally when they + // are created so they sag, but dynamically created ropes for things + // like harpoons don't want this. +#define ROPE_PLAYER_WPN_ATTACH (1<<7) // If this flag is set, then the second attachment must be a player. + // The rope will attach to "buff_attach" on the player's active weapon. + // (This is a flag because it requires special code on the client to + // find the weapon). +#define ROPE_NO_GRAVITY (1<<8) // Disable gravity on this rope. +#define ROPE_TONGUE_ATTACH (1<<9) // Use the special rules unique to a tongue rope, without cutting open the class to inherit one function. +#define ROPE_NUMFLAGS 10 + + +// This is added to all rope slacks so when a level designer enters a +// slack of zero in the entity, it doesn't dangle so low. +#define ROPESLACK_FUDGEFACTOR -100 + + +// Rope shader IDs. +#define ROPESHADER_BLACKCABLE 0 +#define ROPESHADER_ROPE 1 +#define ROPESHADER_CHAIN 2 + + +// Rope locked points +enum +{ + ROPE_LOCK_START_POINT = 0x1, + ROPE_LOCK_END_POINT = 0x2, + ROPE_LOCK_START_DIRECTION = 0x4, + ROPE_LOCK_END_DIRECTION = 0x8, +}; + + +// Rope attachment points. +#define ROPE_ATTACHMENT_START_POINT 1 +#define ROPE_ATTACHMENT_END_POINT 2 + + +#endif // ROPE_SHARED_H diff --git a/public/savegame_version.h b/public/savegame_version.h new file mode 100644 index 0000000..0e5f0a2 --- /dev/null +++ b/public/savegame_version.h @@ -0,0 +1,15 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( SAVEGAME_VERSION_H ) +#define SAVEGAME_VERSION_H +#ifdef _WIN32 +#pragma once +#endif + +#define SAVEGAME_VERSION 0x0073 // Version 0.73 + +#endif // SAVEGAME_VERSION_H diff --git a/public/saverestoretypes.h b/public/saverestoretypes.h new file mode 100644 index 0000000..ac00803 --- /dev/null +++ b/public/saverestoretypes.h @@ -0,0 +1,550 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +// @Note (toml 12-02-02): For now, all of the methods in the types defined here +// are inline to permit simple cross-DLL usage +//=============================================================================// + +#ifndef SAVERESTORETYPES_H +#define SAVERESTORETYPES_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier1/utlhash.h" + +#include // NULL_STRING define +struct edict_t; + + +#ifdef EHANDLE_H // not available to engine +#define SR_ENTS_VISIBLE 1 +#endif + + +//----------------------------------------------------------------------------- +// +// class CSaveRestoreSegment +// + +class CSaveRestoreSegment +{ +public: + CSaveRestoreSegment(); + + //--------------------------------- + // Buffer operations + // + void Init( void *pNewBase, int nBytes ); + void Rebase(); + void Rewind( int nBytes ); + char *GetBuffer(); + int BytesAvailable() const; + int SizeBuffer() const; + bool Write( const void *pData, int nBytes ); + bool Read( void *pOutput, int nBytes ); + int GetCurPos(); + char *AccessCurPos(); + bool Seek( int absPosition ); + void MoveCurPos( int nBytes ); + + //--------------------------------- + // Symbol table operations + // + void InitSymbolTable( char **pNewTokens, int sizeTable); + char **DetachSymbolTable(); + int SizeSymbolTable(); + bool DefineSymbol( const char *pszToken, int token ); + unsigned short FindCreateSymbol( const char *pszToken ); + const char *StringFromSymbol( int token ); + +private: +#ifndef _WIN32 + unsigned _rotr ( unsigned val, int shift); +#endif + unsigned int HashString( const char *pszToken ); + + //--------------------------------- + // Buffer data + // + char *pBaseData; // Start of all entity save data + char *pCurrentData; // Current buffer pointer for sequential access + int size; // Current data size, aka, pCurrentData - pBaseData + int bufferSize; // Total space for data + + //--------------------------------- + // Symbol table + // + int tokenCount; // Number of elements in the pTokens table + char **pTokens; // Hash table of entity strings (sparse) +}; + + +//----------------------------------------------------------------------------- +// +// class CGameSaveRestoreInfo +// + +struct levellist_t +{ + DECLARE_SIMPLE_DATADESC(); + + char mapName[ MAX_MAP_NAME ]; + char landmarkName[ 32 ]; + edict_t *pentLandmark; + Vector vecLandmarkOrigin; +}; + +#define MAX_LEVEL_CONNECTIONS 16 // These are encoded in the lower 16bits of entitytable_t->flags + +//------------------------------------- + +struct EHandlePlaceholder_t // Engine does some of the game writing (alas, probably shouldn't), but can't see ehandle.h +{ + unsigned long i; +}; + +//------------------------------------- + +struct entitytable_t +{ + void Clear() + { + id = -1; + edictindex = -1; + saveentityindex = -1; + restoreentityindex = -1; + location = 0; + size = 0; + flags = 0; + classname = NULL_STRING; + globalname = NULL_STRING; + landmarkModelSpace.Init(); + modelname = NULL_STRING; + } + + int id; // Ordinal ID of this entity (used for entity <--> pointer conversions) + int edictindex; // saved for if the entity requires a certain edict number when restored (players, world) + + int saveentityindex; // the entity index the entity had at save time ( for fixing up client side entities ) + int restoreentityindex; // the entity index given to this entity at restore time + +#ifdef SR_ENTS_VISIBLE + EHANDLE hEnt; // Pointer to the in-game entity +#else + EHandlePlaceholder_t hEnt; +#endif + + int location; // Offset from the base data of this entity + int size; // Byte size of this entity's data + int flags; // This could be a short -- bit mask of transitions that this entity is in the PVS of + string_t classname; // entity class name + string_t globalname; // entity global name + Vector landmarkModelSpace; // a fixed position in model space for comparison + // NOTE: Brush models can be built in different coordiante systems + // in different levels, so this fixes up local quantities to match + // those differences. + string_t modelname; + + DECLARE_SIMPLE_DATADESC(); +}; + +#define FENTTABLE_PLAYER 0x80000000 +#define FENTTABLE_REMOVED 0x40000000 +#define FENTTABLE_MOVEABLE 0x20000000 +#define FENTTABLE_GLOBAL 0x10000000 +#define FENTTABLE_PLAYERCHILD 0x08000000 // this entity is connected to a player, so it must be spawned with it +#define FENTTABLE_LEVELMASK 0x0000FFFF // reserve bits for 16 level connections +//------------------------------------- + +struct saverestorelevelinfo_t +{ + int connectionCount;// Number of elements in the levelList[] + levellist_t levelList[ MAX_LEVEL_CONNECTIONS ]; // List of connections from this level + + // smooth transition + int fUseLandmark; + char szLandmarkName[20]; // landmark we'll spawn near in next level + Vector vecLandmarkOffset; // for landmark transitions + float time; + char szCurrentMapName[MAX_MAP_NAME]; // To check global entities + int mapVersion; +}; + +//------------------------------------- + +class CGameSaveRestoreInfo +{ +public: + CGameSaveRestoreInfo() + : tableCount( 0 ), pTable( 0 ), m_pCurrentEntity( 0 ) + { + memset( &levelInfo, 0, sizeof( levelInfo ) ); + modelSpaceOffset.Init( 0, 0, 0 ); + } + + void InitEntityTable( entitytable_t *pNewTable = NULL, int size = 0 ) + { + pTable = pNewTable; + tableCount = size; + + for ( int i = 0; i < NumEntities(); i++ ) + { + GetEntityInfo( i )->Clear(); + } + } + + entitytable_t *DetachEntityTable() + { + entitytable_t *pReturn = pTable; + pTable = NULL; + tableCount = 0; + return pReturn; + } + + CBaseEntity *GetCurrentEntityContext() { return m_pCurrentEntity; } + void SetCurrentEntityContext(CBaseEntity *pEntity) { m_pCurrentEntity = pEntity; } + + int NumEntities() { return tableCount; } + entitytable_t *GetEntityInfo( int i ) { return (pTable + i); } + float GetBaseTime() const { return levelInfo.time; } + Vector GetLandmark() const { return ( levelInfo.fUseLandmark ) ? levelInfo.vecLandmarkOffset : vec3_origin; } + + void BuildEntityHash() + { +#ifdef GAME_DLL + MEM_ALLOC_CREDIT(); + new (&m_EntityToIndex) CEntityToIndexHash( 1024 ); + int i; + entitytable_t *pTable; + int nEntities = NumEntities(); + + for ( i = 0; i < nEntities; i++ ) + { + pTable = GetEntityInfo( i ); + m_EntityToIndex.Insert( CHashElement( pTable->hEnt.Get(), i ) ); + } +#endif + } + + void PurgeEntityHash() + { + m_EntityToIndex.Purge(); + Destruct( &m_EntityToIndex ); + } + + int GetEntityIndex( const CBaseEntity *pEntity ) + { +#ifdef SR_ENTS_VISIBLE + if ( pEntity ) + { + if ( m_EntityToIndex.Count() ) + { + UtlHashHandle_t hElement = m_EntityToIndex.Find( CHashElement( pEntity ) ); + if ( hElement != m_EntityToIndex.InvalidHandle() ) + { + return m_EntityToIndex.Element( hElement ).index; + } + } + else + { + int i; + entitytable_t *pTable; + + int nEntities = NumEntities(); + for ( i = 0; i < nEntities; i++ ) + { + pTable = GetEntityInfo( i ); + if ( pTable->hEnt == pEntity ) + return pTable->id; + } + } + } +#endif + return -1; + } + + saverestorelevelinfo_t levelInfo; + Vector modelSpaceOffset; // used only for globaly entity brushes modelled in different coordinate systems. + +private: + int tableCount; // Number of elements in the entity table + entitytable_t *pTable; // Array of entitytable_t elements (1 for each entity) + CBaseEntity *m_pCurrentEntity; // only valid during the save functions of this entity, NULL otherwise + + + struct CHashElement + { + const CBaseEntity *pEntity; + int index; + + CHashElement( const CBaseEntity *pEntity, int index) : pEntity(pEntity), index(index) {} + CHashElement( const CBaseEntity *pEntity ) : pEntity(pEntity) {} + CHashElement() {} + }; + + class CHashFuncs + { + public: + CHashFuncs( int ) {} + + // COMPARE + bool operator()( const CHashElement &lhs, const CHashElement &rhs ) const + { + return lhs.pEntity == rhs.pEntity; + } + + // HASH + unsigned int operator()( const CHashElement &item ) const + { + return HashItem( item.pEntity ); + } + }; + + typedef CUtlHash CEntityToIndexHash; + + CEntityToIndexHash m_EntityToIndex; +}; + +//----------------------------------------------------------------------------- + + +class CSaveRestoreData : public CSaveRestoreSegment, + public CGameSaveRestoreInfo +{ +public: + CSaveRestoreData() : bAsync( false ) {} + + + bool bAsync; +}; + +inline CSaveRestoreData *MakeSaveRestoreData( void *pMemory ) +{ + return new (pMemory) CSaveRestoreData; +} + +//----------------------------------------------------------------------------- +// +// class CSaveRestoreSegment, inline functions +// + +inline CSaveRestoreSegment::CSaveRestoreSegment() +{ + memset( this, 0, sizeof(*this) ); +} + +inline void CSaveRestoreSegment::Init( void *pNewBase, int nBytes ) +{ + pCurrentData = pBaseData = (char *)pNewBase; + size = 0; + bufferSize = nBytes; +} + +inline void CSaveRestoreSegment::MoveCurPos( int nBytes ) +{ + pCurrentData += nBytes; + size += nBytes; +} + +inline void CSaveRestoreSegment::Rebase() +{ + pBaseData = pCurrentData; + bufferSize -= size; + size = 0; +} + +inline void CSaveRestoreSegment::Rewind( int nBytes ) +{ + if ( size < nBytes ) + nBytes = size; + + MoveCurPos( -nBytes ); +} + +inline char *CSaveRestoreSegment::GetBuffer() +{ + return pBaseData; +} + +inline int CSaveRestoreSegment::BytesAvailable() const +{ + return (bufferSize - size); +} + +inline int CSaveRestoreSegment::SizeBuffer() const +{ + return bufferSize; +} + +inline bool CSaveRestoreSegment::Write( const void *pData, int nBytes ) +{ + if ( nBytes > BytesAvailable() ) + { + size = bufferSize; + return false; + } + + memcpy( pCurrentData, pData, nBytes ); + MoveCurPos( nBytes ); + + return true; +} + +inline bool CSaveRestoreSegment::Read( void *pOutput, int nBytes ) +{ + if ( !BytesAvailable() ) + return false; + + if ( nBytes > BytesAvailable() ) + { + size = bufferSize; + return false; + } + + if ( pOutput ) + memcpy( pOutput, pCurrentData, nBytes ); + MoveCurPos( nBytes ); + return true; +} + +inline int CSaveRestoreSegment::GetCurPos() +{ + return size; +} + +inline char *CSaveRestoreSegment::AccessCurPos() +{ + return pCurrentData; +} + +inline bool CSaveRestoreSegment::Seek( int absPosition ) +{ + if ( absPosition < 0 || absPosition >= bufferSize ) + return false; + + size = absPosition; + pCurrentData = pBaseData + size; + return true; +} + +inline void CSaveRestoreSegment::InitSymbolTable( char **pNewTokens, int sizeTable) +{ + Assert( !pTokens ); + tokenCount = sizeTable; + pTokens = pNewTokens; + memset( pTokens, 0, sizeTable * sizeof( pTokens[0]) ); +} + +inline char **CSaveRestoreSegment::DetachSymbolTable() +{ + char **pResult = pTokens; + tokenCount = 0; + pTokens = NULL; + return pResult; +} + +inline int CSaveRestoreSegment::SizeSymbolTable() +{ + return tokenCount; +} + +inline bool CSaveRestoreSegment::DefineSymbol( const char *pszToken, int token ) +{ + if ( pTokens[token] == NULL ) + { + pTokens[token] = (char *)pszToken; + return true; + } + Assert( 0 ); + return false; +} + +inline unsigned short CSaveRestoreSegment::FindCreateSymbol( const char *pszToken ) +{ + unsigned short hash = (unsigned short)(HashString( pszToken ) % (unsigned)tokenCount ); + +#if _DEBUG + static int tokensparsed = 0; + tokensparsed++; + if ( !tokenCount || !pTokens ) + { + AssertMsg( 0, ("No token table array in TokenHash()!") ); + } +#endif + + for ( int i=0; i 50 && !beentheredonethat ) + { + beentheredonethat = true; + AssertMsg( 0, ("CSaveRestoreBuffer::TokenHash() is getting too full!" ) ); + } +#endif + + int index = hash + i; + if ( index >= tokenCount ) + index -= tokenCount; + + if ( !pTokens[index] || strcmp( pszToken, pTokens[index] ) == 0 ) + { + pTokens[index] = (char *)pszToken; + return index; + } + } + + // Token hash table full!!! + // [Consider doing overflow table(s) after the main table & limiting linear hash table search] + Warning( "CSaveRestoreBuffer::TokenHash() is COMPLETELY FULL!" ); + Assert( 0 ); + return 0; +} + +inline const char *CSaveRestoreSegment::StringFromSymbol( int token ) +{ + if ( token >= 0 && token < tokenCount ) + return pTokens[token]; + Assert( 0 ); + return "<>"; +} + +#ifndef _WIN32 +inline unsigned CSaveRestoreSegment::_rotr ( unsigned val, int shift) +{ + register unsigned lobit; /* non-zero means lo bit set */ + register unsigned num = val; /* number to rotate */ + + shift &= 0x1f; /* modulo 32 -- this will also make + negative shifts work */ + + while (shift--) + { + lobit = num & 1; /* get high bit */ + num >>= 1; /* shift right one bit */ + if (lobit) + num |= 0x80000000; /* set hi bit if lo bit was set */ + } + + return num; +} +#endif + +inline unsigned int CSaveRestoreSegment::HashString( const char *pszToken ) +{ + unsigned int hash = 0; + + while ( *pszToken ) + hash = _rotr( hash, 4 ) ^ *pszToken++; + + return hash; +} + +//============================================================================= + +#endif // SAVERESTORETYPES_H diff --git a/public/scenefilecache/ISceneFileCache.h b/public/scenefilecache/ISceneFileCache.h new file mode 100644 index 0000000..8508347 --- /dev/null +++ b/public/scenefilecache/ISceneFileCache.h @@ -0,0 +1,44 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ISCENEFILECACHE_H +#define ISCENEFILECACHE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "appframework/IAppSystem.h" + +// the file cache can support persisting some calcs +struct SceneCachedData_t +{ + unsigned int msecs; + float m_fLastSpeakSecs; + int numSounds; + int sceneId; +}; + +class ISceneFileCache : public IAppSystem +{ +public: + + // async implemenation + virtual size_t GetSceneBufferSize( char const *filename ) = 0; + virtual bool GetSceneData( char const *filename, byte *buf, size_t bufsize ) = 0; + + // persisted scene data, returns true if valid, false otherwise + virtual bool GetSceneCachedData( char const *pFilename, SceneCachedData_t *pData ) = 0; + virtual short GetSceneCachedSound( int iScene, int iSound ) = 0; + virtual const char *GetSceneString( short stringId ) = 0; + + // Physically reloads image from disk + virtual void Reload() = 0; +}; + +#define SCENE_FILE_CACHE_INTERFACE_VERSION "SceneFileCache002" + +#endif // ISCENEFILECACHE_H diff --git a/public/scenefilecache/SceneImageFile.h b/public/scenefilecache/SceneImageFile.h new file mode 100644 index 0000000..4172618 --- /dev/null +++ b/public/scenefilecache/SceneImageFile.h @@ -0,0 +1,69 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A Scene Image file aggregates all the compiled binary VCD files into +// a single file. +// +//=====================================================================================// +#ifndef SCENE_IMAGE_FILE_H +#define SCENE_IMAGE_FILE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "commonmacros.h" +#include "tier1/checksum_crc.h" + +#define SCENE_IMAGE_ID MAKEID( 'V','S','I','F' ) +#define SCENE_IMAGE_VERSION 3 + +// scene summary: cached calcs for commmon startup queries, variable sized +// note: if you want to add a member to this struct, one way to do it and +// save memory is to make lastspeech_msecs be a uint16 rather than a uint32 +// and store it as a fixed point seconds (say, 8.8) rather than straight +// msecs; that gives you an additional 16 bits to squeeze into this struct +// without actually increasing memory usage. Override the inline float +// GetDurToSpeechEnd() function if you do this; all game code uses that rather +// than reading the lastspeech_msecs member directly. +struct SceneImageSummary_t +{ + unsigned int msecs; + unsigned int lastspeech_msecs; ///< milliseconds from beginning of vcd to end of last speak event. + int numSounds; + int soundStrings[1]; // has numSounds + + // return time in seconds from beginning of scene to end of last Speak event + inline float GetDurToSpeechEnd( void ) const { return lastspeech_msecs * 0.001f; } +}; + +// stored sorted by crc filename for binary search +struct SceneImageEntry_t +{ + CRC32_t crcFilename; // expected to be normalized as scenes\???.vcd + int nDataOffset; // offset to dword aligned data from start + int nDataLength; + int nSceneSummaryOffset; // offset to summary +}; + +struct SceneImageHeader_t +{ + int nId; + int nVersion; + int nNumScenes; // number of scene files + int nNumStrings; // number of unique strings in table + int nSceneEntryOffset; + + inline const char *String( short iString ) + { + if ( iString < 0 || iString >= nNumStrings ) + { + Assert( 0 ); + return NULL; + } + + // access string table (after header) to access pool + unsigned int *pTable = (unsigned int *)((byte *)this + sizeof( SceneImageHeader_t )); + return (char *)this + pTable[iString]; + } +}; + +#endif // SCENE_IMAGE_FILE_H diff --git a/public/scratchpad3d.cpp b/public/scratchpad3d.cpp new file mode 100644 index 0000000..0d33309 --- /dev/null +++ b/public/scratchpad3d.cpp @@ -0,0 +1,640 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include "scratchpad3d.h" +#include "tier2/tier2.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef POSIX +#define __stdcall +#endif + +#ifndef POSIX +// NOTE - linux doesn't need any of this code! + +extern "C" +{ + extern void __stdcall Sleep( unsigned long ms ); +}; + + +class CFileRead +{ +public: + CFileRead( IFileSystem* pFileSystem, FileHandle_t fp ) + { + m_pFileSystem = pFileSystem; + m_fp = fp; + m_Pos = 0; + } + + bool Read( void *pDest, int len ) + { + int count = m_pFileSystem->Read( pDest, len, m_fp ); + m_Pos += count; + return count == len; + } + + IFileSystem* m_pFileSystem; + FileHandle_t m_fp; + int m_Pos; +}; + + +// ------------------------------------------------------------------------ // +// CCommand_Point. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_Point::Read( CFileRead *pFile ) +{ + pFile->Read( &m_flPointSize, sizeof(m_flPointSize) ); + pFile->Read( &m_Vert, sizeof(m_Vert) ); +} + +void CScratchPad3D::CCommand_Point::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + pFileSystem->Write( &m_flPointSize, sizeof(m_flPointSize), fp ); + pFileSystem->Write( &m_Vert, sizeof(m_Vert), fp ); +} + + +// ------------------------------------------------------------------------ // +// CCommand_Line. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_Line::Read( CFileRead *pFile ) +{ + pFile->Read( m_Verts, sizeof(m_Verts) ); +} + +void CScratchPad3D::CCommand_Line::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + pFileSystem->Write( m_Verts, sizeof(m_Verts), fp ); +} + + +// ------------------------------------------------------------------------ // +// CCommand_Polygon. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_Polygon::Read( CFileRead *pFile ) +{ + int count; + pFile->Read( &count, sizeof(count) ); + m_Verts.RemoveAll(); + m_Verts.AddMultipleToTail( count ); + + if( count ) + pFile->Read( &m_Verts[0], sizeof(CSPVert)*count ); +} + +void CScratchPad3D::CCommand_Polygon::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + int count = m_Verts.Count(); + pFileSystem->Write( &count, sizeof(count), fp ); + + if( count ) + pFileSystem->Write( &m_Verts[0], sizeof(CSPVert)*count, fp ); +} + + +// ------------------------------------------------------------------------ // +// CCommand_Matrix. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_Matrix::Read( CFileRead *pFile ) +{ + pFile->Read( &m_mMatrix, sizeof(m_mMatrix) ); +} + +void CScratchPad3D::CCommand_Matrix::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + pFileSystem->Write( &m_mMatrix, sizeof(m_mMatrix), fp ); +} + + +// ------------------------------------------------------------------------ // +// CCommand_RenderState. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_RenderState::Read( CFileRead *pFile ) +{ + pFile->Read( &m_State, sizeof(m_State) ); + pFile->Read( &m_Val, sizeof(m_Val) ); +} + +void CScratchPad3D::CCommand_RenderState::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + pFileSystem->Write( &m_State, sizeof(m_State), fp ); + pFileSystem->Write( &m_Val, sizeof(m_Val), fp ); +} + + +// ------------------------------------------------------------------------ // +// CCommand_Text. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::CCommand_Text::Read( CFileRead *pFile ) +{ + int strLen; + pFile->Read( &strLen, sizeof( strLen ) ); + m_String.SetSize( strLen ); + pFile->Read( m_String.Base(), strLen ); + + pFile->Read( &m_TextParams, sizeof( m_TextParams ) ); +} + + +void CScratchPad3D::CCommand_Text::Write( IFileSystem* pFileSystem, FileHandle_t fp ) +{ + int strLen = m_String.Count(); + pFileSystem->Write( &strLen, sizeof( strLen ), fp ); + pFileSystem->Write( m_String.Base(), strLen, fp ); + + pFileSystem->Write( &m_TextParams, sizeof( m_TextParams ), fp ); +} + + +// ------------------------------------------------------------------------ // +// CScratchPad3D internals. +// ------------------------------------------------------------------------ // + +CScratchPad3D::CScratchPad3D( char const *pFilename, IFileSystem* pFileSystem, bool bAutoClear ) +{ + m_pFileSystem = pFileSystem; + m_pFilename = pFilename; + m_bAutoFlush = true; + + if( bAutoClear ) + Clear(); // Clear whatever is in the file.. +} + +void CScratchPad3D::AutoFlush() +{ + if( m_bAutoFlush ) + Flush(); +} + +void CScratchPad3D::DrawRectGeneric( int iPlane, int otherDim1, int otherDim2, float planeDist, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) +{ + Vector verts[4]; + + verts[0][iPlane] = verts[1][iPlane] = verts[2][iPlane] = verts[3][iPlane] = planeDist; + + verts[0][otherDim1] = vMin.x; + verts[0][otherDim2] = vMin.y; + + verts[1][otherDim1] = vMin.x; + verts[1][otherDim2] = vMax.y; + + verts[2][otherDim1] = vMax.x; + verts[2][otherDim2] = vMax.y; + + verts[3][otherDim1] = vMax.x; + verts[3][otherDim2] = vMin.y; + + DrawPolygon( CSPVertList(verts, 4, vColor) ); +} + +void CScratchPad3D::DeleteCommands() +{ + for( int i=0; i < m_Commands.Count(); i++ ) + delete m_Commands[i]; + + m_Commands.RemoveAll(); +} + +bool CScratchPad3D::LoadCommandsFromFile( ) +{ + DeleteCommands(); + + FileHandle_t fp = m_pFileSystem->Open( m_pFilename, "rb" ); + if( !fp ) + return false; + + long fileEndPos = m_pFileSystem->Size( fp ); + + CFileRead fileRead( m_pFileSystem, fp ); + while( fileRead.m_Pos != fileEndPos ) + { + unsigned char iCommand; + fileRead.Read( &iCommand, sizeof(iCommand) ); + + CBaseCommand *pCmd = NULL; + if( iCommand == COMMAND_POINT ) + pCmd = new CCommand_Point; + else if( iCommand == COMMAND_LINE ) + pCmd = new CCommand_Line; + else if( iCommand == COMMAND_POLYGON ) + pCmd = new CCommand_Polygon; + else if( iCommand == COMMAND_MATRIX ) + pCmd = new CCommand_Matrix; + else if( iCommand == COMMAND_RENDERSTATE ) + pCmd = new CCommand_RenderState; + else if ( iCommand == COMMAND_TEXT ) + pCmd = new CCommand_Text; + + if( !pCmd ) + { + Assert( !"LoadCommandsFromFile: invalid file" ); + m_pFileSystem->Close( fp ); + return false; + } + + pCmd->Read( &fileRead ); + m_Commands.AddToTail( pCmd ); + } + + m_pFileSystem->Close( fp ); + return true; +} + + +// ------------------------------------------------------------------------ // +// CScratchPad3D's IScratchPad3D implementation. +// ------------------------------------------------------------------------ // + +void CScratchPad3D::Release() +{ + Flush(); + delete this; +} + +void CScratchPad3D::SetMapping( + const Vector &vInputMin, + const Vector &vInputMax, + const Vector &vOutputMin, + const Vector &vOutputMax ) +{ + CCommand_Matrix *cmd = new CCommand_Matrix; + m_Commands.AddToTail( cmd ); + + Vector vDivisor(1,1,1); + for( int i=0; i < 3; i++ ) + vDivisor[i] = fabs(vInputMax[i] - vInputMin[i]) < 0.0001f ? 0.001f : (vInputMax[i] - vInputMin[i]); + + Vector vScale = (vOutputMax - vOutputMin) / vDivisor; + Vector vShift = -vInputMin * vScale + vOutputMin; + + cmd->m_mMatrix.Init( + vScale.x, 0, 0, vShift.x, + 0, vScale.y, 0, vShift.y, + 0, 0, vScale.z, vShift.z, + 0, 0, 0, 1 ); + + + AutoFlush(); +} + +bool CScratchPad3D::GetAutoFlush() +{ + return m_bAutoFlush; +} + +void CScratchPad3D::SetAutoFlush( bool bAutoFlush ) +{ + m_bAutoFlush = bAutoFlush; + if( m_bAutoFlush ) + Flush(); +} + +void CScratchPad3D::DrawPoint( CSPVert const &v, float flPointSize ) +{ + CCommand_Point *cmd = new CCommand_Point; + m_Commands.AddToTail( cmd ); + + cmd->m_Vert = v; + cmd->m_flPointSize = flPointSize; + + AutoFlush(); +} + +void CScratchPad3D::DrawLine( CSPVert const &v1, CSPVert const &v2 ) +{ + CCommand_Line *cmd = new CCommand_Line; + m_Commands.AddToTail( cmd ); + + cmd->m_Verts[0] = v1; + cmd->m_Verts[1] = v2; + + AutoFlush(); +} + +void CScratchPad3D::DrawPolygon( CSPVertList const &verts ) +{ + CCommand_Polygon *cmd = new CCommand_Polygon; + m_Commands.AddToTail( cmd ); + + cmd->m_Verts.AddVectorToTail( verts.m_Verts ); + + AutoFlush(); +} + +void CScratchPad3D::DrawRectYZ( float xPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) +{ + DrawRectGeneric( 0, 1, 2, xPos, vMin, vMax, vColor ); +} + +void CScratchPad3D::DrawRectXZ( float yPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) +{ + DrawRectGeneric( 1, 0, 2, yPos, vMin, vMax, vColor ); +} + +void CScratchPad3D::DrawRectXY( float zPos, const Vector2D &vMin, const Vector2D &vMax, const CSPColor &vColor ) +{ + DrawRectGeneric( 2, 0, 1, zPos, vMin, vMax, vColor ); +} + +void CScratchPad3D::SetRenderState( RenderState state, unsigned long val ) +{ + CCommand_RenderState *cmd = new CCommand_RenderState; + m_Commands.AddToTail( cmd ); + + cmd->m_State = (unsigned long)state; + cmd->m_Val = val; +} + +void CScratchPad3D::DrawWireframeBox( const Vector &vMin, const Vector &vMax, const Vector &vColor ) +{ + // Bottom 4. + DrawLine( + CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), + CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), + CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor), + CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor), + CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor) ); + + // Top 4. + DrawLine( + CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor), + CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor), + CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor), + CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor), + CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); + + // Connecting 4. + DrawLine( + CSPVert(Vector(vMin.x, vMin.y, vMin.z), vColor), + CSPVert(Vector(vMin.x, vMin.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMin.x, vMax.y, vMin.z), vColor), + CSPVert(Vector(vMin.x, vMax.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMax.y, vMin.z), vColor), + CSPVert(Vector(vMax.x, vMax.y, vMax.z), vColor) ); + + DrawLine( + CSPVert(Vector(vMax.x, vMin.y, vMin.z), vColor), + CSPVert(Vector(vMax.x, vMin.y, vMax.z), vColor) ); +} + + +void CScratchPad3D::DrawText( const char *pStr, const CTextParams ¶ms ) +{ + CCommand_Text *cmd = new CCommand_Text; + m_Commands.AddToTail( cmd ); + + cmd->m_String.CopyArray( pStr, strlen( pStr ) + 1 ); + cmd->m_TextParams = params; + + AutoFlush(); +} + + +void CScratchPad3D::Clear() +{ + FileHandle_t fp; + + while( ( fp = m_pFileSystem->Open(m_pFilename, "wb") ) == NULL ) + { +#ifdef _WIN32 + Sleep( 5 ); +#elif POSIX + usleep( 5 ); +#endif + } + + m_pFileSystem->Close( fp ); + + DeleteCommands(); +} + + +void CScratchPad3D::Flush() +{ + FileHandle_t fp; + + while( ( fp = m_pFileSystem->Open(m_pFilename, "ab+") ) == NULL ) + { +#ifdef _WIN32 + Sleep( 5 ); +#elif POSIX + usleep( 5 ); +#endif + } + + // Append the new commands to the file. + for( int i=0; i < m_Commands.Count(); i++ ) + { + m_pFileSystem->Write( &m_Commands[i]->m_iCommand, sizeof(m_Commands[i]->m_iCommand), fp ); + m_Commands[i]->Write( m_pFileSystem, fp ); + } + + m_pFileSystem->Close( fp ); + + DeleteCommands(); +} + +void CScratchPad3D::DrawImageBW( + unsigned char const *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels, + bool bOutlineImage, + Vector *vCorners ) +{ + SPRGBA *pRGBA = new SPRGBA[width*height]; + for( int y=0; y < height; y++ ) + { + SPRGBA *pDest = &pRGBA[ y * width ]; + unsigned char const *pSrc = &pData[ y * pitchInBytes ]; + for( int x=0; x < width; x++ ) + { + pDest->r = pDest->g = pDest->b = *pSrc; + ++pSrc; + ++pDest; + } + } + + DrawImageRGBA( pRGBA, width, height, width*sizeof(SPRGBA), bOutlinePixels, bOutlineImage, vCorners ); + delete [] pRGBA; +} + + +void CScratchPad3D::DrawPolygonsForPixels( + SPRGBA *pData, + int width, + int height, + int pitchInBytes, + Vector *vCorners ) +{ + // Scan top-down. + Vector vCurLeft = vCorners[1]; + Vector vCurRight = vCorners[2]; + + Vector vLeftInc = (vCorners[0] - vCorners[1]) / height; + Vector vRightInc = (vCorners[3] - vCorners[2]) / height; + + Vector vNextLeft = vCurLeft + vLeftInc; + Vector vNextRight = vCurRight + vRightInc; + + Vector vPolyBox[4]; + Vector &vTopLeft = vPolyBox[0]; + Vector &vTopRight = vPolyBox[1]; + Vector &vBottomRight = vPolyBox[2]; + Vector &vBottomLeft = vPolyBox[3]; + + for( int y=0; y < height; y++ ) + { + vTopLeft = vCurLeft; + vBottomLeft = vNextLeft; + + Vector vTopXInc = (vCurRight - vCurLeft) / width; + Vector vBottomXInc = (vNextRight - vNextLeft) / width; + + vTopRight = vTopLeft + vTopXInc; + vBottomRight = vBottomLeft + vBottomXInc; + + SPRGBA *pSrc = &pData[ y * (pitchInBytes/sizeof(SPRGBA)) ]; + for( int x=0; x < width; x++ ) + { + if ( pData ) + DrawPolygon( CSPVertList( vPolyBox, 4, Vector(pSrc->r/255.1f, pSrc->g/255.1f, pSrc->b/255.1f) ) ); + else + DrawPolygon( CSPVertList( vPolyBox, 4, Vector(1,1,1) ) ); + + ++pSrc; + vTopLeft += vTopXInc; + vTopRight += vTopXInc; + vBottomLeft += vBottomXInc; + vBottomRight += vBottomXInc; + } + + vCurLeft += vLeftInc; + vNextLeft += vLeftInc; + vCurRight += vRightInc; + vNextRight += vRightInc; + } +} + + +void CScratchPad3D::DrawImageRGBA( + SPRGBA *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels, + bool bOutlineImage, + Vector *vCorners ) +{ + Assert( pitchInBytes % sizeof(SPRGBA) == 0 ); + + Vector vDefaultCorners[4]; + if ( !vCorners ) + { + vCorners = vDefaultCorners; + vDefaultCorners[0].Init( -100, -100 ); + vDefaultCorners[1].Init( -100, 100 ); + vDefaultCorners[2].Init( 100, 100 ); + vDefaultCorners[3].Init( 100, -100 ); + } + + // Don't auto-flush while drawing all these primitives. + bool bOldAutoFlush = m_bAutoFlush; + m_bAutoFlush = false; + + // Draw solids. + SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Solid ); + DrawPolygonsForPixels( pData, width, height, pitchInBytes, vCorners ); + + // Draw wireframe. + if ( bOutlinePixels ) + { + SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Wireframe ); + DrawPolygonsForPixels( NULL, width, height, pitchInBytes, vCorners ); + } + + // Draw an outline around the whole image. + if ( bOutlineImage ) + { + SetRenderState( IScratchPad3D::RS_FillMode, IScratchPad3D::FillMode_Wireframe ); + DrawPolygon( CSPVertList( vCorners, 4 ) ); + } + + // Restore the old auto-flush state. + m_bAutoFlush = bOldAutoFlush; + AutoFlush(); +} + + +// ------------------------------------------------------------------------ // +// Global functions. +// ------------------------------------------------------------------------ // +IFileSystem* ScratchPad3D_SetupFileSystem() +{ + // Get a filesystem interface. + CSysModule *pModule = Sys_LoadModule( "filesystem_stdio" ); + if( !pModule ) + return NULL; + + CreateInterfaceFn fn = Sys_GetFactory( pModule ); + IFileSystem *pFileSystem; + if( !fn || (pFileSystem = (IFileSystem *)fn( FILESYSTEM_INTERFACE_VERSION, NULL )) == NULL ) + { + Sys_UnloadModule( pModule ); + return NULL; + } + + return pFileSystem; +} + +IScratchPad3D* ScratchPad3D_Create( char const *pFilename ) +{ + IFileSystem *pFileSystem = ScratchPad3D_SetupFileSystem(); + if( !pFileSystem ) + return NULL; + + CScratchPad3D *pRet = new CScratchPad3D( pFilename, pFileSystem, true ); + return pRet; +} +#endif // POSIX + diff --git a/public/scratchpad3d.h b/public/scratchpad3d.h new file mode 100644 index 0000000..b491405 --- /dev/null +++ b/public/scratchpad3d.h @@ -0,0 +1,224 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCRATCHPAD3D_H +#define SCRATCHPAD3D_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "iscratchpad3d.h" +#include "mathlib/vmatrix.h" +#include "filesystem.h" + +class CFileRead; + + +class CScratchPad3D : public IScratchPad3D +{ +// Commands that can go in the file. +public: + + enum + { + COMMAND_POINT=0, + COMMAND_LINE, + COMMAND_POLYGON, + COMMAND_MATRIX, + COMMAND_RENDERSTATE, + COMMAND_TEXT, + COMMAND_NUMCOMMANDS + }; + + class ICachedRenderData + { + public: + virtual void Release() = 0; + }; + + class CBaseCommand + { + public: + CBaseCommand( unsigned char iCommand ) + { + m_iCommand = (unsigned char)iCommand; + m_pCachedRenderData = NULL; + } + + ~CBaseCommand() + { + ReleaseCachedRenderData(); + } + + virtual void Read( CFileRead *pFile ) = 0; + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ) = 0; + + // Release cached render data. Usually used for releasing things like textures when + // the app is resizing.. + void ReleaseCachedRenderData() + { + if ( m_pCachedRenderData ) + { + m_pCachedRenderData->Release(); + m_pCachedRenderData = NULL; + } + } + + public: + + unsigned char m_iCommand; // One of the COMMAND_ defines. + + // The renderer can cache data with the commands to speedup the rendering after + // the first time (text uses this big time). + ICachedRenderData *m_pCachedRenderData; + }; + + class CCommand_Point : public CBaseCommand + { + public: + CCommand_Point() : CBaseCommand( COMMAND_POINT ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + float m_flPointSize; + CSPVert m_Vert; + }; + + class CCommand_Line : public CBaseCommand + { + public: + CCommand_Line() : CBaseCommand( COMMAND_LINE ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + CSPVert m_Verts[2]; + }; + + class CCommand_Polygon : public CBaseCommand + { + public: + CCommand_Polygon() : CBaseCommand( COMMAND_POLYGON ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + CUtlVector m_Verts; + }; + + class CCommand_Matrix : public CBaseCommand + { + public: + CCommand_Matrix() : CBaseCommand( COMMAND_MATRIX ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + VMatrix m_mMatrix; + }; + + class CCommand_RenderState : public CBaseCommand + { + public: + CCommand_RenderState() : CBaseCommand( COMMAND_RENDERSTATE ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + unsigned long m_State; // One of the RS_ enums. + unsigned long m_Val; + }; + + class CCommand_Text : public CBaseCommand + { + public: + CCommand_Text() : CBaseCommand( COMMAND_TEXT ) {} + + virtual void Read( CFileRead *pFile ); + virtual void Write( IFileSystem* pFileSystem, FileHandle_t fp ); + + CUtlVector m_String; // The string to render. + CTextParams m_TextParams; + }; + + +public: + + CScratchPad3D( char const *pFilename, IFileSystem *pFileSystem, bool bAutoClear ); + + void AutoFlush(); + void DrawRectGeneric( int iPlane, int otherDim1, int otherDim2, float planeDist, Vector2D const &vMin, Vector2D const &vMax, CSPColor const &vColor ); + void DeleteCommands(); + + // Load a file... + bool LoadCommandsFromFile( ); + + +public: + + virtual void Release(); + + virtual void SetMapping( + Vector const &vInputMin, + Vector const &vInputMax, + Vector const &vOutputMin, + Vector const &vOutputMax ); + virtual bool GetAutoFlush(); + virtual void SetAutoFlush( bool bAutoFlush ); + virtual void DrawPoint( CSPVert const &v, float flPointSize ); + virtual void DrawLine( CSPVert const &v1, CSPVert const &v2 ); + virtual void DrawPolygon( CSPVertList const &verts ); + virtual void DrawRectYZ( float xPos, Vector2D const &vMin, Vector2D const &vMax, CSPColor const &vColor ); + virtual void DrawRectXZ( float yPos, Vector2D const &vMin, Vector2D const &vMax, CSPColor const &vColor ); + virtual void DrawRectXY( float zPos, Vector2D const &vMin, Vector2D const &vMax, CSPColor const &vColor ); + virtual void DrawWireframeBox( Vector const &vMin, Vector const &vMax, Vector const &vColor ); + virtual void DrawText( const char *pStr, const CTextParams ¶ms ); + virtual void SetRenderState( RenderState state, unsigned long val ); + virtual void Clear(); + virtual void Flush(); + virtual void DrawImageBW( + unsigned char const *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels=true, + bool bOutlineImage=false, + Vector *vCorners=NULL ); + + // Draw an RGBA image. + // Corners are in this order: bottom-left, top-left, top-right, bottom-right. + virtual void DrawImageRGBA( + SPRGBA *pData, + int width, + int height, + int pitchInBytes, + bool bOutlinePixels=true, + bool bOutlineImage=false, + Vector *vCorners=NULL ); + + void DrawPolygonsForPixels( + SPRGBA *pData, + int width, + int height, + int pitchInBytes, + Vector *vCorners ); + +public: + IFileSystem* m_pFileSystem; + char const *m_pFilename; + CUtlVector m_Commands; + bool m_bAutoFlush; +}; + + +IFileSystem* ScratchPad3D_SetupFileSystem(); + + +#endif // SCRATCHPAD3D_H diff --git a/public/sentence.cpp b/public/sentence.cpp new file mode 100644 index 0000000..eedbd67 --- /dev/null +++ b/public/sentence.cpp @@ -0,0 +1,1830 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include "commonmacros.h" +#include "basetypes.h" +#include "sentence.h" +#include "utlbuffer.h" +#include +#include "mathlib/vector.h" +#include "mathlib/mathlib.h" +#include +#include "checksum_crc.h" +#include "phonemeconverter.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Purpose: converts an english string to unicode +//----------------------------------------------------------------------------- +int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize); + +#if PHONEME_EDITOR +void CEmphasisSample::SetSelected( bool isSelected ) +{ + selected = isSelected; +} +void CPhonemeTag::SetSelected( bool isSelected ) +{ + m_bSelected = isSelected; +} +bool CPhonemeTag::GetSelected() const +{ + return m_bSelected; +} +void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) +{ + m_uiStartByte = start; + m_uiEndByte = end; +} +unsigned int CPhonemeTag::GetStartByte() const +{ + return m_uiStartByte; +} +unsigned int CPhonemeTag::GetEndByte() const +{ + return m_uiEndByte; +} +void CWordTag::SetSelected( bool isSelected ) +{ + m_bSelected = isSelected; +} +bool CWordTag::GetSelected() const +{ + return m_bSelected; +} +void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) +{ + m_uiStartByte = start; + m_uiEndByte = end; +} +unsigned int CWordTag::GetStartByte() const +{ + return m_uiStartByte; +} +unsigned int CWordTag::GetEndByte() const +{ + return m_uiEndByte; +} +#else +// xbox doesn't store this data +void CEmphasisSample::SetSelected( bool isSelected ) {} +void CPhonemeTag::SetSelected( bool isSelected ) {} +bool CPhonemeTag::GetSelected() const { return false; } +void CPhonemeTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} +unsigned int CPhonemeTag::GetStartByte() const { return 0; } +unsigned int CPhonemeTag::GetEndByte() const { return 0; } +void CWordTag::SetSelected( bool isSelected ) {} +bool CWordTag::GetSelected() const { return false; } +void CWordTag::SetStartAndEndBytes( unsigned int start, unsigned int end ) {} +unsigned int CWordTag::GetStartByte() const { return 0; } +unsigned int CWordTag::GetEndByte() const { return 0; } +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CWordTag::CWordTag( void ) +{ + m_pszWord = NULL; + + SetStartAndEndBytes( 0, 0 ); + + m_flStartTime = 0.0f; + m_flEndTime = 0.0f; + + SetSelected( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : from - +//----------------------------------------------------------------------------- +CWordTag::CWordTag( const CWordTag& from ) +{ + m_pszWord = NULL; + SetWord( from.m_pszWord ); + + SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); + + m_flStartTime = from.m_flStartTime; + m_flEndTime = from.m_flEndTime; + + SetSelected( from.GetSelected() ); + + for ( int p = 0; p < from.m_Phonemes.Count(); p++ ) + { + CPhonemeTag *newPhoneme = new CPhonemeTag( *from.m_Phonemes[ p ] ); + m_Phonemes.AddToTail( newPhoneme ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *word - +//----------------------------------------------------------------------------- +CWordTag::CWordTag( const char *word ) +{ + SetStartAndEndBytes( 0, 0 ); + + m_flStartTime = 0.0f; + m_flEndTime = 0.0f; + + m_pszWord = NULL; + + SetSelected( false ); + + SetWord( word ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CWordTag::~CWordTag( void ) +{ + delete[] m_pszWord; + + while ( m_Phonemes.Count() > 0 ) + { + delete m_Phonemes[ 0 ]; + m_Phonemes.Remove( 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +// Output : int +//----------------------------------------------------------------------------- +int CWordTag::IndexOfPhoneme( CPhonemeTag *tag ) +{ + for ( int i = 0 ; i < m_Phonemes.Count(); i++ ) + { + CPhonemeTag *p = m_Phonemes[ i ]; + if ( p == tag ) + return i; + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *word - +//----------------------------------------------------------------------------- +void CWordTag::SetWord( const char *word ) +{ + delete[] m_pszWord; + m_pszWord = NULL; + if ( !word || !word[ 0 ] ) + return; + + int len = strlen( word ) + 1; + m_pszWord = new char[ len ]; + Assert( m_pszWord ); + Q_strncpy( m_pszWord, word, len ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CWordTag::GetWord() const +{ + return m_pszWord ? m_pszWord : ""; +} + + +unsigned int CWordTag::ComputeDataCheckSum() +{ + int i; + int c; + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + if ( m_pszWord != NULL ) + { + CRC32_ProcessBuffer( &crc, m_pszWord, Q_strlen( m_pszWord ) ); + } + // Checksum phonemes + c = m_Phonemes.Count(); + for ( i = 0; i < c; ++i ) + { + CPhonemeTag *phoneme = m_Phonemes[ i ]; + unsigned int phonemeCheckSum = phoneme->ComputeDataCheckSum(); + CRC32_ProcessBuffer( &crc, &phonemeCheckSum, sizeof( unsigned int ) ); + } + // Checksum timestamps + CRC32_ProcessBuffer( &crc, &m_flStartTime, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &m_flEndTime, sizeof( float ) ); + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +} + +CBasePhonemeTag::CBasePhonemeTag() +{ + m_flStartTime = 0; + m_flEndTime = 0; + + m_nPhonemeCode = 0; +} + +CBasePhonemeTag::CBasePhonemeTag( const CBasePhonemeTag& from ) +{ + memcpy( this, &from, sizeof(*this) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( void ) +{ + m_szPhoneme = NULL; + + SetStartAndEndBytes( 0, 0 ); + + SetSelected( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : from - +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( const CPhonemeTag& from ) : + BaseClass( from ) +{ + SetStartAndEndBytes( from.GetStartByte(), from.GetEndByte() ); + + SetSelected( from.GetSelected() ); + + m_szPhoneme = NULL; + SetTag( from.GetTag() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +//----------------------------------------------------------------------------- +CPhonemeTag::CPhonemeTag( const char *phoneme ) +{ + SetStartAndEndBytes( 0, 0 ); + + SetStartTime( 0.0f ); + SetEndTime( 0.0f ); + + SetSelected( false ); + + SetPhonemeCode( 0 ); + + m_szPhoneme = NULL; + SetTag( phoneme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CPhonemeTag::~CPhonemeTag( void ) +{ + delete[] m_szPhoneme; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +//----------------------------------------------------------------------------- +void CPhonemeTag::SetTag( const char *phoneme ) +{ + delete m_szPhoneme; + m_szPhoneme = NULL; + if ( !phoneme || !phoneme [ 0 ] ) + return; + + int len = Q_strlen( phoneme ) + 1; + m_szPhoneme = new char[ len ]; + Assert( m_szPhoneme ); + Q_strncpy( m_szPhoneme, phoneme, len ); +} + +char const *CPhonemeTag::GetTag() const +{ + return m_szPhoneme ? m_szPhoneme : ""; +} + + +unsigned int CPhonemeTag::ComputeDataCheckSum() +{ + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + CRC32_ProcessBuffer( &crc, m_szPhoneme, Q_strlen( m_szPhoneme ) ); + int phonemeCode = GetPhonemeCode(); + CRC32_ProcessBuffer( &crc, &phonemeCode, sizeof( int ) ); + + // Checksum timestamps + float startTime = GetStartTime(); + float endTime = GetEndTime(); + CRC32_ProcessBuffer( &crc, &startTime, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &endTime, sizeof( float ) ); + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +} + +//----------------------------------------------------------------------------- +// Purpose: Simple language to string and string to language lookup dictionary +//----------------------------------------------------------------------------- +#pragma pack(1) + +struct CCLanguage +{ + int type; + char const *name; + unsigned char r, g, b; // For faceposer, indicator color for this language +}; + +static CCLanguage g_CCLanguageLookup[] = +{ + { CC_ENGLISH, "english", 0, 0, 0 }, + { CC_FRENCH, "french", 150, 0, 0 }, + { CC_GERMAN, "german", 0, 150, 0 }, + { CC_ITALIAN, "italian", 0, 150, 150 }, + { CC_KOREAN, "korean", 150, 0, 150 }, + { CC_SCHINESE, "schinese", 150, 0, 150 }, + { CC_SPANISH, "spanish", 0, 0, 150 }, + { CC_TCHINESE, "tchinese", 150, 0, 150 }, + { CC_JAPANESE, "japanese", 250, 150, 0 }, + { CC_RUSSIAN, "russian", 0, 250, 150 }, + { CC_THAI, "thai", 0 , 150, 250 }, + { CC_PORTUGUESE,"portuguese", 0 , 0, 150 }, +}; + +#pragma pack() + +void CSentence::ColorForLanguage( int language, unsigned char& r, unsigned char& g, unsigned char& b ) +{ + r = g = b = 0; + + if ( language < 0 || language >= CC_NUM_LANGUAGES ) + { + return; + } + + r = g_CCLanguageLookup[ language ].r; + g = g_CCLanguageLookup[ language ].g; + b = g_CCLanguageLookup[ language ].b; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : language - +// Output : char const +//----------------------------------------------------------------------------- +char const *CSentence::NameForLanguage( int language ) +{ + if ( language < 0 || language >= CC_NUM_LANGUAGES ) + return "unknown_language"; + + CCLanguage *entry = &g_CCLanguageLookup[ language ]; + Assert( entry->type == language ); + return entry->name; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +// Output : int +//----------------------------------------------------------------------------- +int CSentence::LanguageForName( char const *name ) +{ + int l; + for ( l = 0; l < CC_NUM_LANGUAGES; l++ ) + { + CCLanguage *entry = &g_CCLanguageLookup[ l ]; + Assert( entry->type == l ); + if ( !stricmp( entry->name, name ) ) + return l; + } + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSentence::CSentence( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = 0; + m_szText = 0; + m_uCheckSum = 0; +#endif + m_bShouldVoiceDuck = false; + m_bStoreCheckSum = false; + m_bIsValid = false; + m_bIsCached = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSentence::~CSentence( void ) +{ + Reset(); +#if PHONEME_EDITOR + delete[] m_szText; +#endif +} + + +void CSentence::ParsePlaintext( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + char text[ 4096 ]; + text[ 0 ] = 0; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + Q_strncat( text, token, sizeof( text ), COPY_ALL_CHARACTERS ); + Q_strncat( text, " ", sizeof( text ), COPY_ALL_CHARACTERS ); + } + + SetText( text ); +} + +void CSentence::ParseWords( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + char word[ 256 ]; + float start, end; + + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + if ( stricmp( token, "WORD" ) ) + break; + + buf.GetString( token ); + Q_strncpy( word, token, sizeof( word ) ); + + buf.GetString( token ); + start = atof( token ); + buf.GetString( token ); + end = atof( token ); + + CWordTag *wt = new CWordTag( word ); + Assert( wt ); + wt->m_flStartTime = start; + wt->m_flEndTime = end; + + AddWordTag( wt ); + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + // Parse phoneme + int code; + char phonemename[ 256 ]; + float start, end; + float volume; + + code = atoi( token ); + + buf.GetString( token ); + Q_strncpy( phonemename, token, sizeof( phonemename ) ); + buf.GetString( token ); + start = atof( token ); + buf.GetString( token ); + end = atof( token ); + buf.GetString( token ); + volume = atof( token ); + + CPhonemeTag *pt = new CPhonemeTag(); + Assert( pt ); + pt->SetPhonemeCode( code ); + pt->SetTag( phonemename ); + pt->SetStartTime( start ); + pt->SetEndTime( end ); + + AddPhonemeTag( wt, pt ); + } + } +} + +void CSentence::ParseEmphasis( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + char t[ 256 ]; + Q_strncpy( t, token, sizeof( t ) ); + buf.GetString( token ); + + char value[ 256 ]; + Q_strncpy( value, token, sizeof( value ) ); + + CEmphasisSample sample; + sample.SetSelected( false ); + sample.time = atof( t ); + sample.value = atof( value ); + + + m_EmphasisSamples.AddToTail( sample ); + + } +} + +// This is obsolete, so it doesn't do anything with the data which is parsed. +void CSentence::ParseCloseCaption( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + // Format is + // language_name + // { + // PHRASE char streamlength "streambytes" starttime endtime + // PHRASE unicode streamlength "streambytes" starttime endtime + // } + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + buf.GetString( token ); + while ( 1 ) + { + if ( !stricmp( token, "}" ) ) + break; + + if ( stricmp( token, "PHRASE" ) ) + break; + + char cc_type[32]; + char cc_stream[ 4096 ]; + int cc_length; + + memset( cc_stream, 0, sizeof( cc_stream ) ); + + buf.GetString( token ); + Q_strncpy( cc_type, token, sizeof( cc_type ) ); + + bool unicode = false; + if ( !stricmp( cc_type, "unicode" ) ) + { + unicode = true; + } + else if ( stricmp( cc_type, "char" ) ) + { + Assert( 0 ); + } + + buf.GetString( token ); + cc_length = atoi( token ); + Assert( cc_length >= 0 && cc_length < sizeof( cc_stream ) ); + // Skip space + buf.GetChar(); + buf.Get( cc_stream, cc_length ); + cc_stream[ cc_length ] = 0; + + // Skip space + buf.GetChar(); + buf.GetString( token ); + buf.GetString( token ); + + buf.GetString( token ); + } + } +} + +void CSentence::ParseOptions( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + while ( 1 ) + { + buf.GetString( token ); + if ( !stricmp( token, "}" ) ) + break; + + if ( Q_strlen( token ) == 0 ) + break; + + char key[ 256 ]; + Q_strncpy( key, token, sizeof( key ) ); + char value[ 256 ]; + buf.GetString( token ); + Q_strncpy( value, token, sizeof( value ) ); + + if ( !strcmpi( key, "voice_duck" ) ) + { + SetVoiceDuck( atoi(value) ? true : false ); + } + else if ( !strcmpi( key, "checksum" ) ) + { + SetDataCheckSum( (unsigned int)atoi( value ) ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: VERSION 1.0 parser, need to implement new ones if +// file format changes!!! +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::ParseDataVersionOnePointZero( CUtlBuffer& buf ) +{ + char token[ 4096 ]; + + while ( 1 ) + { + buf.GetString( token ); + if ( strlen( token ) <= 0 ) + break; + + char section[ 256 ]; + Q_strncpy( section, token, sizeof( section ) ); + + buf.GetString( token ); + if ( stricmp( token, "{" ) ) + break; + + if ( !stricmp( section, "PLAINTEXT" ) ) + { + ParsePlaintext( buf ); + } + else if ( !stricmp( section, "WORDS" ) ) + { + ParseWords( buf ); + } + else if ( !stricmp( section, "EMPHASIS" ) ) + { + ParseEmphasis( buf ); + } + else if ( !stricmp( section, "CLOSECAPTION" ) ) + { + // NOTE: CLOSECAPTION IS NO LONGER VALID + // This just skips the section of data. + ParseCloseCaption( buf ); + } + else if ( !stricmp( section, "OPTIONS" ) ) + { + ParseOptions( buf ); + } + } +} + +float g_maxTime; + +// This is a compressed save of just the data needed to drive phonemes in the engine (no word / sentence text, etc ) +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::CacheSaveToBuffer( CUtlBuffer& buf, int version ) +{ + Assert( !buf.IsText() ); + Assert( m_bIsCached ); + + int i; + unsigned short pcount = GetRuntimePhonemeCount(); + + // header + if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + buf.PutChar( version ); + buf.PutChar( 0 ); + buf.PutChar( 0 ); + buf.PutChar( 0 ); + buf.PutInt( pcount ); + } + else + { + buf.PutChar( version ); + buf.PutShort( pcount ); + } + + // phoneme + if ( version == CACHED_SENTENCE_VERSION_PACKED ) + { + for ( i = 0; i < pcount; ++i ) + { + const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); + Assert( phoneme ); + buf.PutUnsignedChar( CodeToByteCode( phoneme->GetPhonemeCode() ) ); + + float start = phoneme->GetStartTime() * 1000.0f/5.0f; + Assert( start >= -32768.0f && start <= 32767.0f ); + start = clamp( start, -32768.0f, 32767.0f ); + buf.PutShort( (short)start ); + + float end = phoneme->GetEndTime() * 1000.0f/5.0f; + Assert( end >= -32768.0f && end <= 32767.0f ); + end = clamp( end, -32768.0f, 32767.0f ); + buf.PutShort( (short)end ); + } + } + else if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + for ( i = 0; i < pcount; ++i ) + { + const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); + Assert( phoneme ); + buf.PutInt( phoneme->GetPhonemeCode() ); + buf.PutFloat( phoneme->GetStartTime() ); + buf.PutFloat( phoneme->GetEndTime() ); + } + } + else + { + for ( i = 0; i < pcount; ++i ) + { + const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); + Assert( phoneme ); + buf.PutShort( phoneme->GetPhonemeCode() ); + buf.PutFloat( phoneme->GetStartTime() ); + buf.PutFloat( phoneme->GetEndTime() ); + } + } + + // emphasis samples and voice duck + int c = m_EmphasisSamples.Count(); + Assert( c <= 32767 ); + + if ( version == CACHED_SENTENCE_VERSION_PACKED ) + { + buf.PutShort( c ); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[i]; + Assert( sample ); + + float scaledTime = sample->time * 1000.0f/5.0f; + Assert( scaledTime >= -32768.0f && scaledTime <= 32767.0f ); + scaledTime = clamp( scaledTime, -32768.0f, 32767.0f ); + buf.PutShort( scaledTime ); + + short scaledValue = (short)clamp( sample->value * 32767.0f, 0.0f, 32767.0f ); + buf.PutShort( scaledValue ); + } + buf.PutChar( GetVoiceDuck() ? 1 : 0 ); + } + else if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + buf.PutInt( c ); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[i]; + Assert( sample ); + buf.PutFloat( sample->time ); + buf.PutFloat( sample->value ); + } + buf.PutInt( GetVoiceDuck() ? 1 : 0 ); + } + else + { + buf.PutShort( c ); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[i]; + Assert( sample ); + buf.PutFloat( sample->time ); + short scaledValue = (short)clamp( sample->value * 32767.0f, 0.0f, 32767.0f ); + buf.PutShort( scaledValue ); + } + buf.PutChar( GetVoiceDuck() ? 1 : 0 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::CacheRestoreFromBuffer( CUtlBuffer& buf ) +{ + Assert( !buf.IsText() ); + + Reset(); + + m_bIsCached = true; + + // determine format + int version = buf.GetChar(); + if ( version != CACHED_SENTENCE_VERSION && + version != CACHED_SENTENCE_VERSION_ALIGNED && + version != CACHED_SENTENCE_VERSION_PACKED ) + { + // Uh oh, version changed... + m_bIsValid = false; + return; + } + + unsigned short pcount; + if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + buf.GetChar(); + buf.GetChar(); + buf.GetChar(); + pcount = buf.GetInt(); + } + else + { + pcount = (unsigned short)buf.GetShort(); + } + + // phonemes + CPhonemeTag pt; + int i; + if ( version == CACHED_SENTENCE_VERSION_PACKED ) + { + for ( i = 0; i < pcount; ++i ) + { + unsigned char code = buf.GetUnsignedChar(); + float st = (float)buf.GetShort() * 5.0f/1000.0f; + float et = (float)buf.GetShort() * 5.0f/1000.0f; + + pt.SetPhonemeCode( ByteCodeToCode( code ) ); + pt.SetStartTime( st ); + pt.SetEndTime( et ); + AddRuntimePhoneme( &pt ); + } + } + else if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + for ( i = 0; i < pcount; ++i ) + { + int code = buf.GetInt(); + float st = buf.GetFloat(); + float et = buf.GetFloat(); + + pt.SetPhonemeCode( code ); + pt.SetStartTime( st ); + pt.SetEndTime( et ); + AddRuntimePhoneme( &pt ); + } + } + else + { + for ( i = 0; i < pcount; ++i ) + { + unsigned short code = buf.GetShort(); + float st = buf.GetFloat(); + float et = buf.GetFloat(); + + pt.SetPhonemeCode( code ); + pt.SetStartTime( st ); + pt.SetEndTime( et ); + AddRuntimePhoneme( &pt ); + } + } + + // emphasis samples and voice duck + int c; + if ( version == CACHED_SENTENCE_VERSION_PACKED ) + { + c = buf.GetShort(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample sample; + sample.SetSelected( false ); + sample.time = (float)buf.GetShort() * 5.0f/1000.0f; + sample.value = (float)buf.GetShort() * 1.0f/32767.0f; + m_EmphasisSamples.AddToTail( sample ); + } + SetVoiceDuck( buf.GetChar() == 0 ? false : true ); + } + else if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) + { + c = buf.GetInt(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample sample; + sample.SetSelected( false ); + sample.time = buf.GetFloat(); + sample.value = buf.GetFloat(); + m_EmphasisSamples.AddToTail( sample ); + } + SetVoiceDuck( buf.GetInt() == 0 ? false : true ); + } + else + { + c = buf.GetShort(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample sample; + sample.SetSelected( false ); + sample.time = buf.GetFloat(); + sample.value = (float)buf.GetShort() / 32767.0f; + m_EmphasisSamples.AddToTail( sample ); + } + SetVoiceDuck( buf.GetChar() == 0 ? false : true ); + } + + m_bIsValid = true; +} + +int CSentence::GetRuntimePhonemeCount() const +{ + return m_RunTimePhonemes.Count(); +} + +const CBasePhonemeTag *CSentence::GetRuntimePhoneme( int i ) const +{ + Assert( m_bIsCached ); + return m_RunTimePhonemes[ i ]; +} + +void CSentence::ClearRuntimePhonemes() +{ + while ( m_RunTimePhonemes.Count() > 0 ) + { + CBasePhonemeTag *tag = m_RunTimePhonemes[ 0 ]; + delete tag; + m_RunTimePhonemes.Remove( 0 ); + } +} + +void CSentence::AddRuntimePhoneme( const CPhonemeTag *src ) +{ + Assert( m_bIsCached ); + + CBasePhonemeTag *tag = new CBasePhonemeTag(); + *tag = *src; + + m_RunTimePhonemes.AddToTail( tag ); +} + +void CSentence::MakeRuntimeOnly() +{ + m_bIsCached = true; +#if PHONEME_EDITOR + delete m_szText; + m_szText = NULL; + + int c = m_Words.Count(); + for ( int i = 0; i < c; ++i ) + { + CWordTag *word = m_Words[ i ]; + Assert( word ); + int pcount = word->m_Phonemes.Count(); + for ( int j = 0; j < pcount; ++j ) + { + CPhonemeTag *phoneme = word->m_Phonemes[ j ]; + Assert( phoneme ); + + AddRuntimePhoneme( phoneme ); + } + } + + // Remove all existing words + while ( m_Words.Count() > 0 ) + { + CWordTag *word = m_Words[ 0 ]; + delete word; + m_Words.Remove( 0 ); + } +#endif + m_bIsValid = true; +} + + +void CSentence::SaveToBuffer( CUtlBuffer& buf ) +{ +#if PHONEME_EDITOR + Assert( !m_bIsCached ); + + int i, j; + + buf.Printf( "VERSION 1.0\n" ); + + buf.Printf( "PLAINTEXT\n" ); + buf.Printf( "{\n" ); + buf.Printf( "%s\n", GetText() ); + buf.Printf( "}\n" ); + buf.Printf( "WORDS\n" ); + buf.Printf( "{\n" ); + for ( i = 0; i < m_Words.Count(); i++ ) + { + CWordTag *word = m_Words[ i ]; + Assert( word ); + + buf.Printf( "WORD %s %.3f %.3f\n", + word->GetWord(), + word->m_flStartTime, + word->m_flEndTime ); + + buf.Printf( "{\n" ); + for ( j = 0; j < word->m_Phonemes.Count(); j++ ) + { + CPhonemeTag *phoneme = word->m_Phonemes[ j ]; + Assert( phoneme ); + + buf.Printf( "%i %s %.3f %.3f 1\n", + phoneme->GetPhonemeCode(), + phoneme->GetTag(), + phoneme->GetStartTime(), + phoneme->GetEndTime() ); + } + + buf.Printf( "}\n" ); + } + buf.Printf( "}\n" ); + buf.Printf( "EMPHASIS\n" ); + buf.Printf( "{\n" ); + int c = m_EmphasisSamples.Count(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample *sample = &m_EmphasisSamples[ i ]; + Assert( sample ); + + buf.Printf( "%f %f\n", sample->time, sample->value ); + } + + buf.Printf( "}\n" ); + buf.Printf( "OPTIONS\n" ); + buf.Printf( "{\n" ); + buf.Printf( "voice_duck %d\n", GetVoiceDuck() ? 1 : 0 ); + if ( m_bStoreCheckSum ) + { + buf.Printf( "checksum %d\n", m_uCheckSum ); + } + buf.Printf( "}\n" ); +#else + Assert( 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *data - +// size - +//----------------------------------------------------------------------------- +void CSentence::InitFromDataChunk( void *data, int size ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + buf.EnsureCapacity( size ); + buf.Put( data, size ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, size ); + + InitFromBuffer( buf ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : buf - +//----------------------------------------------------------------------------- +void CSentence::InitFromBuffer( CUtlBuffer& buf ) +{ + Assert( buf.IsText() ); + + Reset(); + + char token[ 4096 ]; + buf.GetString( token ); + + if ( stricmp( token, "VERSION" ) ) + return; + + buf.GetString( token ); + if ( atof( token ) == 1.0f ) + { + ParseDataVersionOnePointZero( buf ); + m_bIsValid = true; + } + else + { + Assert( 0 ); + return; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSentence::GetWordBase( void ) +{ +#if PHONEME_EDITOR + return m_nResetWordBase; +#else + Assert( 0 ); + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::ResetToBase( void ) +{ +#if PHONEME_EDITOR + // Delete everything after m_nResetWordBase + while ( m_Words.Count() > m_nResetWordBase ) + { + delete m_Words[ m_Words.Count() - 1 ]; + m_Words.Remove( m_Words.Count() - 1 ); + } +#endif + ClearRuntimePhonemes(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::MarkNewPhraseBase( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = MAX( m_Words.Count(), 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::Reset( void ) +{ +#if PHONEME_EDITOR + m_nResetWordBase = 0; + + while ( m_Words.Count() > 0 ) + { + delete m_Words[ 0 ]; + m_Words.Remove( 0 ); + } +#endif + m_EmphasisSamples.RemoveAll(); + + ClearRuntimePhonemes(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +//----------------------------------------------------------------------------- +void CSentence::AddPhonemeTag( CWordTag *word, CPhonemeTag *tag ) +{ + word->m_Phonemes.AddToTail( tag ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *tag - +//----------------------------------------------------------------------------- +void CSentence::AddWordTag( CWordTag *tag ) +{ +#if PHONEME_EDITOR + m_Words.AddToTail( tag ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : int +//----------------------------------------------------------------------------- +int CSentence::CountPhonemes( void ) +{ + int c = 0; +#if PHONEME_EDITOR + for( int i = 0; i < m_Words.Count(); i++ ) + { + CWordTag *word = m_Words[ i ]; + c += word->m_Phonemes.Count(); + } +#endif + return c; +} + +//----------------------------------------------------------------------------- +// Purpose: // For legacy loading, try to find a word that contains the time +// Input : time - +// Output : CWordTag +//----------------------------------------------------------------------------- +CWordTag *CSentence::EstimateBestWord( float time ) +{ +#if PHONEME_EDITOR + CWordTag *bestWord = NULL; + + for( int i = 0; i < m_Words.Count(); i++ ) + { + CWordTag *word = m_Words[ i ]; + if ( !word ) + continue; + + if ( word->m_flStartTime <= time && word->m_flEndTime >= time ) + return word; + + if ( time < word->m_flStartTime ) + { + bestWord = word; + } + + if ( time > word->m_flEndTime && bestWord ) + return bestWord; + } + + // return best word if we found one + if ( bestWord ) + { + return bestWord; + } + + // Return last word + if ( m_Words.Count() >= 1 ) + { + return m_Words[ m_Words.Count() - 1 ]; + } +#endif + // Oh well + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *phoneme - +// Output : CWordTag +//----------------------------------------------------------------------------- +CWordTag *CSentence::GetWordForPhoneme( CPhonemeTag *phoneme ) +{ +#if PHONEME_EDITOR + for( int i = 0; i < m_Words.Count(); i++ ) + { + CWordTag *word = m_Words[ i ]; + if ( !word ) + continue; + + for ( int j = 0 ; j < word->m_Phonemes.Count() ; j++ ) + { + CPhonemeTag *p = word->m_Phonemes[ j ]; + if ( p == phoneme ) + { + return word; + } + } + + } +#endif + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Assignment operator +// Input : src - +// Output : CSentence& +//----------------------------------------------------------------------------- +CSentence& CSentence::operator=( const CSentence& src ) +{ + int i; + + // Clear current stuff + Reset(); + + int c; + +#if PHONEME_EDITOR + // Copy everything + for ( i = 0 ; i < src.m_Words.Count(); i++ ) + { + CWordTag *word = src.m_Words[ i ]; + + CWordTag *newWord = new CWordTag( *word ); + + AddWordTag( newWord ); + } + + SetText( src.GetText() ); + m_nResetWordBase = src.m_nResetWordBase; + + c = src.m_EmphasisSamples.Count(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample s = src.m_EmphasisSamples[ i ]; + m_EmphasisSamples.AddToTail( s ); + } +#endif + + m_bIsCached = src.m_bIsCached; + + c = src.GetRuntimePhonemeCount(); + for ( i = 0; i < c; i++ ) + { + Assert( m_bIsCached ); + + const CBasePhonemeTag *tag = src.GetRuntimePhoneme( i ); + CPhonemeTag full; + ((CBasePhonemeTag &)(full)) = *tag; + + AddRuntimePhoneme( &full ); + } + + m_bShouldVoiceDuck = src.m_bShouldVoiceDuck; +#if PHONEME_EDITOR + m_bStoreCheckSum = src.m_bStoreCheckSum; + m_uCheckSum = src.m_uCheckSum; +#endif + m_bIsValid = src.m_bIsValid; + + return (*this); +} + +void CSentence::Append( float starttime, const CSentence& src ) +{ +#if PHONEME_EDITOR + int i; + // Combine + for ( i = 0 ; i < src.m_Words.Count(); i++ ) + { + CWordTag *word = src.m_Words[ i ]; + + CWordTag *newWord = new CWordTag( *word ); + + newWord->m_flStartTime += starttime; + newWord->m_flEndTime += starttime; + + // Offset times + int c = newWord->m_Phonemes.Count(); + for ( int i = 0; i < c; ++i ) + { + CPhonemeTag *tag = newWord->m_Phonemes[ i ]; + tag->AddStartTime( starttime ); + tag->AddEndTime( starttime ); + } + + AddWordTag( newWord ); + } + + if ( src.GetText()[ 0 ] ) + { + char fulltext[ 4096 ]; + if ( GetText()[ 0 ] ) + { + Q_snprintf( fulltext, sizeof( fulltext ), "%s %s", GetText(), src.GetText() ); + } + else + { + Q_strncpy( fulltext, src.GetText(), sizeof( fulltext ) ); + } + SetText( fulltext ); + } + + int c = src.m_EmphasisSamples.Count(); + for ( i = 0; i < c; i++ ) + { + CEmphasisSample s = src.m_EmphasisSamples[ i ]; + + s.time += starttime; + + m_EmphasisSamples.AddToTail( s ); + } + + // Or in voice duck settings + m_bShouldVoiceDuck |= src.m_bShouldVoiceDuck; +#else + Assert( 0 ); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *text - +//----------------------------------------------------------------------------- +void CSentence::SetText( const char *text ) +{ +#if PHONEME_EDITOR + delete[] m_szText; + m_szText = NULL; + + if ( !text || !text[ 0 ] ) + { + return; + } + + int len = Q_strlen( text ) + 1; + + m_szText = new char[ len ]; + Assert( m_szText ); + Q_strncpy( m_szText, text, len ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : const char +//----------------------------------------------------------------------------- +const char *CSentence::GetText( void ) const +{ +#if PHONEME_EDITOR + return m_szText ? m_szText : ""; +#else + return ""; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::SetTextFromWords( void ) +{ +#if PHONEME_EDITOR + char fulltext[ 1024 ]; + fulltext[ 0 ] = 0; + for ( int i = 0 ; i < m_Words.Count(); i++ ) + { + CWordTag *word = m_Words[ i ]; + + Q_strncat( fulltext, word->GetWord(), sizeof( fulltext ), COPY_ALL_CHARACTERS ); + + if ( i != m_Words.Count() ) + { + Q_strncat( fulltext, " ", sizeof( fulltext ), COPY_ALL_CHARACTERS ); + } + } + + SetText( fulltext ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSentence::Resort( void ) +{ + int c = m_EmphasisSamples.Count(); + for ( int i = 0; i < c; i++ ) + { + for ( int j = i + 1; j < c; j++ ) + { + CEmphasisSample src = m_EmphasisSamples[ i ]; + CEmphasisSample dest = m_EmphasisSamples[ j ]; + + if ( src.time > dest.time ) + { + m_EmphasisSamples[ i ] = dest; + m_EmphasisSamples[ j ] = src; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : number - +// Output : CEmphasisSample +//----------------------------------------------------------------------------- +CEmphasisSample *CSentence::GetBoundedSample( int number, float endtime ) +{ + // Search for two samples which span time f + static CEmphasisSample nullstart; + nullstart.time = 0.0f; + nullstart.value = 0.5f; + static CEmphasisSample nullend; + nullend.time = endtime; + nullend.value = 0.5f; + + if ( number < 0 ) + { + return &nullstart; + } + else if ( number >= GetNumSamples() ) + { + return &nullend; + } + + return GetSample( number ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : time - +// type - +// Output : float +//----------------------------------------------------------------------------- +float CSentence::GetIntensity( float time, float endtime ) +{ + float zeroValue = 0.5f; + + int c = GetNumSamples(); + + if ( c <= 0 ) + { + return zeroValue; + } + + int i; + for ( i = -1 ; i < c; i++ ) + { + CEmphasisSample *s = GetBoundedSample( i, endtime ); + CEmphasisSample *n = GetBoundedSample( i + 1, endtime ); + if ( !s || !n ) + continue; + + if ( time >= s->time && time <= n->time ) + { + break; + } + } + + int prev = i - 1; + int start = i; + int end = i + 1; + int next = i + 2; + + prev = MAX( -1, prev ); + start = MAX( -1, start ); + end = MIN( end, GetNumSamples() ); + next = MIN( next, GetNumSamples() ); + + CEmphasisSample *esPre = GetBoundedSample( prev, endtime ); + CEmphasisSample *esStart = GetBoundedSample( start, endtime ); + CEmphasisSample *esEnd = GetBoundedSample( end, endtime ); + CEmphasisSample *esNext = GetBoundedSample( next, endtime ); + + float dt = esEnd->time - esStart->time; + dt = clamp( dt, 0.01f, 1.0f ); + + Vector vPre( esPre->time, esPre->value, 0 ); + Vector vStart( esStart->time, esStart->value, 0 ); + Vector vEnd( esEnd->time, esEnd->value, 0 ); + Vector vNext( esNext->time, esNext->value, 0 ); + + float f2 = ( time - esStart->time ) / ( dt ); + f2 = clamp( f2, 0.0f, 1.0f ); + + Vector vOut; + Catmull_Rom_Spline( + vPre, + vStart, + vEnd, + vNext, + f2, + vOut ); + + float retval = clamp( vOut.y, 0.0f, 1.0f ); + return retval; +} + +int CSentence::GetNumSamples( void ) +{ + return m_EmphasisSamples.Count(); +} + +CEmphasisSample *CSentence::GetSample( int index ) +{ + if ( index < 0 || index >= GetNumSamples() ) + return NULL; + + return &m_EmphasisSamples[ index ]; +} + +void CSentence::GetEstimatedTimes( float& start, float &end ) +{ +#if PHONEME_EDITOR + float beststart = 100000.0f; + float bestend = -100000.0f; + + int c = m_Words.Count(); + if ( !c ) + { + start = end = 0.0f; + return; + } + + for ( int i = 0; i< c; i++ ) + { + CWordTag *w = m_Words[ i ]; + Assert( w ); + if ( w->m_flStartTime < beststart ) + { + beststart = w->m_flStartTime; + } + if ( w->m_flEndTime > bestend ) + { + bestend = w->m_flEndTime; + } + } + + if ( beststart == 100000.0f ) + { + Assert( 0 ); + beststart = 0.0f; + } + if ( bestend == -100000.0f ) + { + Assert( 0 ); + bestend = 1.0f; + } + start = beststart; + end = bestend; +#endif +} + +void CSentence::SetDataCheckSum( unsigned int chk ) +{ +#if PHONEME_EDITOR + m_bStoreCheckSum = true; + m_uCheckSum = chk; +#endif +} + +unsigned int CSentence::ComputeDataCheckSum() +{ +#if PHONEME_EDITOR + int i; + int c; + CRC32_t crc; + CRC32_Init( &crc ); + + // Checksum the text + CRC32_ProcessBuffer( &crc, GetText(), Q_strlen( GetText() ) ); + // Checsum words and phonemes + c = m_Words.Count(); + for ( i = 0; i < c; ++i ) + { + CWordTag *word = m_Words[ i ]; + unsigned int wordCheckSum = word->ComputeDataCheckSum(); + CRC32_ProcessBuffer( &crc, &wordCheckSum, sizeof( unsigned int ) ); + } + + // Checksum emphasis data + c = m_EmphasisSamples.Count(); + for ( i = 0; i < c; ++i ) + { + CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].time, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, &m_EmphasisSamples[ i ].value, sizeof( float ) ); + } + + CRC32_Final( &crc ); + + return ( unsigned int )crc; +#else + Assert( 0 ); + return 0; +#endif +} + +unsigned int CSentence::GetDataCheckSum() const +{ +#if PHONEME_EDITOR + Assert( m_bStoreCheckSum ); + Assert( m_uCheckSum != 0 ); + return m_uCheckSum; +#else + Assert( 0 ); + return 0; +#endif +} + +#define STARTEND_TIMEGAP 0.1 + +int CSentence::CountWords( char const *str ) +{ + if ( !str || !str[ 0 ] ) + return 0; + + int c = 1; + + unsigned char *p = (unsigned char *)str; + while ( *p ) + { + if ( *p <= 32 ) + { + c++; + + while ( *p && *p <= 32 ) + { + p++; + } + } + + if ( !(*p) ) + break; + + p++; + } + + return c; +} + + +//----------------------------------------------------------------------------- +// Purpose: Static method +// Input : in - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSentence::ShouldSplitWord( char in ) +{ + if ( in <= 32 ) + return true; + + if ( ispunct( in ) ) + { + // don't split on apostrophe + if ( in == '\'' ) + return false; + return true; + } + + return false; +} + +void CSentence::CreateEventWordDistribution( char const *pszText, float flSentenceDuration ) +{ + Assert( pszText ); + if ( !pszText ) + return; + + int wordCount = CountWords( pszText ); + if ( wordCount <= 0 ) + return; + + float wordLength = ( flSentenceDuration - 2 * STARTEND_TIMEGAP) / (float)wordCount; + float wordStart = STARTEND_TIMEGAP; + + Reset(); + + char word[ 256 ]; + unsigned char const *in = (unsigned char *)pszText; + char *out = word; + + while ( *in ) + { + if ( !ShouldSplitWord( *in ) ) + { + *out++ = *in++; + } + else + { + *out = 0; + + // Skip over splitters + while ( *in && ( ShouldSplitWord( *in ) ) ) + { + in++; + } + + if ( strlen( word ) > 0 ) + { + CWordTag *w = new CWordTag(); + Assert( w ); + w->SetWord( word ); + w->m_flStartTime = wordStart; + w->m_flEndTime = wordStart + wordLength; + + AddWordTag( w ); + + wordStart += wordLength; + } + + out = word; + } + } + + *out = 0; + + if ( strlen( word ) > 0 ) + { + CWordTag *w = new CWordTag(); + Assert( w ); + w->SetWord( word ); + w->m_flStartTime = wordStart; + w->m_flEndTime = wordStart + wordLength; + + AddWordTag( w ); + + wordStart += wordLength; + } +} + + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/sentence.h b/public/sentence.h new file mode 100644 index 0000000..bbaf118 --- /dev/null +++ b/public/sentence.h @@ -0,0 +1,303 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SENTENCE_H +#define SENTENCE_H +#ifdef _WIN32 +#pragma once +#endif + +// X360 optimizes out the extra memory needed by the editors in these types +#ifndef _X360 +#define PHONEME_EDITOR 1 +#endif + +#include "utlvector.h" + +class CUtlBuffer; + +#define CACHED_SENTENCE_VERSION 1 +#define CACHED_SENTENCE_VERSION_ALIGNED 4 +#define CACHED_SENTENCE_VERSION_PACKED 5 + +//----------------------------------------------------------------------------- +// Purpose: A sample point +//----------------------------------------------------------------------------- +// Can't do this due to backward compat issues +//#ifdef _WIN32 +//#pragma pack (1) +//#endif + +struct CEmphasisSample +{ + float time; + float value; + + void SetSelected( bool isSelected ); +#if PHONEME_EDITOR + // Used by editors only + bool selected; +#endif +}; + +class CBasePhonemeTag +{ +public: + CBasePhonemeTag(); + CBasePhonemeTag( const CBasePhonemeTag& from ); + + CBasePhonemeTag &operator=( const CBasePhonemeTag &from ) { memcpy( this, &from, sizeof(*this) ); return *this; } + + float GetStartTime() const { return m_flStartTime; } + void SetStartTime( float startTime ) { m_flStartTime = startTime; } + void AddStartTime( float startTime ) { m_flStartTime += startTime; } + + float GetEndTime() const { return m_flEndTime; } + void SetEndTime( float endTime ) { m_flEndTime = endTime; } + void AddEndTime( float startTime ) { m_flEndTime += startTime; } + + int GetPhonemeCode() const { return m_nPhonemeCode; } + void SetPhonemeCode( int phonemeCode ) { m_nPhonemeCode = phonemeCode; } + +private: + float m_flStartTime; + float m_flEndTime; + unsigned short m_nPhonemeCode; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CPhonemeTag : public CBasePhonemeTag +{ + typedef CBasePhonemeTag BaseClass; +public: + + CPhonemeTag( void ); + CPhonemeTag( const char *phoneme ); + CPhonemeTag( const CPhonemeTag& from ); + ~CPhonemeTag( void ); + + void SetTag( const char *phoneme ); + char const *GetTag() const; + + unsigned int ComputeDataCheckSum(); +#if PHONEME_EDITOR + bool m_bSelected; + unsigned int m_uiStartByte; + unsigned int m_uiEndByte; +#endif + void SetSelected( bool isSelected ); + bool GetSelected() const; + void SetStartAndEndBytes( unsigned int start, unsigned int end ); + unsigned int GetStartByte() const; + unsigned int GetEndByte() const; + +private: + char *m_szPhoneme; + +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CWordTag +{ +public: + CWordTag( void ); + CWordTag( const char *word ); + CWordTag( const CWordTag& from ); + ~CWordTag( void ); + + void SetWord( const char *word ); + const char *GetWord() const; + + int IndexOfPhoneme( CPhonemeTag *tag ); + + unsigned int ComputeDataCheckSum(); + + float m_flStartTime; + float m_flEndTime; + + CUtlVector < CPhonemeTag *> m_Phonemes; +#if PHONEME_EDITOR + bool m_bSelected; + unsigned int m_uiStartByte; + unsigned int m_uiEndByte; +#endif + void SetSelected( bool isSelected ); + bool GetSelected() const; + void SetStartAndEndBytes( unsigned int start, unsigned int end ); + unsigned int GetStartByte() const; + unsigned int GetEndByte() const; + +private: + char *m_pszWord; +}; + +// A sentence can be closed captioned +// The default case is the entire sentence shown at start time +// +// "The default case" +// "is the entire sentence shown at start time" + +// Commands that aren't closed at end of phrase are automatically terminated +// +// Commands +// The line should persist for 2.0 seconds beyond m_flEndTime +// Don't go to new line for next phrase on stack +// Push current color onto stack and start drawing with new +// color until we reach the next marker or a with no commands which +// means restore the previous color +// Underline text (start/end) +// Italics text (start/end) +// Bold text (start/end) +// Draw caption at special location ??? needed +// Go to new line + +// Close Captioning Support +// The phonemes drive the mouth in english, but the CC text can +// be one of several languages +enum +{ + CC_ENGLISH = 0, + CC_FRENCH, + CC_GERMAN, + CC_ITALIAN, + CC_KOREAN, + CC_SCHINESE, // Simplified Chinese + CC_SPANISH, + CC_TCHINESE, // Traditional Chinese + CC_JAPANESE, + CC_RUSSIAN, + CC_THAI, + CC_PORTUGUESE, + // etc etc + + CC_NUM_LANGUAGES +}; + +//----------------------------------------------------------------------------- +// Purpose: A sentence is a box of words, and words contain phonemes +//----------------------------------------------------------------------------- +class CSentence +{ +public: + static char const *NameForLanguage( int language ); + static int LanguageForName( char const *name ); + static void ColorForLanguage( int language, unsigned char& r, unsigned char& g, unsigned char& b ); + + // Construction + CSentence( void ); + ~CSentence( void ); + + // Assignment operator + CSentence& operator =(const CSentence& src ); + + void Append( float starttime, const CSentence& src ); + + void SetText( const char *text ); + const char *GetText( void ) const; + + void InitFromDataChunk( void *data, int size ); + void InitFromBuffer( CUtlBuffer& buf ); + void SaveToBuffer( CUtlBuffer& buf ); + + // This strips out all of the stuff used by the editor, leaving just one blank work, no sentence text, and just + // the phonemes without the phoneme text...(same as the cacherestore version below) + void MakeRuntimeOnly(); + + // This is a compressed save of just the data needed to drive phonemes in the engine (no word / sentence text, etc ) + void CacheSaveToBuffer( CUtlBuffer& buf, int version ); + void CacheRestoreFromBuffer( CUtlBuffer& buf ); + + // Add word/phoneme to sentence + void AddPhonemeTag( CWordTag *word, CPhonemeTag *tag ); + void AddWordTag( CWordTag *tag ); + + void Reset( void ); + + void ResetToBase( void ); + + void MarkNewPhraseBase( void ); + + int GetWordBase( void ); + + int CountPhonemes( void ); + + // For legacy loading, try to find a word that contains the time + CWordTag *EstimateBestWord( float time ); + + CWordTag *GetWordForPhoneme( CPhonemeTag *phoneme ); + + void SetTextFromWords( void ); + + float GetIntensity( float time, float endtime ); + void Resort( void ); + CEmphasisSample *GetBoundedSample( int number, float endtime ); + int GetNumSamples( void ); + CEmphasisSample *GetSample( int index ); + + // Compute start and endtime based on all words + void GetEstimatedTimes( float& start, float &end ); + + void SetVoiceDuck( bool shouldDuck ) { m_bShouldVoiceDuck = shouldDuck; } + bool GetVoiceDuck() const { return m_bShouldVoiceDuck; } + + unsigned int ComputeDataCheckSum(); + + void SetDataCheckSum( unsigned int chk ); + unsigned int GetDataCheckSum() const; + + int GetRuntimePhonemeCount() const; + const CBasePhonemeTag *GetRuntimePhoneme( int i ) const; + void ClearRuntimePhonemes(); + void AddRuntimePhoneme( const CPhonemeTag *src ); + + void CreateEventWordDistribution( char const *pszText, float flSentenceDuration ); + static int CountWords( char const *pszText ); + static bool ShouldSplitWord( char in ); + +public: +#if PHONEME_EDITOR + char *m_szText; + + CUtlVector< CWordTag * > m_Words; +#endif + CUtlVector < CBasePhonemeTag *> m_RunTimePhonemes; + +#if PHONEME_EDITOR + int m_nResetWordBase; +#endif + // Phoneme emphasis data + CUtlVector< CEmphasisSample > m_EmphasisSamples; + +#if PHONEME_EDITOR + unsigned int m_uCheckSum; +#endif + bool m_bIsValid : 8; + bool m_bStoreCheckSum : 8; + bool m_bShouldVoiceDuck : 8; + bool m_bIsCached : 8; + +private: + void ParseDataVersionOnePointZero( CUtlBuffer& buf ); + void ParsePlaintext( CUtlBuffer& buf ); + void ParseWords( CUtlBuffer& buf ); + void ParseEmphasis( CUtlBuffer& buf ); + void ParseOptions( CUtlBuffer& buf ); + void ParseCloseCaption( CUtlBuffer& buf ); + + void ResetCloseCaptionAll( void ); + + friend class PhonemeEditor; +}; + +//#ifdef _WIN32 +//#pragma pack () +//#endif + +#endif // SENTENCE_H diff --git a/public/server_class.cpp b/public/server_class.cpp new file mode 100644 index 0000000..0d53686 --- /dev/null +++ b/public/server_class.cpp @@ -0,0 +1,16 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "server_class.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ServerClass *g_pServerClassHead=0; + + diff --git a/public/server_class.h b/public/server_class.h new file mode 100644 index 0000000..5e40d9b --- /dev/null +++ b/public/server_class.h @@ -0,0 +1,148 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef SERVER_CLASS_H +#define SERVER_CLASS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "dt_send.h" +#include "networkstringtabledefs.h" + + +class ServerClass; +class SendTable; + +extern ServerClass *g_pServerClassHead; + + +class ServerClass +{ +public: + ServerClass( char *pNetworkName, SendTable *pTable ) + { + m_pNetworkName = pNetworkName; + m_pTable = pTable; + m_InstanceBaselineIndex = INVALID_STRING_INDEX; + // g_pServerClassHead is sorted alphabetically, so find the correct place to insert + if ( !g_pServerClassHead ) + { + g_pServerClassHead = this; + m_pNext = NULL; + } + else + { + ServerClass *p1 = g_pServerClassHead; + ServerClass *p2 = p1->m_pNext; + + // Comment from Alfred on 7/2/2004 6:43:24 PM in CL 91253, //ValveGames/main/src/public/server_class.h#18: + // ---> use _stricmp because Q_stricmp isn't hooked up properly yet + // [Sergiy, 10/19/2009] hooking up V_stricmp + if ( V_stricmp( p1->GetName(), pNetworkName ) > 0) + { + m_pNext = g_pServerClassHead; + g_pServerClassHead = this; + p1 = NULL; + } + + while( p1 ) + { + if ( p2 == NULL || V_stricmp( p2->GetName(), pNetworkName ) > 0) + { + m_pNext = p2; + p1->m_pNext = this; + break; + } + p1 = p2; + p2 = p2->m_pNext; + } + } + } + + const char* GetName() { return m_pNetworkName; } + + +public: + char *m_pNetworkName; + SendTable *m_pTable; + ServerClass *m_pNext; + int m_ClassID; // Managed by the engine. + + // This is an index into the network string table (sv.GetInstanceBaselineTable()). + int m_InstanceBaselineIndex; // INVALID_STRING_INDEX if not initialized yet. +}; + + +class CBaseNetworkable; + +// If you do a DECLARE_SERVERCLASS, you need to do this inside the class definition. +#define DECLARE_SERVERCLASS() \ + public: \ + virtual ServerClass* GetServerClass(); \ + static SendTable *m_pClassSendTable; \ + template friend int ServerClassInit(T *); \ + virtual int YouForgotToImplementOrDeclareServerClass(); \ + +#define DECLARE_SERVERCLASS_NOBASE() \ + public: \ + template friend int ServerClassInit(T *); \ + +// Use this macro to expose your class's data across the network. +#define IMPLEMENT_SERVERCLASS( DLLClassName, sendTable ) \ + IMPLEMENT_SERVERCLASS_INTERNAL( DLLClassName, sendTable ) + +// You can use this instead of BEGIN_SEND_TABLE and it will do a DECLARE_SERVERCLASS automatically. +#define IMPLEMENT_SERVERCLASS_ST(DLLClassName, sendTable) \ + IMPLEMENT_SERVERCLASS_INTERNAL( DLLClassName, sendTable )\ + BEGIN_SEND_TABLE(DLLClassName, sendTable) + +#define IMPLEMENT_SERVERCLASS_ST_NOBASE(DLLClassName, sendTable) \ + IMPLEMENT_SERVERCLASS_INTERNAL( DLLClassName, sendTable )\ + BEGIN_SEND_TABLE_NOBASE( DLLClassName, sendTable ) + + +#ifdef VALIDATE_DECLARE_CLASS + #define CHECK_DECLARE_CLASS( DLLClassName, sendTable ) \ + template int CheckDeclareClass_Access(T *); \ + template <> int CheckDeclareClass_Access(sendTable::ignored *, const char *pIgnored) \ + { \ + return DLLClassName::CheckDeclareClass( #DLLClassName ); \ + } \ + namespace sendTable \ + { \ + int verifyDeclareClass = CheckDeclareClass_Access( (sendTable::ignored*)0 ); \ + } +#else + #define CHECK_DECLARE_CLASS( DLLClassName, sendTable ) +#endif + + +#define IMPLEMENT_SERVERCLASS_INTERNAL( DLLClassName, sendTable ) \ + namespace sendTable \ + { \ + struct ignored; \ + extern SendTable g_SendTable; \ + } \ + CHECK_DECLARE_CLASS( DLLClassName, sendTable ) \ + static ServerClass g_##DLLClassName##_ClassReg(\ + #DLLClassName, \ + &sendTable::g_SendTable\ + ); \ + \ + ServerClass* DLLClassName::GetServerClass() {return &g_##DLLClassName##_ClassReg;} \ + SendTable *DLLClassName::m_pClassSendTable = &sendTable::g_SendTable;\ + int DLLClassName::YouForgotToImplementOrDeclareServerClass() {return 0;} + + +#endif + + + diff --git a/public/shaderapi/IShaderDevice.h b/public/shaderapi/IShaderDevice.h new file mode 100644 index 0000000..b47b326 --- /dev/null +++ b/public/shaderapi/IShaderDevice.h @@ -0,0 +1,386 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERDEVICE_H +#define ISHADERDEVICE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "appframework/IAppSystem.h" +#include "bitmap/imageformat.h" +#include "tier1/utlbuffer.h" +#include "materialsystem/imaterial.h" +#include "shaderapi/ishaderdynamic.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct MaterialAdapterInfo_t; +class IMesh; +class KeyValues; + + +//----------------------------------------------------------------------------- +// Describes how to set the mode +//----------------------------------------------------------------------------- +#define SHADER_DISPLAY_MODE_VERSION 1 + +struct ShaderDisplayMode_t +{ + ShaderDisplayMode_t() { memset( this, 0, sizeof(ShaderDisplayMode_t) ); m_nVersion = SHADER_DISPLAY_MODE_VERSION; } + + int m_nVersion; + int m_nWidth; // 0 when running windowed means use desktop resolution + int m_nHeight; + ImageFormat m_Format; // use ImageFormats (ignored for windowed mode) + int m_nRefreshRateNumerator; // Refresh rate. Use 0 in numerator + denominator for a default setting. + int m_nRefreshRateDenominator; // Refresh rate = numerator / denominator. +}; + + +//----------------------------------------------------------------------------- +// Describes how to set the device +//----------------------------------------------------------------------------- +#define SHADER_DEVICE_INFO_VERSION 1 + +struct ShaderDeviceInfo_t +{ + ShaderDeviceInfo_t() { memset( this, 0, sizeof(ShaderDeviceInfo_t) ); m_nVersion = SHADER_DEVICE_INFO_VERSION; m_DisplayMode.m_nVersion = SHADER_DISPLAY_MODE_VERSION; } + + int m_nVersion; + ShaderDisplayMode_t m_DisplayMode; + int m_nBackBufferCount; // valid values are 1 or 2 [2 results in triple buffering] + int m_nAASamples; // Number of AA samples to use + int m_nAAQuality; // AA quality level + int m_nDXLevel; // 0 means use recommended DX level for this adapter + int m_nWindowedSizeLimitWidth; // Used if m_bLimitWindowedSize is set, defines max bounds for the back buffer + int m_nWindowedSizeLimitHeight; + + bool m_bWindowed : 1; + bool m_bResizing : 1; // Only is meaningful when using windowed mode; means the window can be resized. + bool m_bUseStencil : 1; + bool m_bLimitWindowedSize : 1; // In windowed mode, should we prevent the back buffer from getting too large? + bool m_bWaitForVSync : 1; // Would we not present until vsync? + bool m_bScaleToOutputResolution : 1; // 360 ONLY: sets up hardware scaling + bool m_bProgressive : 1; // 360 ONLY: interlaced or progressive + bool m_bUsingMultipleWindows : 1; // Forces D3DPresent to use _COPY instead +}; + + +//----------------------------------------------------------------------------- +// Info for non-interactive mode +//----------------------------------------------------------------------------- +struct ShaderNonInteractiveInfo_t +{ + ShaderAPITextureHandle_t m_hTempFullscreenTexture; + int m_nPacifierCount; + ShaderAPITextureHandle_t m_pPacifierTextures[64]; + float m_flNormalizedX; + float m_flNormalizedY; + float m_flNormalizedSize; +}; + + +//----------------------------------------------------------------------------- +// For vertex/index buffers. What type is it? +// (NOTE: mirror this with a similarly named enum at the material system level for backwards compatability.) +//----------------------------------------------------------------------------- +enum ShaderBufferType_t +{ + SHADER_BUFFER_TYPE_STATIC = 0, + SHADER_BUFFER_TYPE_DYNAMIC, + SHADER_BUFFER_TYPE_STATIC_TEMP, + SHADER_BUFFER_TYPE_DYNAMIC_TEMP, + + SHADER_BUFFER_TYPE_COUNT, +}; + +inline bool IsDynamicBufferType( ShaderBufferType_t type ) +{ + return ( ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP ) ); +} + + +//----------------------------------------------------------------------------- +// Handle to a vertex, pixel, and geometry shader +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( VertexShaderHandle_t ); +DECLARE_POINTER_HANDLE( GeometryShaderHandle_t ); +DECLARE_POINTER_HANDLE( PixelShaderHandle_t ); + +#define VERTEX_SHADER_HANDLE_INVALID ( (VertexShaderHandle_t)0 ) +#define GEOMETRY_SHADER_HANDLE_INVALID ( (GeometryShaderHandle_t)0 ) +#define PIXEL_SHADER_HANDLE_INVALID ( (PixelShaderHandle_t)0 ) + + +//----------------------------------------------------------------------------- +// A shader buffer returns a block of memory which must be released when done with it +//----------------------------------------------------------------------------- +abstract_class IShaderBuffer +{ +public: + virtual size_t GetSize() const = 0; + virtual const void* GetBits() const = 0; + virtual void Release() = 0; +}; + + +//----------------------------------------------------------------------------- +// Mode chance callback +//----------------------------------------------------------------------------- +typedef void (*ShaderModeChangeCallbackFunc_t)( void ); + + +//----------------------------------------------------------------------------- +// Methods related to discovering and selecting devices +//----------------------------------------------------------------------------- +#define SHADER_DEVICE_MGR_INTERFACE_VERSION "ShaderDeviceMgr001" +abstract_class IShaderDeviceMgr : public IAppSystem +{ +public: + // Gets the number of adapters... + virtual int GetAdapterCount() const = 0; + + // Returns info about each adapter + virtual void GetAdapterInfo( int nAdapter, MaterialAdapterInfo_t& info ) const = 0; + + // Gets recommended congifuration for a particular adapter at a particular dx level + virtual bool GetRecommendedConfigurationInfo( int nAdapter, int nDXLevel, KeyValues *pConfiguration ) = 0; + + // Returns the number of modes + virtual int GetModeCount( int nAdapter ) const = 0; + + // Returns mode information.. + virtual void GetModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter, int nMode ) const = 0; + + // Returns the current mode info for the requested adapter + virtual void GetCurrentModeInfo( ShaderDisplayMode_t* pInfo, int nAdapter ) const = 0; + + // Initialization, shutdown + virtual bool SetAdapter( int nAdapter, int nFlags ) = 0; + + // Sets the mode + // Use the returned factory to get at an IShaderDevice and an IShaderRender + // and any other interfaces we decide to create. + // A returned factory of NULL indicates the mode was not set properly. + virtual CreateInterfaceFn SetMode( void *hWnd, int nAdapter, const ShaderDeviceInfo_t& mode ) = 0; + + // Installs a callback to get called + virtual void AddModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) = 0; + virtual void RemoveModeChangeCallback( ShaderModeChangeCallbackFunc_t func ) = 0; + + virtual bool GetRecommendedVideoConfig( int nAdapter, KeyValues *pConfiguration ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Methods related to control of the device +//----------------------------------------------------------------------------- +#define SHADER_DEVICE_INTERFACE_VERSION "ShaderDevice001" +abstract_class IShaderDevice +{ +public: + // Releases/reloads resources when other apps want some memory + virtual void ReleaseResources( bool bReleaseManagedResources = true ) = 0; + virtual void ReacquireResources() = 0; + + // returns the backbuffer format and dimensions + virtual ImageFormat GetBackBufferFormat() const = 0; + virtual void GetBackBufferDimensions( int& width, int& height ) const = 0; + + // Returns the current adapter in use + virtual int GetCurrentAdapter() const = 0; + + // Are we using graphics? + virtual bool IsUsingGraphics() const = 0; + + // Use this to spew information about the 3D layer + virtual void SpewDriverInfo() const = 0; + + // What's the bit depth of the stencil buffer? + virtual int StencilBufferBits() const = 0; + + // Are we using a mode that uses MSAA + virtual bool IsAAEnabled() const = 0; + + // Does a page flip + virtual void Present() = 0; + + // Returns the window size + virtual void GetWindowSize( int &nWidth, int &nHeight ) const = 0; + + // Gamma ramp control + virtual void SetHardwareGammaRamp( float fGamma, float fGammaTVRangeMin, float fGammaTVRangeMax, float fGammaTVExponent, bool bTVEnabled ) = 0; + + // Creates/ destroys a child window + virtual bool AddView( void* hWnd ) = 0; + virtual void RemoveView( void* hWnd ) = 0; + + // Activates a view + virtual void SetView( void* hWnd ) = 0; + + // Shader compilation + virtual IShaderBuffer* CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) = 0; + + // Shader creation, destruction + virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer ) = 0; + virtual void DestroyVertexShader( VertexShaderHandle_t hShader ) = 0; + virtual GeometryShaderHandle_t CreateGeometryShader( IShaderBuffer* pShaderBuffer ) = 0; + virtual void DestroyGeometryShader( GeometryShaderHandle_t hShader ) = 0; + virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer ) = 0; + virtual void DestroyPixelShader( PixelShaderHandle_t hShader ) = 0; + + // Utility methods to make shader creation simpler + // NOTE: For the utlbuffer version, use a binary buffer for a compiled shader + // and a text buffer for a source-code (.fxc) shader + VertexShaderHandle_t CreateVertexShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + VertexShaderHandle_t CreateVertexShader( CUtlBuffer &buf, const char *pShaderVersion = NULL ); + GeometryShaderHandle_t CreateGeometryShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + GeometryShaderHandle_t CreateGeometryShader( CUtlBuffer &buf, const char *pShaderVersion = NULL ); + PixelShaderHandle_t CreatePixelShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ); + PixelShaderHandle_t CreatePixelShader( CUtlBuffer &buf, const char *pShaderVersion = NULL ); + + // NOTE: Deprecated!! Use CreateVertexBuffer/CreateIndexBuffer instead + // Creates/destroys Mesh + virtual IMesh* CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial * pMaterial = NULL, VertexStreamSpec_t *pStreamSpec = NULL ) = 0; + virtual void DestroyStaticMesh( IMesh* mesh ) = 0; + + // Creates/destroys static vertex + index buffers + virtual IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) = 0; + virtual void DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) = 0; + + virtual IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) = 0; + virtual void DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) = 0; + + // Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? + virtual IVertexBuffer *GetDynamicVertexBuffer( int nStreamID, VertexFormat_t vertexFormat, bool bBuffered = true ) = 0; + virtual IIndexBuffer *GetDynamicIndexBuffer() = 0; + + // A special path used to tick the front buffer while loading on the 360 + virtual void EnableNonInteractiveMode( MaterialNonInteractiveMode_t mode, ShaderNonInteractiveInfo_t *pInfo = NULL ) = 0; + virtual void RefreshFrontBufferNonInteractive( ) = 0; + virtual void HandleThreadEvent( uint32 threadEvent ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Helper wrapper for IShaderBuffer for reading precompiled shader files +// NOTE: This is meant to be instanced on the stack; so don't call Release! +//----------------------------------------------------------------------------- +class CUtlShaderBuffer : public IShaderBuffer +{ +public: + CUtlShaderBuffer( CUtlBuffer &buf ) : m_pBuf( &buf ) {} + + virtual size_t GetSize() const + { + return m_pBuf->TellMaxPut(); + } + + virtual const void* GetBits() const + { + return m_pBuf->Base(); + } + + virtual void Release() + { + Assert( 0 ); + } + +private: + CUtlBuffer *m_pBuf; +}; + + +//----------------------------------------------------------------------------- +// Inline methods of IShaderDevice +//----------------------------------------------------------------------------- +inline VertexShaderHandle_t IShaderDevice::CreateVertexShader( CUtlBuffer &buf, const char *pShaderVersion ) +{ + // NOTE: Text buffers are assumed to have source-code shader files + // Binary buffers are assumed to have compiled shader files + if ( buf.IsText() ) + { + Assert( pShaderVersion ); + return CreateVertexShader( (const char *)buf.Base(), buf.TellMaxPut(), pShaderVersion ); + } + + CUtlShaderBuffer shaderBuffer( buf ); + return CreateVertexShader( &shaderBuffer ); +} + +inline VertexShaderHandle_t IShaderDevice::CreateVertexShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + VertexShaderHandle_t hVertexShader = VERTEX_SHADER_HANDLE_INVALID; + IShaderBuffer* pShaderBuffer = CompileShader( pProgram, nBufLen, pShaderVersion ); + if ( pShaderBuffer ) + { + hVertexShader = CreateVertexShader( pShaderBuffer ); + pShaderBuffer->Release(); + } + return hVertexShader; +} + +inline GeometryShaderHandle_t IShaderDevice::CreateGeometryShader( CUtlBuffer &buf, const char *pShaderVersion ) +{ + // NOTE: Text buffers are assumed to have source-code shader files + // Binary buffers are assumed to have compiled shader files + if ( buf.IsText() ) + { + Assert( pShaderVersion ); + return CreateGeometryShader( (const char *)buf.Base(), buf.TellMaxPut(), pShaderVersion ); + } + + CUtlShaderBuffer shaderBuffer( buf ); + return CreateGeometryShader( &shaderBuffer ); +} + +inline GeometryShaderHandle_t IShaderDevice::CreateGeometryShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + GeometryShaderHandle_t hGeometryShader = GEOMETRY_SHADER_HANDLE_INVALID; + IShaderBuffer* pShaderBuffer = CompileShader( pProgram, nBufLen, pShaderVersion ); + if ( pShaderBuffer ) + { + hGeometryShader = CreateGeometryShader( pShaderBuffer ); + pShaderBuffer->Release(); + } + return hGeometryShader; +} + +inline PixelShaderHandle_t IShaderDevice::CreatePixelShader( CUtlBuffer &buf, const char *pShaderVersion ) +{ + // NOTE: Text buffers are assumed to have source-code shader files + // Binary buffers are assumed to have compiled shader files + if ( buf.IsText() ) + { + Assert( pShaderVersion ); + return CreatePixelShader( (const char *)buf.Base(), buf.TellMaxPut(), pShaderVersion ); + } + + CUtlShaderBuffer shaderBuffer( buf ); + return CreatePixelShader( &shaderBuffer ); +} + +inline PixelShaderHandle_t IShaderDevice::CreatePixelShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion ) +{ + PixelShaderHandle_t hPixelShader = PIXEL_SHADER_HANDLE_INVALID; + IShaderBuffer* pShaderBuffer = CompileShader( pProgram, nBufLen, pShaderVersion ); + if ( pShaderBuffer ) + { + hPixelShader = CreatePixelShader( pShaderBuffer ); + pShaderBuffer->Release(); + } + return hPixelShader; +} + + +#endif // ISHADERDEVICE_H diff --git a/public/shaderapi/commandbuffer.h b/public/shaderapi/commandbuffer.h new file mode 100644 index 0000000..bb56d58 --- /dev/null +++ b/public/shaderapi/commandbuffer.h @@ -0,0 +1,105 @@ +//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// This file defines a number of constants and structured which are used to build up a command +// buffer to pass to ShaderAPI for state setting and other operations. Since the prupose of these +// command buffers is to minimize and optimize calls into shaderapi, their structure is not +// abstract - they are built out by the calling process. +// +//===========================================================================// + +#ifndef COMMANDBUFFER_H +#define COMMANDBUFFER_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Commands used by the per-pass command buffers +//----------------------------------------------------------------------------- +enum CommandBufferCommand_t +{ + // flow control commands. + CBCMD_END = 0, // end of stream + CBCMD_JUMP, // int cmd, void *adr. jump to another + // stream. Can be used to implement + // non-sequentially allocated storage + CBCMD_JSR, // int cmd, void *adr. subroutine call to another stream. + + // constant setting commands + CBCMD_SET_PIXEL_SHADER_FLOAT_CONST, // int cmd,int first_reg, int nregs, float values[nregs*4] + + + CBCMD_SET_VERTEX_SHADER_FLOAT_CONST, // int cmd,int first_reg, int nregs, float values[nregs*4] + CBCMD_SET_VERTEX_SHADER_FLOAT_CONST_REF, // int cmd,int first_reg, int nregs, &float values[nregs*4] + CBCMD_SETPIXELSHADERFOGPARAMS, // int cmd, int regdest + CBCMD_STORE_EYE_POS_IN_PSCONST, // int cmd, int regdest + CBCMD_SET_DEPTH_FEATHERING_CONST, // int cmd, int constant register, float blend scale + + // texture binding + CBCMD_BIND_STANDARD_TEXTURE, // cmd, sampler, texture id + CBCMD_BIND_SHADERAPI_TEXTURE_HANDLE, // cmd, sampler, texture handle + + // shaders + CBCMD_SET_PSHINDEX, // cmd, idx + CBCMD_SET_VSHINDEX, // cmd, idx + + CBCMD_SET_VERTEX_SHADER_FLASHLIGHT_STATE, // cmd, int first_reg (for worldToTexture matrix) + CBCMD_SET_PIXEL_SHADER_FLASHLIGHT_STATE, // cmd, int color reg, int atten reg, int origin reg, sampler (for flashlight texture) + + CBCMD_SET_PIXEL_SHADER_UBERLIGHT_STATE, // cmd + + CBCMD_SET_VERTEX_SHADER_NEARZFARZ_STATE, // cmd +}; + +//----------------------------------------------------------------------------- +// Commands used by the per-instance command buffer +// NOTE: If you add commands, you probably want to change the size of +// CInstanceStorageBuffer and/or the choice of making it a fixed-size allocation +// see shaderlib/baseshader.* +// +// FIXME!! NOTE that this whole scheme here generates a dependency of the +// shaders on internal guts of shaderapidx8, since it's responsible for +// setting various pixel shader + vertex shader constants based on the +// commands below. We need to remove this dependency as it's way too restrictive +// and puts the smarts in the wrong place (see CBICMD_SETPIXELSHADERGLINTDAMPING +// as an example). Not going to solve this for l4d though, as I don't anticipate +// a large amount of new shader writing for that product. +//----------------------------------------------------------------------------- +enum CommandBufferInstanceCommand_t +{ + CBICMD_END = 0, // end of stream + CBICMD_JUMP, // int cmd, void *adr. jump to another + // stream. Can be used to implement + // non-sequentially allocated storage + CBICMD_JSR, // int cmd, void *adr. subroutine call to another stream. + + CBICMD_SETSKINNINGMATRICES, // int cmd + + CBICMD_SETVERTEXSHADERLOCALLIGHTING, // int cmd + CBICMD_SETPIXELSHADERLOCALLIGHTING, // int cmd, int regdest + CBICMD_SETVERTEXSHADERAMBIENTLIGHTCUBE, // int cmd + CBICMD_SETPIXELSHADERAMBIENTLIGHTCUBE, // int cmd, int regdest + CBICMD_SETPIXELSHADERAMBIENTLIGHTCUBELUMINANCE, // int cmd, int regdest + CBICMD_SETPIXELSHADERGLINTDAMPING, // int cmd, int regdest + + CBICMD_BIND_ENV_CUBEMAP_TEXTURE, // cmd, sampler + + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE, + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE_LINEARCOLORSPACE_LINEARSCALE, // int cmd, int constant register, Vector color2 + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE_LINEARCOLORSPACE, // int cmd, int constant register, Vector color2 + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE_LINEARSCALE, // int cmd, int constant register, Vector color2, float scale + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE_LINEARSCALE_SCALEINW, // int cmd, int constant register, Vector color2, float scale + CBICMD_SETMODULATIONVERTEXSHADERDYNAMICSTATE, // int cmd, int constant register, Vector color2 + CBICMD_SETMODULATIONPIXELSHADERDYNAMICSTATE_IDENTITY, // int cmd, int constant register + CBICMD_SETMODULATIONVERTEXSHADERDYNAMICSTATE_LINEARSCALE, // int cmd, int constant register, Vector color2, float scale + // This must be last + CBICMD_COUNT, +}; + +#endif // COMMANDBUFFER_H diff --git a/public/shaderapi/ishaderapi.h b/public/shaderapi/ishaderapi.h new file mode 100644 index 0000000..be99f73 --- /dev/null +++ b/public/shaderapi/ishaderapi.h @@ -0,0 +1,767 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERAPI_H +#define ISHADERAPI_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vector4d.h" +#include "shaderapi/ishaderdynamic.h" +#include "shaderapi/IShaderDevice.h" +#include "materialsystem/deformations.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IShaderUtil; +class IFileSystem; +class CPixelWriter; +class CMeshBuilder; +struct MaterialVideoMode_t; +class IMesh; +class IVertexBuffer; +class IIndexBuffer; +struct MeshDesc_t; +enum MaterialCullMode_t; +class IDataCache; +struct MorphWeight_t; +struct MeshInstanceData_t; +#ifdef _X360 +enum RTMultiSampleCount360_t; +#endif + +//----------------------------------------------------------------------------- +// This must match the definition in playback.cpp! +//----------------------------------------------------------------------------- +enum ShaderRenderTarget_t +{ + SHADER_RENDERTARGET_BACKBUFFER = -1, + SHADER_RENDERTARGET_DEPTHBUFFER = -1, + // GR - no RT, used to disable depth buffer + SHADER_RENDERTARGET_NONE = -2, +}; + + +//----------------------------------------------------------------------------- +// The state snapshot handle +//----------------------------------------------------------------------------- +typedef short StateSnapshot_t; + + +//----------------------------------------------------------------------------- +// The state snapshot handle +//----------------------------------------------------------------------------- +typedef int ShaderDLL_t; + + +//----------------------------------------------------------------------------- +// Texture creation +//----------------------------------------------------------------------------- +enum CreateTextureFlags_t +{ + TEXTURE_CREATE_CUBEMAP = 0x00001, + TEXTURE_CREATE_RENDERTARGET = 0x00002, + TEXTURE_CREATE_MANAGED = 0x00004, + TEXTURE_CREATE_DEPTHBUFFER = 0x00008, + TEXTURE_CREATE_DYNAMIC = 0x00010, + TEXTURE_CREATE_AUTOMIPMAP = 0x00020, + TEXTURE_CREATE_VERTEXTEXTURE = 0x00040, + TEXTURE_CREATE_CACHEABLE = 0x00080, // 360 only, texture may be subject to streaming + TEXTURE_CREATE_NOD3DMEMORY = 0x00100, // 360 only, real allocation needs to occur later + TEXTURE_CREATE_REDUCED = 0x00200, // 360 only, true dimensions forced smaller (i.e. exclusion) + TEXTURE_CREATE_EXCLUDED = 0x00400, // 360 only, marked as excluded + TEXTURE_CREATE_DEFAULT_POOL = 0x00800, + TEXTURE_CREATE_UNFILTERABLE_OK = 0x01000, + TEXTURE_CREATE_CANCONVERTFORMAT = 0x02000, // 360 only, allow format conversions at load + TEXTURE_CREATE_PWLCORRECTED = 0x04000, // 360 only, texture is pwl corrected + TEXTURE_CREATE_ERROR = 0x08000, // 360 only, texture was forced to checkerboard + TEXTURE_CREATE_SYSMEM = 0x10000, + TEXTURE_CREATE_SRGB = 0x20000, // Posix/GL only, for textures which are SRGB-readable +}; + + +//----------------------------------------------------------------------------- +// Viewport structure +//----------------------------------------------------------------------------- +#define SHADER_VIEWPORT_VERSION 1 +struct ShaderViewport_t +{ + int m_nVersion; + int m_nTopLeftX; + int m_nTopLeftY; + int m_nWidth; + int m_nHeight; + float m_flMinZ; + float m_flMaxZ; + + ShaderViewport_t() : m_nVersion( SHADER_VIEWPORT_VERSION ) {} + + void Init() + { + memset( this, 0, sizeof(ShaderViewport_t) ); + m_nVersion = SHADER_VIEWPORT_VERSION; + } + + void Init( int x, int y, int nWidth, int nHeight, float flMinZ = 0.0f, float flMaxZ = 1.0f ) + { + m_nVersion = SHADER_VIEWPORT_VERSION; + m_nTopLeftX = x; m_nTopLeftY = y; m_nWidth = nWidth; m_nHeight = nHeight; + m_flMinZ = flMinZ; + m_flMaxZ = flMaxZ; + } +}; + + +//----------------------------------------------------------------------------- +// Fill modes +//----------------------------------------------------------------------------- +enum ShaderFillMode_t +{ + SHADER_FILL_SOLID = 0, + SHADER_FILL_WIREFRAME, +}; + + +//----------------------------------------------------------------------------- +// Rasterization state object +//----------------------------------------------------------------------------- +struct ShaderRasterState_t +{ + ShaderFillMode_t m_FillMode; + MaterialCullMode_t m_CullMode; + bool m_bCullEnable : 1; + bool m_bDepthBias : 1; + bool m_bScissorEnable : 1; + bool m_bMultisampleEnable : 1; +}; + + +//----------------------------------------------------------------------------- +// allowed stencil operations. These match the d3d operations +//----------------------------------------------------------------------------- +enum ShaderStencilOp_t +{ +#if !defined( _X360 ) + SHADER_STENCILOP_KEEP = 1, + SHADER_STENCILOP_ZERO = 2, + SHADER_STENCILOP_SET_TO_REFERENCE = 3, + SHADER_STENCILOP_INCREMENT_CLAMP = 4, + SHADER_STENCILOP_DECREMENT_CLAMP = 5, + SHADER_STENCILOP_INVERT = 6, + SHADER_STENCILOP_INCREMENT_WRAP = 7, + SHADER_STENCILOP_DECREMENT_WRAP = 8, +#else + SHADER_STENCILOP_KEEP = D3DSTENCILOP_KEEP, + SHADER_STENCILOP_ZERO = D3DSTENCILOP_ZERO, + SHADER_STENCILOP_SET_TO_REFERENCE = D3DSTENCILOP_REPLACE, + SHADER_STENCILOP_INCREMENT_CLAMP = D3DSTENCILOP_INCRSAT, + SHADER_STENCILOP_DECREMENT_CLAMP = D3DSTENCILOP_DECRSAT, + SHADER_STENCILOP_INVERT = D3DSTENCILOP_INVERT, + SHADER_STENCILOP_INCREMENT_WRAP = D3DSTENCILOP_INCR, + SHADER_STENCILOP_DECREMENT_WRAP = D3DSTENCILOP_DECR, +#endif + SHADER_STENCILOP_FORCE_DWORD = 0x7fffffff +}; + +enum ShaderStencilFunc_t +{ +#if !defined( _X360 ) + SHADER_STENCILFUNC_NEVER = 1, + SHADER_STENCILFUNC_LESS = 2, + SHADER_STENCILFUNC_EQUAL = 3, + SHADER_STENCILFUNC_LEQUAL = 4, + SHADER_STENCILFUNC_GREATER = 5, + SHADER_STENCILFUNC_NOTEQUAL = 6, + SHADER_STENCILFUNC_GEQUAL = 7, + SHADER_STENCILFUNC_ALWAYS = 8, +#else + SHADER_STENCILFUNC_NEVER = D3DCMP_NEVER, + SHADER_STENCILFUNC_LESS = D3DCMP_LESS, + SHADER_STENCILFUNC_EQUAL = D3DCMP_EQUAL, + SHADER_STENCILFUNC_LEQUAL = D3DCMP_LESSEQUAL, + SHADER_STENCILFUNC_GREATER = D3DCMP_GREATER, + SHADER_STENCILFUNC_NOTEQUAL = D3DCMP_NOTEQUAL, + SHADER_STENCILFUNC_GEQUAL = D3DCMP_GREATEREQUAL, + SHADER_STENCILFUNC_ALWAYS = D3DCMP_ALWAYS, +#endif + + SHADER_STENCILFUNC_FORCE_DWORD = 0x7fffffff +}; + +#if defined( _X360 ) +enum ShaderHiStencilFunc_t +{ + SHADER_HI_STENCILFUNC_EQUAL = D3DHSCMP_EQUAL, + SHADER_HI_STENCILFUNC_NOTEQUAL = D3DHSCMP_NOTEQUAL, + + SHADER_HI_STENCILFUNC_FORCE_DWORD = 0x7fffffff +}; +#endif + +//----------------------------------------------------------------------------- +// Stencil state +//----------------------------------------------------------------------------- +struct ShaderStencilState_t +{ + bool m_bEnable; + ShaderStencilOp_t m_FailOp; + ShaderStencilOp_t m_ZFailOp; + ShaderStencilOp_t m_PassOp; + ShaderStencilFunc_t m_CompareFunc; + int m_nReferenceValue; + uint32 m_nTestMask; + uint32 m_nWriteMask; + +#if defined( _X360 ) + bool m_bHiStencilEnable; + bool m_bHiStencilWriteEnable; + ShaderHiStencilFunc_t m_HiStencilCompareFunc; + int m_nHiStencilReferenceValue; +#endif + + ShaderStencilState_t() + { + m_bEnable = false; + m_PassOp = m_FailOp = m_ZFailOp = SHADER_STENCILOP_KEEP; + m_CompareFunc = SHADER_STENCILFUNC_ALWAYS; + m_nReferenceValue = 0; + m_nTestMask = m_nWriteMask = 0xFFFFFFFF; + +#if defined( _X360 ) + m_bHiStencilEnable = false; + m_bHiStencilWriteEnable = false; + m_HiStencilCompareFunc = SHADER_HI_STENCILFUNC_EQUAL; + m_nHiStencilReferenceValue = 0; +#endif + } +}; + + +//----------------------------------------------------------------------------- +// Used for occlusion queries +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( ShaderAPIOcclusionQuery_t ); +#define INVALID_SHADERAPI_OCCLUSION_QUERY_HANDLE ( (ShaderAPIOcclusionQuery_t)0 ) + +enum ShaderAPIOcclusionQueryResult_t +{ + OCCLUSION_QUERY_RESULT_PENDING = -1, + OCCLUSION_QUERY_RESULT_ERROR = -2 +}; +#define OCCLUSION_QUERY_FINISHED( iQueryResult ) ( ( iQueryResult ) != OCCLUSION_QUERY_RESULT_PENDING ) + + +//----------------------------------------------------------------------------- +// This is what the material system gets to see. +//----------------------------------------------------------------------------- +#define SHADERAPI_INTERFACE_VERSION "ShaderApi029" +abstract_class IShaderAPI : public IShaderDynamicAPI +{ +public: + // + // NOTE: These methods have been ported to DX10 + // + + // Viewport methods + virtual void SetViewports( int nCount, const ShaderViewport_t* pViewports ) = 0; + virtual int GetViewports( ShaderViewport_t* pViewports, int nMax ) const = 0; + + // Buffer clearing + virtual void ClearBuffers( bool bClearColor, bool bClearDepth, bool bClearStencil, int renderTargetWidth, int renderTargetHeight ) = 0; + virtual void ClearColor3ub( unsigned char r, unsigned char g, unsigned char b ) = 0; + virtual void ClearColor4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) = 0; + + // Methods related to binding shaders + virtual void BindVertexShader( VertexShaderHandle_t hVertexShader ) = 0; + virtual void BindGeometryShader( GeometryShaderHandle_t hGeometryShader ) = 0; + virtual void BindPixelShader( PixelShaderHandle_t hPixelShader ) = 0; + + // Methods related to state objects + virtual void SetRasterState( const ShaderRasterState_t& state ) = 0; + + // + // NOTE: These methods have not yet been ported to DX10 + // + + // Sets the mode... + virtual bool SetMode( void* hwnd, int nAdapter, const ShaderDeviceInfo_t &info ) = 0; + + virtual void ChangeVideoMode( const ShaderDeviceInfo_t &info ) = 0; + + // Returns the snapshot id for the shader state + virtual StateSnapshot_t TakeSnapshot( ) = 0; + + virtual void TexMinFilter( ShaderTexFilterMode_t texFilterMode ) = 0; + virtual void TexMagFilter( ShaderTexFilterMode_t texFilterMode ) = 0; + virtual void TexWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ) = 0; + + virtual void CopyRenderTargetToTexture( ShaderAPITextureHandle_t textureHandle ) = 0; + + // Binds a particular material to render with + virtual void Bind( IMaterial* pMaterial ) = 0; + + // Flushes any primitives that are buffered + virtual void FlushBufferedPrimitives() = 0; + + // Gets the dynamic mesh; note that you've got to render the mesh + // before calling this function a second time. Clients should *not* + // call DestroyStaticMesh on the mesh returned by this call. + virtual IMesh* GetDynamicMesh( IMaterial* pMaterial, int nHWSkinBoneCount, bool bBuffered = true, + IMesh* pVertexOverride = 0, IMesh* pIndexOverride = 0) = 0; + virtual IMesh* GetDynamicMeshEx( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, + bool bBuffered = true, IMesh* pVertexOverride = 0, IMesh* pIndexOverride = 0 ) = 0; + + // Methods to ask about particular state snapshots + virtual bool IsTranslucent( StateSnapshot_t id ) const = 0; + virtual bool IsAlphaTested( StateSnapshot_t id ) const = 0; + virtual bool UsesVertexAndPixelShaders( StateSnapshot_t id ) const = 0; + virtual bool IsDepthWriteEnabled( StateSnapshot_t id ) const = 0; + + // Gets the vertex format for a set of snapshot ids + virtual VertexFormat_t ComputeVertexFormat( int numSnapshots, StateSnapshot_t* pIds ) const = 0; + + // What fields in the vertex do we actually use? + virtual VertexFormat_t ComputeVertexUsage( int numSnapshots, StateSnapshot_t* pIds ) const = 0; + + // Begins a rendering pass + virtual void BeginPass( StateSnapshot_t snapshot ) = 0; + + // Renders a single pass of a material + virtual void RenderPass( const unsigned char *pInstanceCommandBuffer, int nPass, int nPassCount ) = 0; + + // Set the number of bone weights + virtual void SetNumBoneWeights( int numBones ) = 0; + + // Sets the lights + virtual void SetLights( int nCount, const LightDesc_t *pDesc ) = 0; + + // Lighting origin for the current model + virtual void SetLightingOrigin( Vector vLightingOrigin ) = 0; + + virtual void SetLightingState( const MaterialLightingState_t& state ) = 0; + virtual void SetAmbientLightCube( Vector4D cube[6] ) = 0; + + // The shade mode + virtual void ShadeMode( ShaderShadeMode_t mode ) = 0; + + // The cull mode + virtual void CullMode( MaterialCullMode_t cullMode ) = 0; + virtual void FlipCullMode( void ) = 0; //CW->CCW or CCW->CW, intended for mirror support where the view matrix is flipped horizontally + + // Force writes only when z matches. . . useful for stenciling things out + // by rendering the desired Z values ahead of time. + virtual void ForceDepthFuncEquals( bool bEnable ) = 0; + + // Forces Z buffering to be on or off + virtual void OverrideDepthEnable( bool bEnable, bool bDepthEnable ) = 0; + + virtual void SetHeightClipZ( float z ) = 0; + virtual void SetHeightClipMode( enum MaterialHeightClipMode_t heightClipMode ) = 0; + + virtual void SetClipPlane( int index, const float *pPlane ) = 0; + virtual void EnableClipPlane( int index, bool bEnable ) = 0; + + // Returns the nearest supported format + virtual ImageFormat GetNearestSupportedFormat( ImageFormat fmt ) const = 0; + virtual ImageFormat GetNearestRenderTargetFormat( ImageFormat fmt ) const = 0; + + // When AA is enabled, render targets are not AA and require a separate + // depth buffer. + virtual bool DoRenderTargetsNeedSeparateDepthBuffer() const = 0; + + // Texture management methods + // For CreateTexture also see CreateTextures below + virtual ShaderAPITextureHandle_t CreateTexture( + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ) = 0; + + virtual void DeleteTexture( ShaderAPITextureHandle_t textureHandle ) = 0; + + virtual ShaderAPITextureHandle_t CreateDepthTexture( + ImageFormat renderTargetFormat, + int width, + int height, + const char *pDebugName, + bool bTexture ) = 0; + + virtual bool IsTexture( ShaderAPITextureHandle_t textureHandle ) = 0; + virtual bool IsTextureResident( ShaderAPITextureHandle_t textureHandle ) = 0; + + // Indicates we're going to be modifying this texture + // TexImage2D, TexSubImage2D, TexWrap, TexMinFilter, and TexMagFilter + // all use the texture specified by this function. + virtual void ModifyTexture( ShaderAPITextureHandle_t textureHandle ) = 0; + + virtual void TexImage2D( + int level, + int cubeFaceID, + ImageFormat dstFormat, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + bool bSrcIsTiled, // NOTE: for X360 only + void *imageData ) = 0; + + virtual void TexSubImage2D( + int level, + int cubeFaceID, + int xOffset, + int yOffset, + int zOffset, + int width, + int height, + ImageFormat srcFormat, + int srcStride, + bool bSrcIsTiled, // NOTE: for X360 only + void *imageData ) = 0; + + // An alternate (and faster) way of writing image data + // (locks the current Modify Texture). Use the pixel writer to write the data + // after Lock is called + // Doesn't work for compressed textures + virtual bool TexLock( int level, int cubeFaceID, int xOffset, int yOffset, + int width, int height, CPixelWriter& writer ) = 0; + virtual void TexUnlock( ) = 0; + + // Copy sysmem surface to default pool surface asynchronously + virtual void UpdateTexture( int xOffset, int yOffset, int w, int h, ShaderAPITextureHandle_t hDstTexture, ShaderAPITextureHandle_t hSrcTexture ) = 0; + virtual void *LockTex( ShaderAPITextureHandle_t hTexture ) = 0; + virtual void UnlockTex( ShaderAPITextureHandle_t hTexture ) = 0; + + // These are bound to the texture + virtual void TexSetPriority( int priority ) = 0; + + // Sets the texture state + virtual void BindTexture( Sampler_t sampler, ShaderAPITextureHandle_t textureHandle ) = 0; + + // Set the render target to a texID. + // Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer. + // Set to SHADER_RENDERTARGET_DEPTHBUFFER if you want to use the regular z buffer. + virtual void SetRenderTarget( ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER, + ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER ) = 0; + + // stuff that isn't to be used from within a shader + virtual void ClearBuffersObeyStencil( bool bClearColor, bool bClearDepth ) = 0; + virtual void ReadPixels( int x, int y, int width, int height, unsigned char *data, ImageFormat dstFormat ) = 0; + virtual void ReadPixels( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *data, ImageFormat dstFormat, int nDstStride ) = 0; + + virtual void FlushHardware() = 0; + + // Use this to begin and end the frame + virtual void BeginFrame() = 0; + virtual void EndFrame() = 0; + + // Selection mode methods + virtual int SelectionMode( bool selectionMode ) = 0; + virtual void SelectionBuffer( unsigned int* pBuffer, int size ) = 0; + virtual void ClearSelectionNames( ) = 0; + virtual void LoadSelectionName( int name ) = 0; + virtual void PushSelectionName( int name ) = 0; + virtual void PopSelectionName() = 0; + + // Force the hardware to finish whatever it's doing + virtual void ForceHardwareSync() = 0; + + // Used to clear the transition table when we know it's become invalid. + virtual void ClearSnapshots() = 0; + + virtual void FogStart( float fStart ) = 0; + virtual void FogEnd( float fEnd ) = 0; + virtual void SetFogZ( float fogZ ) = 0; + // Scene fog state. + virtual void SceneFogColor3ub( unsigned char r, unsigned char g, unsigned char b ) = 0; + virtual void GetSceneFogColor( unsigned char *rgb ) = 0; + virtual void SceneFogMode( MaterialFogMode_t fogMode ) = 0; + + // Can we download textures? + virtual bool CanDownloadTextures() const = 0; + + virtual void ResetRenderState( bool bFullReset = true ) = 0; + + // We use smaller dynamic VBs during level transitions, to free up memory + virtual int GetCurrentDynamicVBSize( void ) = 0; + virtual void DestroyVertexBuffers( bool bExitingLevel = false ) = 0; + + virtual void EvictManagedResources() = 0; + + // Level of anisotropic filtering + virtual void SetAnisotropicLevel( int nAnisotropyLevel ) = 0; + + // For debugging and building recording files. This will stuff a token into the recording file, + // then someone doing a playback can watch for the token. + virtual void SyncToken( const char *pToken ) = 0; + + // Setup standard vertex shader constants (that don't change) + // This needs to be called anytime that overbright changes. + virtual void SetStandardVertexShaderConstants( float fOverbright ) = 0; + + // + // Occlusion query support + // + + // Allocate and delete query objects. + virtual ShaderAPIOcclusionQuery_t CreateOcclusionQueryObject( void ) = 0; + virtual void DestroyOcclusionQueryObject( ShaderAPIOcclusionQuery_t ) = 0; + + // Bracket drawing with begin and end so that we can get counts next frame. + virtual void BeginOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t ) = 0; + virtual void EndOcclusionQueryDrawing( ShaderAPIOcclusionQuery_t ) = 0; + + // OcclusionQuery_GetNumPixelsRendered + // Get the number of pixels rendered between begin and end on an earlier frame. + // Calling this in the same frame is a huge perf hit! + // Returns iQueryResult: + // iQueryResult >= 0 - iQueryResult is the number of pixels rendered + // OCCLUSION_QUERY_RESULT_PENDING - query results are not available yet + // OCCLUSION_QUERY_RESULT_ERROR - query failed + // Use OCCLUSION_QUERY_FINISHED( iQueryResult ) to test if query finished. + virtual int OcclusionQuery_GetNumPixelsRendered( ShaderAPIOcclusionQuery_t hQuery, bool bFlush = false ) = 0; + + virtual void SetFlashlightState( const FlashlightState_t &state, const VMatrix &worldToTexture ) = 0; + + virtual void ClearVertexAndPixelShaderRefCounts() = 0; + virtual void PurgeUnusedVertexAndPixelShaders() = 0; + + // Called when the dx support level has changed + virtual void DXSupportLevelChanged( int nDXLevel ) = 0; + + // By default, the material system applies the VIEW and PROJECTION matrices to the user clip + // planes (which are specified in world space) to generate projection-space user clip planes + // Occasionally (for the particle system in hl2, for example), we want to override that + // behavior and explictly specify a View transform for user clip planes. The PROJECTION + // will be mutliplied against this instead of the normal VIEW matrix. + virtual void EnableUserClipTransformOverride( bool bEnable ) = 0; + virtual void UserClipTransform( const VMatrix &worldToView ) = 0; + + // ---------------------------------------------------------------------------------- + // Everything after this point added after HL2 shipped. + // ---------------------------------------------------------------------------------- + + // Set the render target to a texID. + // Set to SHADER_RENDERTARGET_BACKBUFFER if you want to use the regular framebuffer. + // Set to SHADER_RENDERTARGET_DEPTHBUFFER if you want to use the regular z buffer. + virtual void SetRenderTargetEx( int nRenderTargetID, + ShaderAPITextureHandle_t colorTextureHandle = SHADER_RENDERTARGET_BACKBUFFER, + ShaderAPITextureHandle_t depthTextureHandle = SHADER_RENDERTARGET_DEPTHBUFFER ) = 0; + + virtual void CopyRenderTargetToTextureEx( ShaderAPITextureHandle_t textureHandle, int nRenderTargetID, Rect_t *pSrcRect = NULL, Rect_t *pDstRect = NULL ) = 0; + + // For dealing with device lost in cases where SwapBuffers isn't called all the time (Hammer) + virtual void HandleDeviceLost() = 0; + + virtual void EnableLinearColorSpaceFrameBuffer( bool bEnable ) = 0; + + // Lets the shader know about the full-screen texture so it can + virtual void SetFullScreenTextureHandle( ShaderAPITextureHandle_t h ) = 0; + + // Sets the ambient light color + virtual void SetAmbientLightColor( float r, float g, float b ) = 0; + + // Rendering parameters control special drawing modes withing the material system, shader + // system, shaders, and engine. renderparm.h has their definitions. + virtual void SetFloatRenderingParameter(int parm_number, float value) = 0; + virtual void SetIntRenderingParameter(int parm_number, int value) = 0; + virtual void SetVectorRenderingParameter(int parm_number, Vector const &value) = 0; + + virtual float GetFloatRenderingParameter(int parm_number) const = 0; + virtual int GetIntRenderingParameter(int parm_number) const = 0; + virtual Vector GetVectorRenderingParameter(int parm_number) const = 0; + + virtual void SetFastClipPlane( const float *pPlane ) = 0; + virtual void EnableFastClip( bool bEnable ) = 0; + + // Returns the number of vertices + indices we can render using the dynamic mesh + // Passing true in the second parameter will return the max # of vertices + indices + // we can use before a flush is provoked and may return different values + // if called multiple times in succession. + // Passing false into the second parameter will return + // the maximum possible vertices + indices that can be rendered in a single batch + virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) = 0; + + // Returns the max number of vertices we can render for a given material + virtual int GetMaxVerticesToRender( IMaterial *pMaterial ) = 0; + virtual int GetMaxIndicesToRender( ) = 0; + + // stencil methods + virtual void SetStencilState( const ShaderStencilState_t& state ) = 0; + virtual void ClearStencilBufferRectangle(int xmin, int ymin, int xmax, int ymax, int value) = 0; + + // disables all local lights + virtual void DisableAllLocalLights() = 0; + virtual int CompareSnapshots( StateSnapshot_t snapshot0, StateSnapshot_t snapshot1 ) = 0; + + virtual IMesh *GetFlexMesh() = 0; + + virtual void SetFlashlightStateEx( const FlashlightState_t &state, const VMatrix &worldToTexture, ITexture *pFlashlightDepthTexture ) = 0; + + virtual bool SupportsMSAAMode( int nMSAAMode ) = 0; + +#if defined( _X360 ) + virtual HXUIFONT OpenTrueTypeFont( const char *pFontname, int tall, int style ) = 0; + virtual void CloseTrueTypeFont( HXUIFONT hFont ) = 0; + virtual bool GetTrueTypeFontMetrics( HXUIFONT hFont, wchar_t wchFirst, wchar_t wchLast, XUIFontMetrics *pFontMetrics, XUICharMetrics *pCharMetrics ) = 0; + // Render a sequence of characters and extract the data into a buffer + // For each character, provide the width+height of the font texture subrect, + // an offset to apply when rendering the glyph, and an offset into a buffer to receive the RGBA data + virtual bool GetTrueTypeGlyphs( HXUIFONT hFont, int numChars, wchar_t *pWch, int *pOffsetX, int *pOffsetY, int *pWidth, int *pHeight, unsigned char *pRGBA, int *pRGBAOffset ) = 0; + virtual ShaderAPITextureHandle_t CreateRenderTargetSurface( int width, int height, ImageFormat format, RTMultiSampleCount360_t multiSampleCount, const char *pDebugName, const char *pTextureGroupName ) = 0; + virtual void PersistDisplay() = 0; + virtual bool PostQueuedTexture( const void *pData, int nSize, ShaderAPITextureHandle_t *pHandles, int nHandles, int nWidth, int nHeight, int nDepth, int nMips, int *pRefCount ) = 0; + virtual void *GetD3DDevice() = 0; + + virtual void PushVertexShaderGPRAllocation( int iVertexShaderCount = 64 ) = 0; + virtual void PopVertexShaderGPRAllocation( void ) = 0; + + // 360 allows us to bypass vsync blocking up to 60 fps without creating a new device + virtual void EnableVSync_360( bool bEnable ) = 0; + + virtual void SetCacheableTextureParams( ShaderAPITextureHandle_t *pHandles, int count, const char *pFilename, int mipSkipCount ) = 0; + virtual void FlushHiStencil() = 0; + + virtual void Begin360ZPass( int nNumDynamicIndicesNeeded ) = 0; + virtual void End360ZPass() = 0; + virtual unsigned int Get360ZPassCounter() const = 0; +#endif + + virtual bool OwnGPUResources( bool bEnable ) = 0; + + //get fog distances entered with FogStart(), FogEnd(), and SetFogZ() + virtual void GetFogDistances( float *fStart, float *fEnd, float *fFogZ ) = 0; + + // Hooks for firing PIX events from outside the Material System... + virtual void BeginPIXEvent( unsigned long color, const char *szName ) = 0; + virtual void EndPIXEvent() = 0; + virtual void SetPIXMarker( unsigned long color, const char *szName ) = 0; + + // Enables and disables for Alpha To Coverage + virtual void EnableAlphaToCoverage() = 0; + virtual void DisableAlphaToCoverage() = 0; + + // Computes the vertex buffer pointers + virtual void ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const = 0; + + virtual void SetDisallowAccess( bool ) = 0; + virtual void EnableShaderShaderMutex( bool ) = 0; + virtual void ShaderLock() = 0; + virtual void ShaderUnlock() = 0; + + virtual void SetShadowDepthBiasFactors( float fShadowSlopeScaleDepthBias, float fShadowDepthBias ) = 0; + +// ------------ New Vertex/Index Buffer interface ---------------------------- + virtual void BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ) = 0; + virtual void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) = 0; + virtual void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) = 0; +// ------------ End ---------------------------- + + + // Apply stencil operations to every pixel on the screen without disturbing depth or color buffers + virtual void PerformFullScreenStencilOperation( void ) = 0; + + virtual void SetScissorRect( const int nLeft, const int nTop, const int nRight, const int nBottom, const bool bEnableScissor ) = 0; + + // nVidia CSAA modes, different from SupportsMSAAMode() + virtual bool SupportsCSAAMode( int nNumSamples, int nQualityLevel ) = 0; + + //Notifies the shaderapi to invalidate the current set of delayed constants because we just finished a draw pass. Either actual or not. + virtual void InvalidateDelayedShaderConstants( void ) = 0; + + // Gamma<->Linear conversions according to the video hardware we're running on + virtual float GammaToLinear_HardwareSpecific( float fGamma ) const =0; + virtual float LinearToGamma_HardwareSpecific( float fLinear ) const =0; + + //Set's the linear->gamma conversion textures to use for this hardware for both srgb writes enabled and disabled(identity) + virtual void SetLinearToGammaConversionTextures( ShaderAPITextureHandle_t hSRGBWriteEnabledTexture, ShaderAPITextureHandle_t hIdentityTexture ) = 0; + + virtual void BindVertexTexture( VertexTextureSampler_t nSampler, ShaderAPITextureHandle_t textureHandle ) = 0; + + // Enables hardware morphing + virtual void EnableHWMorphing( bool bEnable ) = 0; + + // Sets flexweights for rendering + virtual void SetFlexWeights( int nFirstWeight, int nCount, const MorphWeight_t* pWeights ) = 0; + + virtual void FogMaxDensity( float flMaxDensity ) = 0; + + // Create a multi-frame texture (equivalent to calling "CreateTexture" multiple times, but more efficient) + virtual void CreateTextures( + ShaderAPITextureHandle_t *pHandles, + int count, + int width, + int height, + int depth, + ImageFormat dstImageFormat, + int numMipLevels, + int numCopies, + int flags, + const char *pDebugName, + const char *pTextureGroupName ) = 0; + + virtual void AcquireThreadOwnership() = 0; + virtual void ReleaseThreadOwnership() = 0; + + // Only does anything on XBox360. This is useful to eliminate stalls + virtual void EnableBuffer2FramesAhead( bool bEnable ) = 0; + + virtual void FlipCulling( bool bFlipCulling ) = 0; + + virtual void SetTextureRenderingParameter(int parm_number, ITexture *pTexture) = 0; + + //only actually sets a bool that can be read in from shaders, doesn't do any of the legwork + virtual void EnableSinglePassFlashlightMode( bool bEnable ) = 0; + + // stuff related to matrix stacks + virtual void MatrixMode( MaterialMatrixMode_t matrixMode ) = 0; + virtual void PushMatrix() = 0; + virtual void PopMatrix() = 0; + virtual void LoadMatrix( float *m ) = 0; + virtual void MultMatrix( float *m ) = 0; + virtual void MultMatrixLocal( float *m ) = 0; + virtual void LoadIdentity( void ) = 0; + virtual void LoadCameraToWorld( void ) = 0; + virtual void Ortho( double left, double right, double bottom, double top, double zNear, double zFar ) = 0; + virtual void PerspectiveX( double fovx, double aspect, double zNear, double zFar ) = 0; + virtual void PickMatrix( int x, int y, int width, int height ) = 0; + virtual void Rotate( float angle, float x, float y, float z ) = 0; + virtual void Translate( float x, float y, float z ) = 0; + virtual void Scale( float x, float y, float z ) = 0; + virtual void ScaleXY( float x, float y ) = 0; + virtual void PerspectiveOffCenterX( double fovx, double aspect, double zNear, double zFar, double bottom, double top, double left, double right ) = 0; + + virtual void LoadBoneMatrix( int boneIndex, const float *m ) = 0; + + // interface for mat system to tell shaderapi about standard texture handles + virtual void SetStandardTextureHandle( StandardTextureId_t nId, ShaderAPITextureHandle_t nHandle ) =0; + + virtual void DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstance ) = 0; + + // Allows us to override the color/alpha write settings of a material + virtual void OverrideAlphaWriteEnable( bool bOverrideEnable, bool bAlphaWriteEnable ) = 0; + virtual void OverrideColorWriteEnable( bool bOverrideEnable, bool bColorWriteEnable ) = 0; + + //extended clear buffers function with alpha independent from color + virtual void ClearBuffersObeyStencilEx( bool bClearColor, bool bClearAlpha, bool bClearDepth ) = 0; + + virtual void OnPresent( void ) = 0; + + virtual void UpdateGameTime( float flTime ) = 0; +}; + + +#endif // ISHADERAPI_H diff --git a/public/shaderapi/ishaderdynamic.h b/public/shaderapi/ishaderdynamic.h new file mode 100644 index 0000000..962acfc --- /dev/null +++ b/public/shaderapi/ishaderdynamic.h @@ -0,0 +1,291 @@ +//===== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERDYNAMIC_H +#define ISHADERDYNAMIC_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "shaderapi/shareddefs.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialsystem.h" + + +typedef int ShaderAPITextureHandle_t; +#define INVALID_SHADERAPI_TEXTURE_HANDLE 0 + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class CMeshBuilder; +class IMaterialVar; +struct LightDesc_t; + + +//----------------------------------------------------------------------------- +// State from ShaderAPI used to select proper vertex and pixel shader combos +//----------------------------------------------------------------------------- +struct LightState_t +{ + int m_nNumLights; + bool m_bAmbientLight; + bool m_bStaticLight; + inline int HasDynamicLight() { return (m_bAmbientLight || (m_nNumLights > 0)) ? 1 : 0; } +}; + + +//----------------------------------------------------------------------------- +// Color correction info +//----------------------------------------------------------------------------- +struct ShaderColorCorrectionInfo_t +{ + bool m_bIsEnabled; + int m_nLookupCount; + float m_flDefaultWeight; + float m_pLookupWeights[4]; +}; + + +//----------------------------------------------------------------------------- +// the 3D shader API interface +// This interface is all that shaders see. +//----------------------------------------------------------------------------- +enum StandardTextureId_t +{ + // Lightmaps + TEXTURE_LIGHTMAP = 0, + TEXTURE_LIGHTMAP_FULLBRIGHT, + TEXTURE_LIGHTMAP_BUMPED, + TEXTURE_LIGHTMAP_BUMPED_FULLBRIGHT, + + // Flat colors + TEXTURE_WHITE, + TEXTURE_BLACK, + TEXTURE_BLACK_ALPHA_ZERO, + TEXTURE_GREY, + TEXTURE_GREY_ALPHA_ZERO, + + // Normalmaps + TEXTURE_NORMALMAP_FLAT, + TEXTURE_SSBUMP_FLAT, + + // Normalization + TEXTURE_NORMALIZATION_CUBEMAP, + TEXTURE_NORMALIZATION_CUBEMAP_SIGNED, + + // Frame-buffer textures + TEXTURE_FRAME_BUFFER_FULL_TEXTURE_0, + TEXTURE_FRAME_BUFFER_FULL_TEXTURE_1, + + // Color correction + TEXTURE_COLOR_CORRECTION_VOLUME_0, + TEXTURE_COLOR_CORRECTION_VOLUME_1, + TEXTURE_COLOR_CORRECTION_VOLUME_2, + TEXTURE_COLOR_CORRECTION_VOLUME_3, + + // An alias to the Back Frame Buffer + TEXTURE_FRAME_BUFFER_ALIAS, + + // Noise for shadow mapping algorithm + TEXTURE_SHADOW_NOISE_2D, + + // A texture in which morph data gets accumulated (vs30, fast vertex textures required) + TEXTURE_MORPH_ACCUMULATOR, + + // A texture which contains morph weights + TEXTURE_MORPH_WEIGHTS, + + // A snapshot of the frame buffer's depth. Currently only valid on the 360 + TEXTURE_FRAME_BUFFER_FULL_DEPTH, + + // A snapshot of the frame buffer's depth. Currently only valid on the 360 + TEXTURE_IDENTITY_LIGHTWARP, + + // The current local env_cubemap + TEXTURE_LOCAL_ENV_CUBEMAP, + + // Texture containing subdivision surface patch data + TEXTURE_SUBDIVISION_PATCHES, + + // Screen-space texture which contains random 3D reflection vectors used in SSAO algorithm + TEXTURE_SSAO_NOISE_2D, + + TEXTURE_PAINT, + + TEXTURE_MAX_STD_TEXTURES = TEXTURE_SSAO_NOISE_2D + 1 +}; + +enum TextureFilterMode_t +{ + TFILTER_MODE_POINTSAMPLED = 1, +}; + +enum TessellationMode_t; + +//----------------------------------------------------------------------------- +// The Shader interface versions +//----------------------------------------------------------------------------- +#define SHADERDYNAMIC_INTERFACE_VERSION "ShaderDynamic001" +abstract_class IShaderDynamicAPI +{ +public: + // returns the current time in seconds.... + virtual double CurrentTime() const = 0; + + // Gets the lightmap dimensions + virtual void GetLightmapDimensions( int *w, int *h ) = 0; + + // Scene fog state. + // This is used by the shaders for picking the proper vertex shader for fogging based on dynamic state. + virtual MaterialFogMode_t GetSceneFogMode( ) = 0; + virtual void GetSceneFogColor( unsigned char *rgb ) = 0; + + // Sets the constant register for vertex and pixel shaders + virtual void SetVertexShaderConstant( int var, float const* pVec, int numConst = 1, bool bForce = false ) = 0; + virtual void SetPixelShaderConstant( int var, float const* pVec, int numConst = 1, bool bForce = false ) = 0; + + // Sets the default *dynamic* state + virtual void SetDefaultState() = 0; + + // Get the current camera position in world space. + virtual void GetWorldSpaceCameraPosition( float* pPos ) const = 0; + virtual void GetWorldSpaceCameraDirection( float* pDir ) const = 0; + + virtual int GetCurrentNumBones( void ) const = 0; + + virtual MaterialFogMode_t GetCurrentFogType( void ) const = 0; + + // Sets the vertex and pixel shaders + virtual void SetVertexShaderIndex( int vshIndex = -1 ) = 0; + virtual void SetPixelShaderIndex( int pshIndex = 0 ) = 0; + + // Get the dimensions of the back buffer. + virtual void GetBackBufferDimensions( int& width, int& height ) const = 0; + + // Get the dimensions of the current render target + virtual void GetCurrentRenderTargetDimensions( int& nWidth, int& nHeight ) const = 0; + + // Get the current viewport + virtual void GetCurrentViewport( int& nX, int& nY, int& nWidth, int& nHeight ) const = 0; + + // FIXME: The following 6 methods used to live in IShaderAPI + // and were moved for stdshader_dx8. Let's try to move them back! + + virtual void SetPixelShaderFogParams( int reg ) = 0; + + // Use this to get the mesh builder that allows us to modify vertex data + virtual bool InFlashlightMode() const = 0; + virtual const FlashlightState_t &GetFlashlightState( VMatrix &worldToTexture ) const = 0; + virtual bool InEditorMode() const = 0; + + // Binds a standard texture + virtual void BindStandardTexture( Sampler_t sampler, StandardTextureId_t id ) = 0; + + virtual ITexture *GetRenderTargetEx( int nRenderTargetID ) const = 0; + + virtual void SetToneMappingScaleLinear( const Vector &scale ) = 0; + virtual const Vector &GetToneMappingScaleLinear( void ) const = 0; + + // Sets the ambient light color + virtual void SetAmbientLightColor( float r, float g, float b ) = 0; + + virtual void SetFloatRenderingParameter(int parm_number, float value) = 0; + virtual void SetIntRenderingParameter(int parm_number, int value) = 0 ; + virtual void SetVectorRenderingParameter(int parm_number, Vector const &value) = 0 ; + + virtual float GetFloatRenderingParameter(int parm_number) const = 0 ; + + virtual int GetIntRenderingParameter(int parm_number) const = 0 ; + + virtual Vector GetVectorRenderingParameter(int parm_number) const = 0 ; + + virtual const FlashlightState_t &GetFlashlightStateEx( VMatrix &worldToTexture, ITexture **pFlashlightDepthTexture ) const = 0; + + virtual void GetDX9LightState( LightState_t *state ) const = 0; + virtual int GetPixelFogCombo( ) = 0; //0 is either range fog, or no fog simulated with rigged range fog values. 1 is height fog + + virtual void BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id ) = 0; + + // Is hardware morphing enabled? + virtual bool IsHWMorphingEnabled( ) const = 0; + + virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) = 0; + + virtual void SetBooleanVertexShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ) = 0; + virtual void SetIntegerVertexShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ) = 0; + virtual void SetBooleanPixelShaderConstant( int var, BOOL const* pVec, int numBools = 1, bool bForce = false ) = 0; + virtual void SetIntegerPixelShaderConstant( int var, int const* pVec, int numIntVecs = 1, bool bForce = false ) = 0; + + //Are we in a configuration that needs access to depth data through the alpha channel later? + virtual bool ShouldWriteDepthToDestAlpha( void ) const = 0; + + // Returns current matrices + virtual void GetMatrix( MaterialMatrixMode_t matrixMode, float *dst ) = 0; + + // deformations + virtual void PushDeformation( DeformationBase_t const *Deformation ) = 0; + virtual void PopDeformation( ) = 0; + virtual int GetNumActiveDeformations() const =0; + + + // for shaders to set vertex shader constants. returns a packed state which can be used to set + // the dynamic combo. returns # of active deformations + virtual int GetPackedDeformationInformation( int nMaskOfUnderstoodDeformations, + float *pConstantValuesOut, + int nBufferSize, + int nMaximumDeformations, + int *pNumDefsOut ) const = 0; + + // This lets the lower level system that certain vertex fields requested + // in the shadow state aren't actually being read given particular state + // known only at dynamic state time. It's here only to silence warnings. + virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) = 0; + + + virtual void ExecuteCommandBuffer( uint8 *pCmdBuffer ) =0; + + // Interface for mat system to tell shaderapi about color correction + virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ) = 0; + + virtual ITexture *GetTextureRenderingParameter(int parm_number) const = 0; + + virtual void SetScreenSizeForVPOS( int pshReg = 32 ) = 0; + virtual void SetVSNearAndFarZ( int vshReg ) = 0; + virtual float GetFarZ() = 0; + + virtual bool SinglePassFlashlightModeEnabled( void ) = 0; + + virtual void SetDepthFeatheringPixelShaderConstant( int iConstant, float fDepthBlendScale ) = 0; + + virtual void GetFlashlightShaderInfo( bool *pShadowsEnabled, bool *pUberLight ) const = 0; + + virtual float GetFlashlightAmbientOcclusion( ) const = 0; + + // allows overriding texture filtering mode on an already bound texture. + virtual void SetTextureFilterMode( Sampler_t sampler, TextureFilterMode_t nMode ) = 0; + + virtual TessellationMode_t GetTessellationMode() const = 0; + + virtual float GetSubDHeight() = 0; + +#ifdef _X360 + // Enables X360-specific command predication. + // Set values to 'true' if batches should be rendered in the z-pass and/or the render pass. + // Disabling predication returns to default values, which allows D3D to control predication + virtual void EnablePredication( bool bZPass, bool bRenderPass ) = 0; + virtual void DisablePredication() = 0; +#endif // _X360 +}; + +// end class IShaderDynamicAPI + + +#endif // ISHADERDYNAMIC_H diff --git a/public/shaderapi/ishadershadow.h b/public/shaderapi/ishadershadow.h new file mode 100644 index 0000000..1539c1e --- /dev/null +++ b/public/shaderapi/ishadershadow.h @@ -0,0 +1,207 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERSHADOW_H +#define ISHADERSHADOW_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "shaderapi/shareddefs.h" +#include + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class CMeshBuilder; +class IMaterialVar; +struct LightDesc_t; + + +//----------------------------------------------------------------------------- +// important enumerations +//----------------------------------------------------------------------------- +enum ShaderDepthFunc_t +{ + SHADER_DEPTHFUNC_NEVER, + SHADER_DEPTHFUNC_NEARER, + SHADER_DEPTHFUNC_EQUAL, + SHADER_DEPTHFUNC_NEAREROREQUAL, + SHADER_DEPTHFUNC_FARTHER, + SHADER_DEPTHFUNC_NOTEQUAL, + SHADER_DEPTHFUNC_FARTHEROREQUAL, + SHADER_DEPTHFUNC_ALWAYS +}; + +enum ShaderBlendFactor_t +{ + SHADER_BLEND_ZERO, + SHADER_BLEND_ONE, + SHADER_BLEND_DST_COLOR, + SHADER_BLEND_ONE_MINUS_DST_COLOR, + SHADER_BLEND_SRC_ALPHA, + SHADER_BLEND_ONE_MINUS_SRC_ALPHA, + SHADER_BLEND_DST_ALPHA, + SHADER_BLEND_ONE_MINUS_DST_ALPHA, + SHADER_BLEND_SRC_ALPHA_SATURATE, + SHADER_BLEND_SRC_COLOR, + SHADER_BLEND_ONE_MINUS_SRC_COLOR +}; + +enum ShaderBlendOp_t +{ + SHADER_BLEND_OP_ADD, + SHADER_BLEND_OP_SUBTRACT, + SHADER_BLEND_OP_REVSUBTRACT, + SHADER_BLEND_OP_MIN, + SHADER_BLEND_OP_MAX +}; + +enum ShaderAlphaFunc_t +{ + SHADER_ALPHAFUNC_NEVER, + SHADER_ALPHAFUNC_LESS, + SHADER_ALPHAFUNC_EQUAL, + SHADER_ALPHAFUNC_LEQUAL, + SHADER_ALPHAFUNC_GREATER, + SHADER_ALPHAFUNC_NOTEQUAL, + SHADER_ALPHAFUNC_GEQUAL, + SHADER_ALPHAFUNC_ALWAYS +}; + +enum ShaderTexChannel_t +{ + SHADER_TEXCHANNEL_COLOR = 0, + SHADER_TEXCHANNEL_ALPHA +}; + +enum ShaderPolyModeFace_t +{ + SHADER_POLYMODEFACE_FRONT, + SHADER_POLYMODEFACE_BACK, + SHADER_POLYMODEFACE_FRONT_AND_BACK, +}; + +enum ShaderPolyMode_t +{ + SHADER_POLYMODE_POINT, + SHADER_POLYMODE_LINE, + SHADER_POLYMODE_FILL +}; + +enum ShaderFogMode_t +{ + SHADER_FOGMODE_DISABLED = 0, + SHADER_FOGMODE_OO_OVERBRIGHT, + SHADER_FOGMODE_BLACK, + SHADER_FOGMODE_GREY, + SHADER_FOGMODE_FOGCOLOR, + SHADER_FOGMODE_WHITE, + SHADER_FOGMODE_NUMFOGMODES +}; + + + +// m_ZBias has only two bits in ShadowState_t, so be careful extending this enum +enum PolygonOffsetMode_t +{ + SHADER_POLYOFFSET_DISABLE = 0x0, + SHADER_POLYOFFSET_DECAL = 0x1, + SHADER_POLYOFFSET_SHADOW_BIAS = 0x2, + SHADER_POLYOFFSET_RESERVED = 0x3 // Reserved for future use +}; + + +//----------------------------------------------------------------------------- +// The Shader interface versions +//----------------------------------------------------------------------------- +#define SHADERSHADOW_INTERFACE_VERSION "ShaderShadow010" + + +//----------------------------------------------------------------------------- +// the shader API interface (methods called from shaders) +//----------------------------------------------------------------------------- +abstract_class IShaderShadow +{ +public: + // Sets the default *shadow* state + virtual void SetDefaultState() = 0; + + // Methods related to depth buffering + virtual void DepthFunc( ShaderDepthFunc_t depthFunc ) = 0; + virtual void EnableDepthWrites( bool bEnable ) = 0; + virtual void EnableDepthTest( bool bEnable ) = 0; + virtual void EnablePolyOffset( PolygonOffsetMode_t nOffsetMode ) = 0; + + // Suppresses/activates color writing + virtual void EnableColorWrites( bool bEnable ) = 0; + virtual void EnableAlphaWrites( bool bEnable ) = 0; + + // Methods related to alpha blending + virtual void EnableBlending( bool bEnable ) = 0; + virtual void BlendFunc( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) = 0; + virtual void EnableBlendingSeparateAlpha( bool bEnable ) = 0; + virtual void BlendFuncSeparateAlpha( ShaderBlendFactor_t srcFactor, ShaderBlendFactor_t dstFactor ) = 0; + // More below... + + // Alpha testing + virtual void EnableAlphaTest( bool bEnable ) = 0; + virtual void AlphaFunc( ShaderAlphaFunc_t alphaFunc, float alphaRef /* [0-1] */ ) = 0; + + // Wireframe/filled polygons + virtual void PolyMode( ShaderPolyModeFace_t face, ShaderPolyMode_t polyMode ) = 0; + + // Back face culling + virtual void EnableCulling( bool bEnable ) = 0; + + // Indicates the vertex format for use with a vertex shader + // The flags to pass in here come from the VertexFormatFlags_t enum + // If pTexCoordDimensions is *not* specified, we assume all coordinates + // are 2-dimensional + virtual void VertexShaderVertexFormat( unsigned int nFlags, + int nTexCoordCount, int* pTexCoordDimensions, int nUserDataSize ) = 0; + + // Pixel and vertex shader methods + virtual void SetVertexShader( const char* pFileName, int nStaticVshIndex ) = 0; + virtual void SetPixelShader( const char* pFileName, int nStaticPshIndex = 0 ) = 0; + + // Convert from linear to gamma color space on writes to frame buffer. + virtual void EnableSRGBWrite( bool bEnable ) = 0; + + // Convert from gamma to linear on texture fetch. + virtual void EnableSRGBRead( Sampler_t sampler, bool bEnable ) = 0; + + // Per texture unit stuff + virtual void EnableTexture( Sampler_t sampler, bool bEnable ) = 0; + + virtual void FogMode( ShaderFogMode_t fogMode, bool bVertexFog ) = 0; + + virtual void DisableFogGammaCorrection( bool bDisable ) = 0; //some blending modes won't work properly with corrected fog + + // Alpha to coverage + virtual void EnableAlphaToCoverage( bool bEnable ) = 0; + + // Shadow map filtering + virtual void SetShadowDepthFiltering( Sampler_t stage ) = 0; + + // Per vertex texture unit stuff + virtual void EnableVertexTexture( VertexTextureSampler_t sampler, bool bEnable ) = 0; + + // More alpha blending state + virtual void BlendOp( ShaderBlendOp_t blendOp ) = 0; + virtual void BlendOpSeparateAlpha( ShaderBlendOp_t blendOp ) = 0; + + virtual float GetLightMapScaleFactor( void ) const = 0; +}; +// end class IShaderShadow + + + +#endif // ISHADERSHADOW_H diff --git a/public/shaderapi/ishaderutil.h b/public/shaderapi/ishaderutil.h new file mode 100644 index 0000000..92da39f --- /dev/null +++ b/public/shaderapi/ishaderutil.h @@ -0,0 +1,136 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef ISHADERUTIL_H +#define ISHADERUTIL_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "materialsystem/imaterial.h" +#include "appframework/IAppSystem.h" +#include "shaderapi/ishaderapi.h" + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class ITexture; +struct MaterialSystem_Config_t; +struct ImageFormatInfo_t; +enum Sampler_t; +enum VertexTextureSampler_t; +enum StandardTextureId_t; +class CPrimList; +struct ShaderColorCorrectionInfo_t; + +#define SHADER_UTIL_INTERFACE_VERSION "VShaderUtil001" + +enum shaderthreadevent_t +{ + SHADER_THREAD_RELEASE_RESOURCES = 1, + SHADER_THREAD_ACQUIRE_RESOURCES = 2, + SHADER_THREAD_DEVICE_LOST = 3, + SHADER_THREAD_EVICT_RESOURCES = 4, + SHADER_THREAD_OTHER_APP_START = 5, + SHADER_THREAD_OTHER_APP_END = 6, + SHADER_THREAD_RESET_RENDER_STATE = 7, +}; + +abstract_class IShaderUtil : public IAppSystem +{ +public: + // Method to allow clients access to the MaterialSystem_Config + virtual MaterialSystem_Config_t& GetConfig() = 0; + + // Allows us to convert image formats + virtual bool ConvertImageFormat( unsigned char *src, enum ImageFormat srcImageFormat, + unsigned char *dst, enum ImageFormat dstImageFormat, + int width, int height, int srcStride = 0, int dstStride = 0 ) = 0; + + // Figures out the amount of memory needed by a bitmap + virtual int GetMemRequired( int width, int height, int depth, ImageFormat format, bool mipmap ) = 0; + + // Gets image format info + virtual const ImageFormatInfo_t& ImageFormatInfo( ImageFormat fmt ) const = 0; + + // Bind standard textures + virtual void BindStandardTexture( Sampler_t sampler, StandardTextureId_t id ) = 0; + + // What are the lightmap dimensions? + virtual void GetLightmapDimensions( int *w, int *h ) = 0; + + // These methods are called when the shader must eject + restore HW memory + virtual void ReleaseShaderObjects( int nChangeFlags = 0 ) = 0; + virtual void RestoreShaderObjects( CreateInterfaceFn shaderFactory, int nChangeFlags = 0 ) = 0; + + // Used to prevent meshes from drawing. + virtual bool IsInStubMode() = 0; + virtual bool InFlashlightMode() const = 0; + + // For the shader API to shove the current version of aniso level into the + // "definitive" place (g_config) when the shader API decides to change it. + // Eventually, we should have a better system of who owns the definitive + // versions of config vars. + virtual void NoteAnisotropicLevel( int currentLevel ) = 0; + + // NOTE: Stuff after this is added after shipping HL2. + + // Are we rendering through the editor? + virtual bool InEditorMode() const = 0; + + virtual ITexture *GetRenderTargetEx( int nRenderTargetID ) = 0; + + // Tells the material system to draw a buffer clearing quad + virtual void DrawClearBufferQuad( unsigned char r, unsigned char g, unsigned char b, unsigned char a, bool bClearColor, bool bClearAlpha, bool bClearDepth ) = 0; + +#if defined( _X360 ) + virtual void ReadBackBuffer( Rect_t *pSrcRect, Rect_t *pDstRect, unsigned char *pData, ImageFormat dstFormat, int nDstStride ) = 0; +#endif + + // Calls from meshes to material system to handle queing/threading + virtual bool OnDrawMesh( IMesh *pMesh, int firstIndex, int numIndices ) = 0; + virtual bool OnDrawMesh( IMesh *pMesh, CPrimList *pLists, int nLists ) = 0; + virtual bool OnSetFlexMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) = 0; + virtual bool OnSetColorMesh( IMesh *pStaticMesh, IMesh *pMesh, int nVertexOffsetInBytes ) = 0; + virtual bool OnSetPrimitiveType( IMesh *pMesh, MaterialPrimitiveType_t type ) = 0; + virtual bool OnFlushBufferedPrimitives() = 0; + + + virtual void SyncMatrices() = 0; + virtual void SyncMatrix( MaterialMatrixMode_t ) = 0; + + virtual void BindStandardVertexTexture( VertexTextureSampler_t sampler, StandardTextureId_t id ) = 0; + virtual void GetStandardTextureDimensions( int *pWidth, int *pHeight, StandardTextureId_t id ) = 0; + + virtual int MaxHWMorphBatchCount() const = 0; + + // Interface for mat system to tell shaderapi about color correction + virtual void GetCurrentColorCorrection( ShaderColorCorrectionInfo_t* pInfo ) = 0; + + // Gets texture handles for ITextures + virtual ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel ) = 0; + + virtual float GetSubDHeight() = 0; + + virtual bool OnDrawMeshModulated( IMesh *pMesh, const Vector4D &diffuseModulation, int firstIndex, int numIndices ) = 0; + + // received an event while not in owning thread, handle this outside + virtual void OnThreadEvent( uint32 threadEvent ) = 0; + + virtual MaterialThreadMode_t GetThreadMode() = 0; + + // Remove any materials from memory that aren't in use as determined + // by the IMaterial's reference count. + virtual void UncacheUnusedMaterials( bool bRecomputeStateSnapshots = false ) = 0; + + virtual bool IsInFrame( ) const = 0; +}; + +#endif // ISHADERUTIL_H diff --git a/public/shaderapi/shareddefs.h b/public/shaderapi/shareddefs.h new file mode 100644 index 0000000..f9ba5ac --- /dev/null +++ b/public/shaderapi/shareddefs.h @@ -0,0 +1,106 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: NOTE: This file is for backward compat! +// We'll get rid of it soon. Most of the contents of this file were moved +// into shaderpi/ishadershadow.h, shaderapi/ishaderdynamic.h, or +// shaderapi/shareddefs.h +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef SHADERAPI_SHAREDDEFS_H +#define SHADERAPI_SHAREDDEFS_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Important enumerations +//----------------------------------------------------------------------------- +enum ShaderShadeMode_t +{ + SHADER_FLAT = 0, + SHADER_SMOOTH +}; + +enum ShaderTexCoordComponent_t +{ + SHADER_TEXCOORD_S = 0, + SHADER_TEXCOORD_T, + SHADER_TEXCOORD_U +}; + +enum ShaderTexFilterMode_t +{ + SHADER_TEXFILTERMODE_NEAREST, + SHADER_TEXFILTERMODE_LINEAR, + SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST, + SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST, + SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR, + SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR, + SHADER_TEXFILTERMODE_ANISOTROPIC +}; + +enum ShaderTexWrapMode_t +{ + SHADER_TEXWRAPMODE_CLAMP, + SHADER_TEXWRAPMODE_REPEAT, + SHADER_TEXWRAPMODE_BORDER + // MIRROR? - probably don't need it. +}; + + +//----------------------------------------------------------------------------- +// Sampler identifiers +// Samplers are used to enable and bind textures + by programmable shading algorithms +//----------------------------------------------------------------------------- +enum Sampler_t +{ + SHADER_SAMPLER0 = 0, + SHADER_SAMPLER1, + SHADER_SAMPLER2, + SHADER_SAMPLER3, + SHADER_SAMPLER4, + SHADER_SAMPLER5, + SHADER_SAMPLER6, + SHADER_SAMPLER7, + SHADER_SAMPLER8, + SHADER_SAMPLER9, + SHADER_SAMPLER10, + SHADER_SAMPLER11, + SHADER_SAMPLER12, + SHADER_SAMPLER13, + SHADER_SAMPLER14, + SHADER_SAMPLER15, + + SHADER_SAMPLER_COUNT, +}; + +//----------------------------------------------------------------------------- +// Vertex texture sampler identifiers +//----------------------------------------------------------------------------- +enum VertexTextureSampler_t +{ + SHADER_VERTEXTEXTURE_SAMPLER0 = 0, + SHADER_VERTEXTEXTURE_SAMPLER1, + SHADER_VERTEXTEXTURE_SAMPLER2, + SHADER_VERTEXTEXTURE_SAMPLER3, +}; + + +#if defined( _X360 ) +#define REVERSE_DEPTH_ON_X360 //uncomment to use D3DFMT_D24FS8 with an inverted depth viewport for better performance. Keep this in sync with the same named #define in materialsystem/stdshaders/common_fxc.h +#endif + +#if defined( REVERSE_DEPTH_ON_X360 ) +#define ReverseDepthOnX360() true +#else +#define ReverseDepthOnX360() false +#endif + + + +#endif // SHADERAPI_SHAREDDEFS_H diff --git a/public/shaderlib/BaseShader.h b/public/shaderlib/BaseShader.h new file mode 100644 index 0000000..08515a5 --- /dev/null +++ b/public/shaderlib/BaseShader.h @@ -0,0 +1,285 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// This is what all shaders inherit from. +//===========================================================================// + +#ifndef BASESHADER_H +#define BASESHADER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "materialsystem/IShader.h" +#include "materialsystem/imaterialvar.h" +#include "materialsystem/ishaderapi.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IMaterialVar; +class CPerInstanceContextData; + + +//----------------------------------------------------------------------------- +// Standard material vars +//----------------------------------------------------------------------------- +// Note: if you add to these, add to s_StandardParams in CBaseShader.cpp +enum ShaderMaterialVars_t +{ + FLAGS = 0, + FLAGS_DEFINED, // mask indicating if the flag was specified + FLAGS2, + FLAGS_DEFINED2, + COLOR, + ALPHA, + BASETEXTURE, + FRAME, + BASETEXTURETRANSFORM, + FLASHLIGHTTEXTURE, + FLASHLIGHTTEXTUREFRAME, + COLOR2, + SRGBTINT, + + NUM_SHADER_MATERIAL_VARS +}; + + +// Alpha belnd mode enums. Moved from basevsshader +enum BlendType_t +{ + // no alpha blending + BT_NONE = 0, + + + + // src * srcAlpha + dst * (1-srcAlpha) + // two passes for HDR: + // pass 1: + // color: src * srcAlpha + dst * (1-srcAlpha) + // alpha: srcAlpha * zero + dstAlpha * (1-srcAlpha) + // pass 2: + // color: none + // alpha: srcAlpha * one + dstAlpha * one + // + BT_BLEND, + + + + // src * one + dst * one + // one pass for HDR + BT_ADD, + + + + // Why do we ever use this instead of using premultiplied alpha? + // src * srcAlpha + dst * one + // two passes for HDR + // pass 1: + // color: src * srcAlpha + dst * one + // alpha: srcAlpha * one + dstAlpha * one + // pass 2: + // color: none + // alpha: srcAlpha * one + dstAlpha * one + BT_BLENDADD +}; + + +//----------------------------------------------------------------------------- +// Base class for shaders, contains helper methods. +//----------------------------------------------------------------------------- +class CBaseShader : public IShader +{ +public: + // constructor + CBaseShader(); + + // Methods inherited from IShader + virtual char const* GetFallbackShader( IMaterialVar** params ) const { return 0; } + virtual int GetParamCount( ) const; + virtual const ShaderParamInfo_t& GetParamInfo( int paramIndex ) const; + + virtual void InitShaderParams( IMaterialVar** ppParams, const char *pMaterialName ); + virtual void InitShaderInstance( IMaterialVar** ppParams, IShaderInit *pShaderInit, const char *pMaterialName, const char *pTextureGroupName ); + virtual void DrawElements( IMaterialVar **params, int nModulationFlags, IShaderShadow* pShaderShadow, IShaderDynamicAPI* pShaderAPI, + VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContext, CBasePerInstanceContextData** pInstanceDataPtr ); + + virtual int ComputeModulationFlags( IMaterialVar** params, IShaderDynamicAPI* pShaderAPI ); + virtual bool NeedsPowerOfTwoFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame = true ) const; + virtual bool NeedsFullFrameBufferTexture( IMaterialVar **params, bool bCheckSpecificToThisFrame = true ) const; + virtual bool IsTranslucent( IMaterialVar **params ) const; + +public: + // These functions must be implemented by the shader + virtual void OnInitShaderParams( IMaterialVar** ppParams, const char *pMaterialName ) {} + virtual void OnInitShaderInstance( IMaterialVar** ppParams, IShaderInit *pShaderInit, const char *pMaterialName ) = 0; + virtual void OnDrawElements( IMaterialVar **params, IShaderShadow* pShaderShadow, IShaderDynamicAPI* pShaderAPI, VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContextDataPtr ) = 0; + + // Sets the default shadow state + void SetInitialShadowState( ); + + // Draws a snapshot + void Draw( bool bMakeActualDrawCall = true ); + + // Are we currently taking a snapshot? + bool IsSnapshotting() const; + + // Methods related to building per-instance ("PI_") command buffers + void PI_BeginCommandBuffer(); + void PI_EndCommandBuffer(); + void PI_SetPixelShaderAmbientLightCube( int nFirstRegister ); + void PI_SetPixelShaderLocalLighting( int nFirstRegister ); + void PI_SetPixelShaderAmbientLightCubeLuminance( int nFirstRegister ); + void PI_SetPixelShaderGlintDamping( int nFirstRegister ); + void PI_SetVertexShaderAmbientLightCube( /*int nFirstRegister*/ ); + void PI_SetModulationPixelShaderDynamicState( int nRegister ); + void PI_SetModulationPixelShaderDynamicState_LinearColorSpace_LinearScale( int nRegister, float scale ); + void PI_SetModulationPixelShaderDynamicState_LinearScale( int nRegister, float scale ); + void PI_SetModulationPixelShaderDynamicState_LinearScale_ScaleInW( int nRegister, float scale ); + void PI_SetModulationPixelShaderDynamicState_LinearColorSpace( int nRegister ); + void PI_SetModulationPixelShaderDynamicState_Identity( int nRegister ); + void PI_SetModulationVertexShaderDynamicState( void ); + void PI_SetModulationVertexShaderDynamicState_LinearScale( float flScale ); + + // Gets at the current materialvar flags + int CurrentMaterialVarFlags() const; + + // Gets at the current materialvar2 flags + int CurrentMaterialVarFlags2() const; + + // Finds a particular parameter (works because the lowest parameters match the shader) + int FindParamIndex( const char *pName ) const; + + // Are we using graphics? + bool IsUsingGraphics(); + + // Are we using editor materials? + bool CanUseEditorMaterials() const; + + // Loads a texture + void LoadTexture( int nTextureVar ); + + // Loads a bumpmap + void LoadBumpMap( int nTextureVar ); + + // Loads a cubemap + void LoadCubeMap( int nTextureVar ); + + // get the shaderapi handle for a texture. BE CAREFUL WITH THIS. + ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( int nTextureVar, int nFrameVar, int nTextureChannel = 0 ); + ShaderAPITextureHandle_t GetShaderAPITextureBindHandle( ITexture *pTexture, int nFrame, int nTextureChannel = 0 ); + + // Binds a texture + void BindTexture( Sampler_t sampler1, Sampler_t sampler2, int nTextureVar, int nFrameVar = -1 ); + void BindTexture( Sampler_t sampler1, int nTextureVar, int nFrameVar = -1 ); + void BindTexture( Sampler_t sampler1, ITexture *pTexture, int nFrame = 0 ); + void BindTexture( Sampler_t sampler1, Sampler_t sampler2, ITexture *pTexture, int nFrame = 0 ); + + // Bind vertex texture + void BindVertexTexture( VertexTextureSampler_t vtSampler, int nTextureVar, int nFrame = 0 ); + + // Is the texture translucent? + bool TextureIsTranslucent( int textureVar, bool isBaseTexture ); + + // Is the color var white? + bool IsWhite( int colorVar ); + + // Helper methods for fog + void FogToOOOverbright( void ); + void FogToWhite( void ); + void FogToBlack( void ); + void FogToGrey( void ); + void FogToFogColor( void ); + void DisableFog( void ); + void DefaultFog( void ); + + // Helpers for alpha blending + void EnableAlphaBlending( ShaderBlendFactor_t src, ShaderBlendFactor_t dst ); + void DisableAlphaBlending(); + + void SetBlendingShadowState( BlendType_t nMode ); + + void SetNormalBlendingShadowState( int textureVar = -1, bool isBaseTexture = true ); + void SetAdditiveBlendingShadowState( int textureVar = -1, bool isBaseTexture = true ); + void SetDefaultBlendingShadowState( int textureVar = -1, bool isBaseTexture = true ); + void SingleTextureLightmapBlendMode( ); + + // Helpers for color modulation + bool IsAlphaModulating(); + // Helpers for HDR + bool IsHDREnabled( void ); + + bool UsingFlashlight( IMaterialVar **params ) const; + bool UsingEditor( IMaterialVar **params ) const; + + void ApplyColor2Factor( float *pColorOut ) const; // (*pColorOut) *= COLOR2 + +private: + // This is a per-instance state which is handled completely by the system + void PI_SetSkinningMatrices(); + void PI_SetVertexShaderLocalLighting( ); + + FORCEINLINE void SetFogMode( ShaderFogMode_t fogMode ); + +protected: + static IMaterialVar **s_ppParams; + static const char *s_pTextureGroupName; // Current material's texture group name. + static IShaderShadow *s_pShaderShadow; + static IShaderDynamicAPI *s_pShaderAPI; + static IShaderInit *s_pShaderInit; +private: + static int s_nPassCount; + static int s_nModulationFlags; + static CPerInstanceContextData** s_pInstanceDataPtr; + + template friend class CBaseCommandBufferBuilder; +}; + + +//----------------------------------------------------------------------------- +// Gets at the current materialvar flags +//----------------------------------------------------------------------------- +inline int CBaseShader::CurrentMaterialVarFlags() const +{ + return s_ppParams[FLAGS]->GetIntValue(); +} + +//----------------------------------------------------------------------------- +// Gets at the current materialvar2 flags +//----------------------------------------------------------------------------- +inline int CBaseShader::CurrentMaterialVarFlags2() const +{ + return s_ppParams[FLAGS2]->GetIntValue(); +} + +//----------------------------------------------------------------------------- +// Are we currently taking a snapshot? +//----------------------------------------------------------------------------- +inline bool CBaseShader::IsSnapshotting() const +{ + return (s_pShaderShadow != NULL); +} + +//----------------------------------------------------------------------------- +// Is the color var white? +//----------------------------------------------------------------------------- +inline bool CBaseShader::IsWhite( int colorVar ) +{ + if (colorVar < 0) + return true; + + if (!s_ppParams[colorVar]->IsDefined()) + return true; + + float color[3]; + s_ppParams[colorVar]->GetVecValue( color, 3 ); + return (color[0] >= 1.0f) && (color[1] >= 1.0f) && (color[2] >= 1.0f); +} + + +#endif // BASESHADER_H diff --git a/public/shaderlib/ShaderDLL.h b/public/shaderlib/ShaderDLL.h new file mode 100644 index 0000000..bb090bf --- /dev/null +++ b/public/shaderlib/ShaderDLL.h @@ -0,0 +1,49 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef SHADERDLL_H +#define SHADERDLL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class IShader; +class ICvar; + +//----------------------------------------------------------------------------- +// The standard implementation of CShaderDLL +//----------------------------------------------------------------------------- +class IShaderDLL +{ +public: + // Adds a shader to the list of shaders + virtual void InsertShader( IShader *pShader ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Singleton interface +//----------------------------------------------------------------------------- +IShaderDLL *GetShaderDLL(); + +//----------------------------------------------------------------------------- +// Singleton interface for CVars +//----------------------------------------------------------------------------- +ICvar *GetCVar(); + + + + + +#endif // SHADERDLL_H diff --git a/public/shaderlib/cshader.h b/public/shaderlib/cshader.h new file mode 100644 index 0000000..36f28f0 --- /dev/null +++ b/public/shaderlib/cshader.h @@ -0,0 +1,439 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef CSHADER_H +#define CSHADER_H + +#ifdef _WIN32 +#pragma once +#endif + +// uncomment this if you want to build for nv3x +//#define NV3X 1 + +// This is what all shaders include. +// CBaseShader will become CShader in this file. +#include "materialsystem/ishaderapi.h" +#include "utlvector.h" +#include "materialsystem/imaterialvar.h" +#include "materialsystem/imaterial.h" +#include "BaseShader.h" + +#include "materialsystem/itexture.h" + +// Included for convenience because they are used in a bunch of shaders +#include "materialsystem/imesh.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/materialsystem_config.h" +#include "shaderlib/ShaderDLL.h" + +// make "local variable is initialized but not referenced" warnings errors for combo checking macros +#pragma warning ( error : 4189 ) + +//----------------------------------------------------------------------------- +// Global interfaces +//----------------------------------------------------------------------------- +extern IMaterialSystemHardwareConfig *g_pHardwareConfig; +extern const MaterialSystem_Config_t *g_pConfig; +extern bool g_shaderConfigDumpEnable; + +// Helper method +bool IsUsingGraphics(); + +#define SOFTWARE_VERTEX_SHADER(name) \ + static void SoftwareVertexShader_ ## name( CMeshBuilder &meshBuilder, IMaterialVar **params, IShaderDynamicAPI* pShaderAPI ) + +#define FORWARD_DECLARE_SOFTWARE_VERTEX_SHADER(name)\ + static void SoftwareVertexShader_ ## name( CMeshBuilder &meshBuilder, IMaterialVar **params, IShaderDynamicAPI* pShaderAPI ); + +#define USE_SOFTWARE_VERTEX_SHADER(name) \ + m_SoftwareVertexShader = SoftwareVertexShader_ ## name + +#define SHADER_INIT_PARAMS() \ + virtual void OnInitShaderParams( IMaterialVar **params, const char *pMaterialName ) + +#define SHADER_FALLBACK \ + virtual char const* GetFallbackShader( IMaterialVar** params ) const + +// Typesafe flag setting +inline void CShader_SetFlags( IMaterialVar **params, MaterialVarFlags_t _flag ) +{ + params[FLAGS]->SetIntValue( params[FLAGS]->GetIntValue() | (_flag) ); +} + +inline bool CShader_IsFlagSet( IMaterialVar **params, MaterialVarFlags_t _flag ) +{ + return ((params[FLAGS]->GetIntValue() & (_flag) ) != 0); +} + +#define SET_FLAGS( _flag ) CShader_SetFlags( params, _flag ) +#define CLEAR_FLAGS( _flag ) params[FLAGS]->SetIntValue( params[FLAGS]->GetIntValue() & ~(_flag) ) +#define IS_FLAG_SET( _flag ) CShader_IsFlagSet( params, _flag ) +#define IS_FLAG_DEFINED( _flag ) ((params[FLAGS_DEFINED]->GetIntValue() & (_flag) ) != 0) + +#define IS_PARAM_DEFINED( _param ) ( ( ( _param >= 0 ) && ( params[_param]->IsDefined() ) ) ) + +#define SET_PARAM_STRING_IF_NOT_DEFINED( nParamIndex, kDefaultValue ) \ + if ( ( (nParamIndex) != -1 ) && ( !params[nParamIndex]->IsDefined() ) ) \ + { \ + params[nParamIndex]->SetStringValue( kDefaultValue ); \ + } + +#define SET_PARAM_FLOAT_IF_NOT_DEFINED( nParamIndex, kDefaultValue ) \ + if ( ( (nParamIndex) != -1 ) && ( !params[nParamIndex]->IsDefined() ) ) \ + { \ + params[nParamIndex]->SetFloatValue( kDefaultValue ); \ + } + +#define SET_PARAM_VEC_IF_NOT_DEFINED( nParamIndex, kDefaultValue, nSize ) \ + if ( ( (nParamIndex) != -1 ) && ( !params[nParamIndex]->IsDefined() ) ) \ + { \ + params[nParamIndex]->SetVecValue( kDefaultValue, nSize ); \ + } + +#define SET_PARAM_INT_IF_NOT_DEFINED( nParamIndex, kDefaultValue ) \ + if ( ( (nParamIndex) != -1 ) && ( !params[nParamIndex]->IsDefined() ) ) \ +{ \ + params[nParamIndex]->SetIntValue( kDefaultValue ); \ +} + +// Typesafe flag setting +inline void CShader_SetFlags2( IMaterialVar **params, MaterialVarFlags2_t _flag ) +{ + params[FLAGS2]->SetIntValue( params[FLAGS2]->GetIntValue() | (_flag) ); +} + +inline bool CShader_IsFlag2Set( IMaterialVar **params, MaterialVarFlags2_t _flag ) +{ + return ((params[FLAGS2]->GetIntValue() & (_flag) ) != 0); +} + +#define SET_FLAGS2( _flag ) CShader_SetFlags2( params, _flag ) +#define CLEAR_FLAGS2( _flag ) params[FLAGS2]->SetIntValue( params[FLAGS2]->GetIntValue() & ~(_flag) ) +#define IS_FLAG2_SET( _flag ) CShader_IsFlag2Set( params, _flag ) +#define IS_FLAG2_DEFINED( _flag ) ((params[FLAGS_DEFINED2]->GetIntValue() & (_flag) ) != 0) + +#define __BEGIN_SHADER_INTERNAL(_baseclass, name, help, flags) \ + namespace name \ + {\ + typedef _baseclass CBaseClass;\ + static const char *s_HelpString = help; \ + static const char *s_Name = #name; \ + static int s_nFlags = flags; \ + class CShaderParam;\ + static CUtlVector s_ShaderParams;\ + static CShaderParam *s_pShaderParamOverrides[NUM_SHADER_MATERIAL_VARS];\ + class CShaderParam\ + {\ + public:\ + CShaderParam( ShaderMaterialVars_t var, ShaderParamType_t type, const char *pDefaultParam, const char *pHelp, int nFlags )\ + {\ + m_Info.m_pName = "override";\ + m_Info.m_Type = type;\ + m_Info.m_pDefaultValue = pDefaultParam;\ + m_Info.m_pHelp = pHelp;\ + m_Info.m_nFlags = nFlags;\ + AssertMsg( !s_pShaderParamOverrides[var], ( "Shader parameter override duplicately defined!" ) );\ + s_pShaderParamOverrides[var] = this;\ + m_Index = var;\ + }\ + CShaderParam( const char *pName, ShaderParamType_t type, const char *pDefaultParam, const char *pHelp, int nFlags )\ + {\ + m_Info.m_pName = pName;\ + m_Info.m_Type = type;\ + m_Info.m_pDefaultValue = pDefaultParam;\ + m_Info.m_pHelp = pHelp;\ + m_Info.m_nFlags = nFlags;\ + m_Index = NUM_SHADER_MATERIAL_VARS + s_ShaderParams.Count();\ + s_ShaderParams.AddToTail( this );\ + }\ + operator int() \ + {\ + return m_Index;\ + }\ + const ShaderParamInfo_t &GetInfo() const\ + {\ + return m_Info;\ + }\ + private:\ + ShaderParamInfo_t m_Info; \ + int m_Index;\ + };\ + +#define BEGIN_SHADER(name,help) __BEGIN_SHADER_INTERNAL( CBaseShader, name, help, 0 ) +#define BEGIN_SHADER_FLAGS(name,help,flags) __BEGIN_SHADER_INTERNAL( CBaseShader, name, help, flags ) + +#define BEGIN_SHADER_PARAMS + +#define SHADER_PARAM( param, paramtype, paramdefault, paramhelp ) \ + static CShaderParam param( "$" #param, paramtype, paramdefault, paramhelp, 0 ); + +#define SHADER_PARAM_FLAGS( param, paramtype, paramdefault, paramhelp, flags ) \ + static CShaderParam param( "$" #param, paramtype, paramdefault, paramhelp, flags ); + +#define SHADER_PARAM_OVERRIDE( param, paramtype, paramdefault, paramhelp, flags ) \ + static CShaderParam param( (ShaderMaterialVars_t) ::param, paramtype, paramdefault, paramhelp, flags ); + + // regarding the macro above: the "::" was added to the first argument in order to disambiguate it for GCC. + // for example, in cloak.cpp, this usage appears: + // SHADER_PARAM_OVERRIDE( COLOR, SHADER_PARAM_TYPE_COLOR, "{255 255 255}", "unused", SHADER_PARAM_NOT_EDITABLE ) + // which in turn tries to ask the compiler to instantiate an object like so: + // static CShaderParam COLOR( (ShaderMaterialVars_t)COLOR, SHADER_PARAM_TYPE_COLOR, "{255 255 255}", "unused", SHADER_PARAM_NOT_EDITABLE ) + // and GCC thinks that the reference to COLOR in the arg list is actually a reference to the object we're in the middle of making. + // and you get --> error: invalid cast from type ‘Cloak_DX90::CShaderParam’ to type ‘ShaderMaterialVars_t’ + // Resolved: add the "::" so compiler knows that reference is to the enum, not to the name of the object being made. + + +#define END_SHADER_PARAMS \ + class CShader : public CBaseClass\ + {\ + public: + +#define END_SHADER }; \ + static CShader s_ShaderInstance;\ +} // namespace + + +#define SHADER_INIT \ + char const* GetName() const \ + { \ + return s_Name; \ + } \ + int GetFlags() const \ + { \ + return s_nFlags; \ + } \ + int GetParamCount() const \ + {\ + return CBaseClass::GetParamCount() + s_ShaderParams.Count();\ + }\ + const ShaderParamInfo_t& GetParamInfo( int param ) const \ + {\ + int nBaseClassParamCount = CBaseClass::GetParamCount( ); \ + if (param < nBaseClassParamCount) \ + return CBaseClass::GetParamInfo( param ); \ + else \ + return s_ShaderParams[param - nBaseClassParamCount]->GetInfo(); \ + }\ + void OnInitShaderInstance( IMaterialVar **params, IShaderInit *pShaderInit, const char *pMaterialName ) + +#define SHADER_DRAW \ + void OnDrawElements( IMaterialVar **params, IShaderShadow* pShaderShadow, IShaderDynamicAPI* pShaderAPI, VertexCompressionType_t vertexCompression, CBasePerMaterialContextData **pContextDataPtr ) + +#define SHADOW_STATE if (pShaderShadow) +#define DYNAMIC_STATE if (pShaderAPI) + +#define ShaderWarning if (pShaderShadow) Warning + +//----------------------------------------------------------------------------- +// Used to easily define a shader which *always* falls back +//----------------------------------------------------------------------------- +#define DEFINE_FALLBACK_SHADER( _shadername, _fallbackshadername ) \ + BEGIN_SHADER( _shadername, "" ) \ + BEGIN_SHADER_PARAMS \ + END_SHADER_PARAMS \ + SHADER_FALLBACK { return #_fallbackshadername; } \ + SHADER_INIT {} \ + SHADER_DRAW {} \ + END_SHADER + + +//----------------------------------------------------------------------------- +// Used to easily define a shader which inherits from another shader +//----------------------------------------------------------------------------- + +// FIXME: There's a compiler bug preventing this from working. +// Maybe it'll work under VC7! + +//#define BEGIN_INHERITED_SHADER( name, _baseclass, help ) \ +// namespace _baseclass \ +// {\ +// __BEGIN_SHADER_INTERNAL( _baseclass::CShader, name, help ) + +//#define END_INHERITED_SHADER END_SHADER } + +//#define CHAIN_SHADER_INIT_PARAMS() CBaseClass::OnInitShaderParams( params, pMaterialName ) +//#define CHAIN_SHADER_FALLBACK() CBaseClass::GetFallbackShader( params ) +//#define CHAIN_SHADER_INIT() CBaseClass::OnInitShaderInstance( params, pShaderInit, pMaterialName ) +//#define CHAIN_SHADER_DRAW() CBaseClass::OnDrawElements( params, pShaderShadow, pShaderAPI ) + +// A dumbed-down version which does what I need now which works +// This version doesn't allow you to do chain *anything* down to the base class +#define BEGIN_INHERITED_SHADER_FLAGS( _name, _base, _help, _flags ) \ + namespace _base\ + {\ + namespace _name\ + {\ + static const char *s_Name = #_name; \ + static const char *s_HelpString = _help;\ + static int s_nFlags = _flags;\ + class CShader : public _base::CShader\ + {\ + public:\ + char const* GetName() const \ + { \ + return s_Name; \ + } \ + int GetFlags() const \ + { \ + return s_nFlags; \ + } + +#define BEGIN_INHERITED_SHADER( _name, _base, _help ) BEGIN_INHERITED_SHADER_FLAGS( _name, _base, _help, 0 ) +#define END_INHERITED_SHADER END_SHADER } + +// psh ## shader is used here to generate a warning if you don't ever call SET_DYNAMIC_PIXEL_SHADER +#define DECLARE_DYNAMIC_PIXEL_SHADER( shader ) \ + int declaredynpixshader_ ## shader ## _missingcurlybraces = 0; \ + declaredynpixshader_ ## shader ## _missingcurlybraces = declaredynpixshader_ ## shader ## _missingcurlybraces; \ + shader ## _Dynamic_Index _pshIndex( pShaderAPI ); \ + int psh ## shader = 0 + +// vsh ## shader is used here to generate a warning if you don't ever call SET_DYNAMIC_VERTEX_SHADER +#define DECLARE_DYNAMIC_VERTEX_SHADER( shader ) \ + int declaredynvertshader_ ## shader ## _missingcurlybraces = 0; \ + declaredynvertshader_ ## shader ## _missingcurlybraces = declaredynvertshader_ ## shader ## _missingcurlybraces; \ + shader ## _Dynamic_Index _vshIndex( pShaderAPI ); \ + int vsh ## shader = 0 + + +// psh ## shader is used here to generate a warning if you don't ever call SET_STATIC_PIXEL_SHADER +#define DECLARE_STATIC_PIXEL_SHADER( shader ) \ + int declarestaticpixshader_ ## shader ## _missingcurlybraces = 0; \ + declarestaticpixshader_ ## shader ## _missingcurlybraces = declarestaticpixshader_ ## shader ## _missingcurlybraces; \ + shader ## _Static_Index _pshIndex( pShaderShadow, params ); \ + int psh ## shader = 0 + +// vsh ## shader is used here to generate a warning if you don't ever call SET_STATIC_VERTEX_SHADER +#define DECLARE_STATIC_VERTEX_SHADER( shader ) \ + int declarestaticvertshader_ ## shader ## _missingcurlybraces = 0; \ + declarestaticvertshader_ ## shader ## _missingcurlybraces = declarestaticvertshader_ ## shader ## _missingcurlybraces; \ + shader ## _Static_Index _vshIndex( pShaderShadow, params ); \ + int vsh ## shader = 0 + + +// psh_forgot_to_set_dynamic_ ## var is used to make sure that you set all +// all combos. If you don't, you will get an undefined variable used error +// in the SET_DYNAMIC_PIXEL_SHADER block. +#define SET_DYNAMIC_PIXEL_SHADER_COMBO( var, val ) \ + int dynpixshadercombo_ ## var ## _missingcurlybraces = 0; \ + dynpixshadercombo_ ## var ## _missingcurlybraces = dynpixshadercombo_ ## var ## _missingcurlybraces; \ + _pshIndex.Set ## var( ( val ) ); if(g_shaderConfigDumpEnable){printf("\n PS dyn var %s = %d (%s)", #var, (int) val, #val );}; \ + int psh_forgot_to_set_dynamic_ ## var = 0 + +// Same as SET_DYNAMIC_PIXEL_SHADER_COMBO, except doesn't set "psh_forgot_to_set_dynamic_*" since +// it isn't required to set a default variable, and we don't want an error when we do set it. +#define SET_DYNAMIC_PIXEL_SHADER_COMBO_OVERRIDE_DEFAULT( var, val ) \ + int dynpixshadercombo_ ## var ## _missingcurlybraces = 0; \ + dynpixshadercombo_ ## var ## _missingcurlybraces = dynpixshadercombo_ ## var ## _missingcurlybraces; \ + _pshIndex.Set ## var( ( val ) ); + +// vsh_forgot_to_set_dynamic_ ## var is used to make sure that you set all +// all combos. If you don't, you will get an undefined variable used error +// in the SET_DYNAMIC_VERTEX_SHADER block. +#define SET_DYNAMIC_VERTEX_SHADER_COMBO( var, val ) \ + int dynvertshadercombo_ ## var ## _missingcurlybraces = 0; \ + dynvertshadercombo_ ## var ## _missingcurlybraces = dynvertshadercombo_ ## var ## _missingcurlybraces; \ + _vshIndex.Set ## var( ( val ) ); if(g_shaderConfigDumpEnable){printf("\n VS dyn var %s = %d (%s)", #var, (int) val, #val );}; \ + int vsh_forgot_to_set_dynamic_ ## var = 0 + + +// psh_forgot_to_set_static_ ## var is used to make sure that you set all +// all combos. If you don't, you will get an undefined variable used error +// in the SET_STATIC_PIXEL_SHADER block. +#define SET_STATIC_PIXEL_SHADER_COMBO( var, val ) \ + int staticpixshadercombo_ ## var ## _missingcurlybraces = 0; \ + staticpixshadercombo_ ## var ## _missingcurlybraces = staticpixshadercombo_ ## var ## _missingcurlybraces; \ + _pshIndex.Set ## var( ( val ) ); if(g_shaderConfigDumpEnable){printf("\n PS stat var %s = %d (%s)", #var, (int) val, #val );}; \ + int psh_forgot_to_set_static_ ## var = 0 + +// vsh_forgot_to_set_static_ ## var is used to make sure that you set all +// all combos. If you don't, you will get an undefined variable used error +// in the SET_STATIC_VERTEX_SHADER block. +#define SET_STATIC_VERTEX_SHADER_COMBO( var, val ) \ + int staticvertshadercombo_ ## var ## _missingcurlybraces = 0; \ + staticvertshadercombo_ ## var ## _missingcurlybraces = staticvertshadercombo_ ## var ## _missingcurlybraces; \ + _vshIndex.Set ## var( ( val ) ); if(g_shaderConfigDumpEnable){printf("\n VS stat var %s = %d (%s)", #var, (int) val, #val );}; \ + int vsh_forgot_to_set_static_ ## var = 0 + +#define SET_STATIC_VERTEX_SHADER_COMBO_HAS_DEFAULT( var, val ) \ + int staticvertshadercombo_ ## var ## _missingcurlybraces = 0; \ + staticvertshadercombo_ ## var ## _missingcurlybraces = staticvertshadercombo_ ## var ## _missingcurlybraces; \ + _vshIndex.Set ## var( ( val ) ); + + +// psh_testAllCombos adds up all of the psh_forgot_to_set_dynamic_ ## var's from +// SET_DYNAMIC_PIXEL_SHADER_COMBO so that an error is generated if they aren't set. +// psh_testAllCombos is set to itself to avoid an unused variable warning. +// psh ## shader being set to itself ensures that DECLARE_DYNAMIC_PIXEL_SHADER +// was called for this particular shader. +#define SET_DYNAMIC_PIXEL_SHADER( shader ) \ + int dynamicpixshader_ ## shader ## _missingcurlybraces = 0; \ + dynamicpixshader_ ## shader ## _missingcurlybraces = dynamicpixshader_ ## shader ## _missingcurlybraces; \ + int psh_testAllCombos = shaderDynamicTest_ ## shader; \ + psh_testAllCombos = psh_testAllCombos; \ + psh ## shader = psh ## shader; \ + pShaderAPI->SetPixelShaderIndex( _pshIndex.GetIndex() ) + +#define SET_DYNAMIC_PIXEL_SHADER_CMD( cmdstream, shader ) \ + int dynamicpixshader_ ## shader ## _missingcurlybraces = 0; \ + dynamicpixshader_ ## shader ## _missingcurlybraces = dynamicpixshader_ ## shader ## _missingcurlybraces; \ + int psh_testAllCombos = shaderDynamicTest_ ## shader; \ + psh_testAllCombos = psh_testAllCombos; \ + psh ## shader = psh ## shader; \ + cmdstream.SetPixelShaderIndex( _pshIndex.GetIndex() ) + + +// vsh_testAllCombos adds up all of the vsh_forgot_to_set_dynamic_ ## var's from +// SET_DYNAMIC_VERTEX_SHADER_COMBO so that an error is generated if they aren't set. +// vsh_testAllCombos is set to itself to avoid an unused variable warning. +// vsh ## shader being set to itself ensures that DECLARE_DYNAMIC_VERTEX_SHADER +// was called for this particular shader. +#define SET_DYNAMIC_VERTEX_SHADER( shader ) \ + int dynamicvertshader_ ## shader ## _missingcurlybraces = 0; \ + dynamicvertshader_ ## shader ## _missingcurlybraces = dynamicvertshader_ ## shader ## _missingcurlybraces; \ + int vsh_testAllCombos = shaderDynamicTest_ ## shader; \ + vsh_testAllCombos = vsh_testAllCombos; \ + vsh ## shader = vsh ## shader; \ + pShaderAPI->SetVertexShaderIndex( _vshIndex.GetIndex() ) + +#define SET_DYNAMIC_VERTEX_SHADER_CMD( cmdstream, shader ) \ + int dynamicvertshader_ ## shader ## _missingcurlybraces = 0; \ + dynamicvertshader_ ## shader ## _missingcurlybraces = dynamicvertshader_ ## shader ## _missingcurlybraces; \ + int vsh_testAllCombos = shaderDynamicTest_ ## shader; \ + vsh_testAllCombos = vsh_testAllCombos; \ + vsh ## shader = vsh ## shader; \ + cmdstream.SetVertexShaderIndex( _vshIndex.GetIndex() ) + + +// psh_testAllCombos adds up all of the psh_forgot_to_set_static_ ## var's from +// SET_STATIC_PIXEL_SHADER_COMBO so that an error is generated if they aren't set. +// psh_testAllCombos is set to itself to avoid an unused variable warning. +// psh ## shader being set to itself ensures that DECLARE_STATIC_PIXEL_SHADER +// was called for this particular shader. +#define SET_STATIC_PIXEL_SHADER( shader ) \ + int staticpixshader_ ## shader ## _missingcurlybraces = 0; \ + staticpixshader_ ## shader ## _missingcurlybraces = staticpixshader_ ## shader ## _missingcurlybraces; \ + int psh_testAllCombos = shaderStaticTest_ ## shader; \ + psh_testAllCombos = psh_testAllCombos; \ + psh ## shader = psh ## shader; \ + pShaderShadow->SetPixelShader( #shader, _pshIndex.GetIndex() ) + +// vsh_testAllCombos adds up all of the vsh_forgot_to_set_static_ ## var's from +// SET_STATIC_VERTEX_SHADER_COMBO so that an error is generated if they aren't set. +// vsh_testAllCombos is set to itself to avoid an unused variable warning. +// vsh ## shader being set to itself ensures that DECLARE_STATIC_VERTEX_SHADER +// was called for this particular shader. +#define SET_STATIC_VERTEX_SHADER( shader ) \ + int staticvertshader_ ## shader ## _missingcurlybraces = 0; \ + staticvertshader_ ## shader ## _missingcurlybraces = staticvertshader_ ## shader ## _missingcurlybraces; \ + int vsh_testAllCombos = shaderStaticTest_ ## shader; \ + vsh_testAllCombos = vsh_testAllCombos; \ + vsh ## shader = vsh ## shader; \ + pShaderShadow->SetVertexShader( #shader, _vshIndex.GetIndex() ) + +#endif // CSHADER_H diff --git a/public/shake.h b/public/shake.h new file mode 100644 index 0000000..010bdcd --- /dev/null +++ b/public/shake.h @@ -0,0 +1,91 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Network data for screen shake and screen fade. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SHAKE_H +#define SHAKE_H +#ifdef _WIN32 +#pragma once +#endif + + +// +// Commands for the screen shake effect. +enum ShakeCommand_t +{ + SHAKE_START = 0, // Starts the screen shake for all players within the radius. + SHAKE_STOP, // Stops the screen shake for all players within the radius. + SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius. + SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius. + SHAKE_START_RUMBLEONLY, // Starts a shake effect that only rumbles the controller, no screen effect. + SHAKE_START_NORUMBLE, // Starts a shake that does NOT rumble the controller. +}; + +// This structure must have a working copy/assignment constructor. +// At the time of this writing, the implicit one works properly. + +struct ScreenShake_t +{ + ShakeCommand_t command; + float amplitude; + float frequency; + float duration; + Vector direction; + + inline ScreenShake_t() : direction(0,0,0) {}; + inline ScreenShake_t( ShakeCommand_t _command, float _amplitude, float _frequency, + float _duration, const Vector &_direction ); +}; + + +// +// Screen shake message. +// +extern int gmsgShake; + + +// +// Commands for the screen tilt effect. +// + +struct ScreenTilt_t +{ + int command; + bool easeInOut; + QAngle angle; + float duration; + float time; +}; + +// Fade in/out +extern int gmsgFade; + +#define FFADE_IN 0x0001 // Just here so we don't pass 0 into the function +#define FFADE_OUT 0x0002 // Fade out (not in) +#define FFADE_MODULATE 0x0004 // Modulate (don't blend) +#define FFADE_STAYOUT 0x0008 // ignores the duration, stays faded out until new ScreenFade message received +#define FFADE_PURGE 0x0010 // Purges all other fades, replacing them with this one + +#define SCREENFADE_FRACBITS 9 // which leaves 16-this for the integer part +// This structure is sent over the net to describe a screen fade event +struct ScreenFade_t +{ + unsigned short duration; // FIXED 16 bit, with SCREENFADE_FRACBITS fractional, seconds duration + unsigned short holdTime; // FIXED 16 bit, with SCREENFADE_FRACBITS fractional, seconds duration until reset (fade & hold) + short fadeFlags; // flags + byte r, g, b, a; // fade to color ( max alpha ) +}; + + +// inline funcs: + +inline ScreenShake_t::ScreenShake_t( ShakeCommand_t _command, float _amplitude, float _frequency, + float _duration, const Vector &_direction ) : + command(_command), amplitude(_amplitude), frequency(_frequency), + duration(_duration), direction(_direction) +{} + +#endif // SHAKE_H diff --git a/public/shattersurfacetypes.h b/public/shattersurfacetypes.h new file mode 100644 index 0000000..ffdee42 --- /dev/null +++ b/public/shattersurfacetypes.h @@ -0,0 +1,21 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined ( SHATTERSURFACETYPES_H ) +#define SHATTERSURFACETYPES_H +#ifdef _WIN32 +#pragma once +#endif + +enum ShatterSurface_t +{ + // Note: This much match with the client entity + SHATTERSURFACE_GLASS = 0, + SHATTERSURFACE_TILE = 1, +}; + +#endif diff --git a/public/simple_physics.cpp b/public/simple_physics.cpp new file mode 100644 index 0000000..10f58b2 --- /dev/null +++ b/public/simple_physics.cpp @@ -0,0 +1,72 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "simple_physics.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CSimplePhysics::CSimplePhysics() +{ + Init( 1.0f / 30.0f ); // default is 30 steps per second +} + + +void CSimplePhysics::Init( float flTimeStep ) +{ + m_flPredictedTime = 0; + m_iCurTimeStep = 0; + m_flTimeStep = flTimeStep; + m_flTimeStepMul = m_flTimeStep*m_flTimeStep*0.5f; +} + + +void CSimplePhysics::Simulate( + CSimplePhysics::CNode *pNodes, + int nNodes, + CSimplePhysics::IHelper *pHelper, + float dt, + float flDamp ) +{ + // Figure out how many time steps to run. + m_flPredictedTime += dt; + int newTimeStep = (int)ceil( m_flPredictedTime / m_flTimeStep ); + int nTimeSteps = newTimeStep - m_iCurTimeStep; + for( int iTimeStep=0; iTimeStep < nTimeSteps; iTimeStep++ ) + { + // Simulate everything.. + for( int iNode=0; iNode < nNodes; iNode++ ) + { + CSimplePhysics::CNode *pNode = &pNodes[iNode]; + + // Apply forces. + Vector vAccel; + pHelper->GetNodeForces( pNodes, iNode, &vAccel ); + Assert( vAccel.IsValid() ); + + Vector vPrevPos = pNode->m_vPos; + pNode->m_vPos = pNode->m_vPos + (pNode->m_vPos - pNode->m_vPrevPos) * flDamp + vAccel * m_flTimeStepMul; + pNode->m_vPrevPos = vPrevPos; + } + + // Apply constraints. + pHelper->ApplyConstraints( pNodes, nNodes ); + } + m_iCurTimeStep = newTimeStep; + + // Setup predicted positions. + float flInterpolant = (m_flPredictedTime - (GetCurTime() - m_flTimeStep)) / m_flTimeStep; + for( int iNode=0; iNode < nNodes; iNode++ ) + { + CSimplePhysics::CNode *pNode = &pNodes[iNode]; + VectorLerp( pNode->m_vPrevPos, pNode->m_vPos, flInterpolant, pNode->m_vPredicted ); + } +} + + diff --git a/public/simple_physics.h b/public/simple_physics.h new file mode 100644 index 0000000..6cec7af --- /dev/null +++ b/public/simple_physics.h @@ -0,0 +1,81 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SIMPLE_PHYSICS_H +#define SIMPLE_PHYSICS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/vector.h" + + +// CSimplePhysics is a framework for simplified physics simulation. +// It simulates at a fixed timestep and uses the Verlet integrator. +// +// To use it, create your nodes and implement your constraints and +// forces in an IHelper, then call Simulate each frame. +// CSimplePhysics will figure out how many timesteps to run and will +// provide predicted positions of things for you. +class CSimplePhysics +{ +public: + + class CNode + { + public: + + // Call this when initializing the nodes with their starting positions. + void Init( const Vector &vPos ) + { + m_vPos = m_vPrevPos = m_vPredicted = vPos; + } + + Vector m_vPos; // At time t + Vector m_vPrevPos; // At time t - m_flTimeStep + Vector m_vPredicted; // Predicted position + }; + + class IHelper + { + public: + virtual void GetNodeForces( CNode *pNodes, int iNode, Vector *pAccel ) = 0; + virtual void ApplyConstraints( CNode *pNodes, int nNodes ) = 0; + }; + + +public: + + CSimplePhysics(); + + void Init( float flTimeStep ); + + void Simulate( + CNode *pNodes, + int nNodes, + IHelper *pHelper, + float dt, + float flDamp ); + + +private: + + double GetCurTime() { return m_flTimeStep * m_iCurTimeStep; } + + +private: + + double m_flPredictedTime; // (GetCurTime()-m_flTimeStep) <= m_flPredictedTime <= GetCurTime() + int m_iCurTimeStep; + + float m_flTimeStep; + float m_flTimeStepMul; // dt*dt*0.5 +}; + + +#endif // SIMPLE_PHYSICS_H diff --git a/public/smooth_average.h b/public/smooth_average.h new file mode 100644 index 0000000..af6e35a --- /dev/null +++ b/public/smooth_average.h @@ -0,0 +1,221 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SMOOTH_AVERAGE_H +#define SMOOTH_AVERAGE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "utldict.h" + + + +// Use this macro around any value, and it'll queue up the results given to it nTimes and +// provide a running average. +#define SMOOTH_AVERAGE( value, nCount ) CalcSmoothAverage( value, nCount, __FILE__, __LINE__ ) + + +// Same as their counterpart functions but they return more info in a CTimingInfo structure. +#define SMOOTH_AVERAGE_STRUCT( value, nCount ) CalcSmoothAverage_Struct( value, nCount, __FILE__, __LINE__ ) +#define SUM_OVER_TIME_INTERVAL_STRUCT( value, nSeconds ) SumOverTimeInterval_Struct( value, nSeconds, __FILE__, __LINE__ ) + + +template< class T > +class CTimingInfo +{ +public: + T m_AverageValue; // Note: this will be the SUM of the values if using SUM_OVER_TIME_INTERVAL. + + // The high and low points for m_AverageValue over the time interval. + T m_HighAverage; + T m_LowAverage; + + // The high and low points for the value itself over the time interval. + T m_HighValue; + T m_LowValue; +}; + + +template< class T > +class CAveragesInfo +{ +public: + class CEntry + { + public: + T m_Average; + T m_Value; + }; + +public: + CUtlVector< CEntry > m_Values; + int m_iCurValue; +}; + + +template< class T > +class CAveragesInfo_TimeBased +{ +public: + class CEntry + { + public: + CCycleCount m_Time; // When this sample was taken. + T m_Value; + T m_Average; + }; + + CUtlVector m_Values; +}; + + +#if 0 +template< class T > +inline CTimingInfo< T > CalcSmoothAverage_Struct( const T &value, int nTimes, const char *pFilename, int iLine ) +{ + // Find an entry at this file and line. + char fullStr[1024]; + Q_snprintf( fullStr, sizeof( fullStr ), "%s_%i", pFilename, iLine ); + + int index = s_SmoothAverages.Find( fullStr ); + CAveragesInfo *pInfo; + if ( index == s_SmoothAverages.InvalidIndex() ) + { + pInfo = new CAveragesInfo; + index = s_SmoothAverages.Insert( fullStr, pInfo ); + } + else + { + pInfo = (CAveragesInfo*)s_SmoothAverages[index]; + } + + // Add the new value. + int newValueIndex; + CAveragesInfo< T >::CEntry entry; + entry.m_Value = value; + if ( pInfo->m_Values.Count() < nTimes ) + { + newValueIndex = pInfo->m_Values.AddToTail( entry ); + pInfo->m_iCurValue = 0; + } + else + { + newValueIndex = pInfo->m_iCurValue; + pInfo->m_Values[pInfo->m_iCurValue] = entry; + pInfo->m_iCurValue = (pInfo->m_iCurValue+1) % pInfo->m_Values.Count(); + } + + CTimingInfo< T > info; + info.m_AverageValue = pInfo->m_Values[0].m_Value; + + info.m_HighAverage = pInfo->m_Values[0].m_Average; + info.m_LowAverage = pInfo->m_Values[0].m_Average; + + info.m_HighValue = pInfo->m_Values[0].m_Value; + info.m_LowValue = pInfo->m_Values[0].m_Value; + + for ( int i=1; i < pInfo->m_Values.Count(); i++ ) + { + if ( i != newValueIndex ) + { + info.m_HighAverage = max( pInfo->m_Values[i].m_Average, info.m_HighAverage ); + info.m_LowAverage = min( pInfo->m_Values[i].m_Average, info.m_LowAverage ); + } + + info.m_HighValue = max( pInfo->m_Values[i].m_Value, info.m_HighValue ); + info.m_LowValue = min( pInfo->m_Values[i].m_Value, info.m_LowValue ); + + info.m_AverageValue += pInfo->m_Values[i].m_Value; + } + + info.m_AverageValue /= pInfo->m_Values.Count(); + pInfo->m_Values[newValueIndex].m_Average = info.m_AverageValue; + return info; +} +#endif + +template< class T > +inline T CalcSmoothAverage( const T &value, int nTimes, const char *pFilename, int iLine ) +{ + CTimingInfo< T > info = CalcSmoothAverage_Struct( value, nTimes, pFilename, iLine ); + return info.m_AverageValue; +}; + + +template< class T > +inline CTimingInfo< T > SumOverTimeInterval_Struct( const T &value, float nSeconds, const char *pFilename, int iLine ) +{ + static CUtlDict< CAveragesInfo_TimeBased< T >*, int > s_SmoothAverages; + + char fullStr[1024]; + Q_snprintf( fullStr, sizeof( fullStr ), "%s_%i", pFilename, iLine ); + + int index = s_SmoothAverages.Find( fullStr ); + CAveragesInfo_TimeBased *pInfo; + if ( index == s_SmoothAverages.InvalidIndex() ) + { + pInfo = new CAveragesInfo_TimeBased; + index = s_SmoothAverages.Insert( fullStr, pInfo ); + } + else + { + pInfo = s_SmoothAverages[index]; + } + + // Get the current time now. + CCycleCount curTime; + curTime.Sample(); + + // Get rid of old samples. + while ( pInfo->m_Values.Count() > 0 && (curTime.GetSeconds() - pInfo->m_Values[0].m_Time.GetSeconds()) > nSeconds ) + pInfo->m_Values.Remove( 0 ); + + // Add on the new sample. + typename CAveragesInfo_TimeBased< T >::CEntry newEntry; + newEntry.m_Time = curTime; + newEntry.m_Value = value; + int newValueIndex = pInfo->m_Values.AddToTail( newEntry ); + + CTimingInfo< T > info; + info.m_AverageValue = pInfo->m_Values[0].m_Value; + + info.m_HighAverage = pInfo->m_Values[0].m_Average; + info.m_LowAverage = pInfo->m_Values[0].m_Average; + + info.m_HighValue = pInfo->m_Values[0].m_Value; + info.m_LowValue = pInfo->m_Values[0].m_Value; + + for ( int i=1; i < pInfo->m_Values.Count(); i++ ) + { + if ( i != newValueIndex ) + { + info.m_HighAverage = MAX( pInfo->m_Values[i].m_Average, info.m_HighAverage ); + info.m_LowAverage = MIN( pInfo->m_Values[i].m_Average, info.m_LowAverage ); + } + + info.m_HighValue = MAX( pInfo->m_Values[i].m_Value, info.m_HighValue ); + info.m_LowValue = MIN( pInfo->m_Values[i].m_Value, info.m_LowValue ); + + info.m_AverageValue += pInfo->m_Values[i].m_Value; + } + + pInfo->m_Values[newValueIndex].m_Average = info.m_AverageValue; + return info; +} + + +template< class T > +inline CTimingInfo< T > SumOverTimeInterval( const T &value, float nSeconds, const char *pFilename, int iLine ) +{ + CTimingInfo< T > info = SumOverTimeInterval_Struct( value, nSeconds, pFilename, iLine ); + return info.m_AverageValue; +} + + +#endif // SMOOTH_AVERAGE_H + diff --git a/public/soundchars.h b/public/soundchars.h new file mode 100644 index 0000000..fbb8ad9 --- /dev/null +++ b/public/soundchars.h @@ -0,0 +1,71 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef SOUNDCHARS_H +#define SOUNDCHARS_H +#ifdef _WIN32 +#pragma once +#endif + +#define CHAR_STREAM '*' // as one of 1st 2 chars in name, indicates streaming wav data +#define CHAR_USERVOX '?' // as one of 1st 2 chars in name, indicates user realtime voice data +#define CHAR_SENTENCE '!' // as one of 1st 2 chars in name, indicates sentence wav +#define CHAR_DRYMIX '#' // as one of 1st 2 chars in name, indicates wav bypasses dsp fx +#define CHAR_DOPPLER '>' // as one of 1st 2 chars in name, indicates doppler encoded stereo wav: left wav (incomming) and right wav (outgoing). +#define CHAR_DIRECTIONAL '<' // as one of 1st 2 chars in name, indicates stereo wav has direction cone: mix left wav (front facing) with right wav (rear facing) based on soundfacing direction +#define CHAR_DISTVARIANT '^' // as one of 1st 2 chars in name, indicates distance variant encoded stereo wav (left is close, right is far) +#define CHAR_OMNI '@' // as one of 1st 2 chars in name, indicates non-directional wav (default mono or stereo) +#define CHAR_SPATIALSTEREO ')' // as one of 1st 2 chars in name, indicates spatialized stereo wav +#define CHAR_DIRSTEREO '(' // as one of 1st 2 chars in name, indicates directional stereo wav (like doppler) +#define CHAR_FAST_PITCH '}' // as one of 1st 2 chars in name, forces low quality, non-interpolated pitch shift +#define CHAR_SUBTITLED '$' // as one of 1st 2 chars in name, indicates the subtitles are forced + +inline bool IsSoundChar(char c) +{ + bool b; + + b = ( c == CHAR_STREAM || c == CHAR_USERVOX || c == CHAR_SENTENCE || c == CHAR_DRYMIX || c == CHAR_OMNI || c== CHAR_DIRSTEREO ); + b = b || ( c == CHAR_DOPPLER || c == CHAR_DIRECTIONAL || c == CHAR_DISTVARIANT || c == CHAR_SPATIALSTEREO || c == CHAR_FAST_PITCH ); + b = b || ( c == CHAR_SUBTITLED ); + + return b; +} + +// return pointer to first valid character in file name +// by skipping over CHAR_STREAM...CHAR_DRYMIX + +inline char *PSkipSoundChars(const char *pch) +{ + char *pcht = (char *)pch; + + while ( 1 ) + { + if (!IsSoundChar(*pcht)) + break; + pcht++; + } + + return pcht; +} + + +inline bool TestSoundChar(const char *pch, char c) +{ + char *pcht = (char *)pch; + + while ( 1 ) + { + if (!IsSoundChar(*pcht)) + break; + if (*pcht == c) + return true; + pcht++; + } + + return false; +} + +#endif // SOUNDCHARS_H diff --git a/public/soundcombiner.cpp b/public/soundcombiner.cpp new file mode 100644 index 0000000..d92114d --- /dev/null +++ b/public/soundcombiner.cpp @@ -0,0 +1,823 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#include "cbase.h" +#include "isoundcombiner.h" +#include "sentence.h" +#include "filesystem.h" +#include "tier2/riff.h" +#include "tier1/utlbuffer.h" +#include "snd_audio_source.h" +#include "snd_wave_source.h" +#include "AudioWaveOutput.h" +#include "ifaceposersound.h" +#include "vstdlib/random.h" +#include "checksum_crc.h" + +#define WAVEOUTPUT_BITSPERCHANNEL 16 +#define WAVEOUTPUT_FREQUENCY 44100 + +class CSoundCombiner : public ISoundCombiner +{ +public: + CSoundCombiner() : + m_pWaveOutput( NULL ), + m_pOutRIFF( NULL ), + m_pOutIterator( NULL ) + { + m_szOutFile[ 0 ] = 0; + } + + virtual bool CombineSoundFiles( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ); + virtual bool IsCombinedFileChecksumValid( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ); + +private: + + struct CombinerWork + { + CombinerWork() : + sentence(), + duration( 0.0 ), + wave( 0 ), + mixer( 0 ), + entry( 0 ) + { + } + CSentence sentence; + float duration; + CAudioSource *wave; + CAudioMixer *mixer; + CombinerEntry *entry; + }; + + bool InternalCombineSoundFiles( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ); + bool VerifyFilesExist( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ); + bool CreateWorkList( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ); + + bool PerformSplicingOnWorkItems( IFileSystem *filesystem ); + void CleanupWork(); + + // .wav file utils + int ComputeBestNumChannels(); + void ParseSentence( CSentence& sentence, IterateRIFF &walk ); + bool LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io ); + bool LoadSentenceFromWavFile( char const *wavfile, CSentence& sentence ); + void StoreValveDataChunk( CSentence& sentence ); +// bool SaveSentenceToWavFile( char const *wavfile, CSentence& sentence ); + + bool InitSplicer( IFileSystem *filesystem, int samplerate, int numchannels, int bitspersample ); + bool LoadSpliceAudioSources(); + bool AppendSilence( int ¤tsample, float duration ); + bool AppendStereo16Data( short samples[ 2 ] ); + bool AppendWaveData( int& currentsample, CAudioSource *wave, CAudioMixer *mixer ); + void AddSentenceToCombined( float offset, CSentence& sentence ); + + unsigned int CheckSumWork( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ); + unsigned int ComputeChecksum(); + + CUtlVector< CombinerWork * > m_Work; + CSentence m_Combined; + + CAudioWaveOutput *m_pWaveOutput; + + OutFileRIFF *m_pOutRIFF; + IterateOutputRIFF *m_pOutIterator; + + int m_nSampleRate; + int m_nNumChannels; + int m_nBitsPerSample; + int m_nBytesPerSample; + char m_szOutFile[ MAX_PATH ]; +}; + +static CSoundCombiner g_SoundCombiner; +ISoundCombiner *soundcombiner = &g_SoundCombiner; + +bool CSoundCombiner::CreateWorkList( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ) +{ + m_Work.RemoveAll(); + + int c = info.Count(); + for ( int i = 0; i < c; ++i ) + { + CombinerWork *workitem = new CombinerWork(); + + char fullpath[ MAX_PATH ]; + Q_strncpy( fullpath, info[ i ].wavefile, sizeof( fullpath ) ); + filesystem->GetLocalPath( info[ i ].wavefile, fullpath, sizeof( fullpath ) ); + + if ( !LoadSentenceFromWavFile( fullpath, workitem->sentence ) ) + { + Warning( "CSoundCombiner::CreateWorkList couldn't load %s for work item (%d)\n", + fullpath, i ); + return false; + } + + workitem->entry = &info[ i ]; + + m_Work.AddToTail( workitem ); + } + + return true; +} + +void CSoundCombiner::CleanupWork() +{ + int c = m_Work.Count(); + for ( int i = 0; i < c; ++i ) + { + CombinerWork *workitem = m_Work[ i ]; + delete workitem->mixer; + delete workitem->wave; + + delete m_Work[ i ]; + } + m_Work.RemoveAll(); + + delete m_pOutIterator; + m_pOutIterator = NULL; + + delete m_pOutRIFF; + m_pOutRIFF = NULL; +} + +bool CSoundCombiner::InternalCombineSoundFiles( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ) +{ + Q_strncpy( m_szOutFile, outfile, sizeof( m_szOutFile ) ); + if ( info.Count() <= 0 ) + { + Warning( "CSoundCombiner::InternalCombineSoundFiles: work item count is zero\n" ); + return false; + } + + if ( !VerifyFilesExist( filesystem, info ) ) + { + return false; + } + + if ( !CreateWorkList( filesystem, info ) ) + { + return false; + } + + PerformSplicingOnWorkItems( filesystem ); + + return true; +} + +bool CSoundCombiner::CombineSoundFiles( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ) +{ + bool bret = InternalCombineSoundFiles( filesystem, outfile, info ); + CleanupWork(); + return bret; +} + +unsigned int CSoundCombiner::ComputeChecksum() +{ + CRC32_t crc; + CRC32_Init( &crc ); + + int c = m_Work.Count(); + for ( int i = 0; i < c; ++i ) + { + CombinerWork *curitem = m_Work[ i ]; + unsigned int chk = curitem->sentence.ComputeDataCheckSum(); + + // Msg( " %i -> sentence %u, startoffset %f fn %s\n", + // i, chk, curitem->entry->startoffset, curitem->entry->wavefile ); + + CRC32_ProcessBuffer( &crc, &chk, sizeof( unsigned long ) ); + CRC32_ProcessBuffer( &crc, &curitem->entry->startoffset, sizeof( float ) ); + CRC32_ProcessBuffer( &crc, curitem->entry->wavefile, Q_strlen( curitem->entry->wavefile ) ); + } + + CRC32_Final( &crc ); + return ( unsigned int )crc; +} + +unsigned int CSoundCombiner::CheckSumWork( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ) +{ + if ( info.Count() <= 0 ) + { + Warning( "CSoundCombiner::CheckSumWork: work item count is zero\n" ); + return 0; + } + + if ( !VerifyFilesExist( filesystem, info ) ) + { + return 0; + } + + if ( !CreateWorkList( filesystem, info ) ) + { + return 0; + } + + // Checkum work items + unsigned int checksum = ComputeChecksum(); + + return checksum; +} + +bool CSoundCombiner::IsCombinedFileChecksumValid( IFileSystem *filesystem, char const *outfile, CUtlVector< CombinerEntry >& info ) +{ + unsigned int computedChecksum = CheckSumWork( filesystem, info ); + + char fullpath[ MAX_PATH ]; + Q_strncpy( fullpath, outfile, sizeof( fullpath ) ); + filesystem->GetLocalPath( outfile, fullpath, sizeof( fullpath ) ); + + CSentence sentence; + + bool valid = false; + + if ( LoadSentenceFromWavFile( fullpath, sentence ) ) + { + unsigned int diskFileEmbeddedChecksum = sentence.GetDataCheckSum(); + + valid = computedChecksum == diskFileEmbeddedChecksum; + + if ( !valid ) + { + Warning( " checksum computed %u, disk %u\n", + computedChecksum, diskFileEmbeddedChecksum ); + } + } + else + { + Warning( "CSoundCombiner::IsCombinedFileChecksumValid: Unabled to load %s\n", fullpath ); + } + + CleanupWork(); + return valid; +} + +bool CSoundCombiner::VerifyFilesExist( IFileSystem *filesystem, CUtlVector< CombinerEntry >& info ) +{ + int c = info.Count(); + for ( int i = 0 ; i < c; ++i ) + { + CombinerEntry& entry = info[ i ]; + if ( !filesystem->FileExists( entry.wavefile ) ) + { + Warning( "CSoundCombiner::VerifyFilesExist: missing file %s\n", entry.wavefile ); + return false; + } + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Implements the RIFF i/o interface on stdio +//----------------------------------------------------------------------------- +class StdIOReadBinary : public IFileReadBinary +{ +public: + int open( const char *pFileName ) + { + return (int)filesystem->Open( pFileName, "rb" ); + } + + int read( void *pOutput, int size, int file ) + { + if ( !file ) + return 0; + + return filesystem->Read( pOutput, size, (FileHandle_t)file ); + } + + void seek( int file, int pos ) + { + if ( !file ) + return; + + filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); + } + + unsigned int tell( int file ) + { + if ( !file ) + return 0; + + return filesystem->Tell( (FileHandle_t)file ); + } + + unsigned int size( int file ) + { + if ( !file ) + return 0; + + return filesystem->Size( (FileHandle_t)file ); + } + + void close( int file ) + { + if ( !file ) + return; + + filesystem->Close( (FileHandle_t)file ); + } +}; + +class StdIOWriteBinary : public IFileWriteBinary +{ +public: + int create( const char *pFileName ) + { + return (int)filesystem->Open( pFileName, "wb" ); + } + + int write( void *pData, int size, int file ) + { + return filesystem->Write( pData, size, (FileHandle_t)file ); + } + + void close( int file ) + { + filesystem->Close( (FileHandle_t)file ); + } + + void seek( int file, int pos ) + { + filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD ); + } + + unsigned int tell( int file ) + { + return filesystem->Tell( (FileHandle_t)file ); + } +}; + +static StdIOReadBinary io_in; +static StdIOWriteBinary io_out; + +#define RIFF_WAVE MAKEID('W','A','V','E') +#define WAVE_FMT MAKEID('f','m','t',' ') +#define WAVE_DATA MAKEID('d','a','t','a') +#define WAVE_FACT MAKEID('f','a','c','t') +#define WAVE_CUE MAKEID('c','u','e',' ') + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &walk - +//----------------------------------------------------------------------------- +void CSoundCombiner::ParseSentence( CSentence& sentence, IterateRIFF &walk ) +{ + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + buf.EnsureCapacity( walk.ChunkSize() ); + walk.ChunkRead( buf.Base() ); + buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() ); + + sentence.InitFromDataChunk( buf.Base(), buf.TellPut() ); +} + +bool CSoundCombiner::LoadSentenceFromWavFileUsingIO( char const *wavfile, CSentence& sentence, IFileReadBinary& io ) +{ + sentence.Reset(); + + InFileRIFF riff( wavfile, io ); + + // UNDONE: Don't use printf to handle errors + if ( riff.RIFFName() != RIFF_WAVE ) + { + return false; + } + + // set up the iterator for the whole file (root RIFF is a chunk) + IterateRIFF walk( riff, riff.RIFFSize() ); + + // This chunk must be first as it contains the wave's format + // break out when we've parsed it + bool found = false; + while ( walk.ChunkAvailable() && !found ) + { + switch( walk.ChunkName() ) + { + case WAVE_VALVEDATA: + { + found = true; + CSoundCombiner::ParseSentence( sentence, walk ); + } + break; + } + walk.ChunkNext(); + } + + return true; +} + +bool CSoundCombiner::LoadSentenceFromWavFile( char const *wavfile, CSentence& sentence ) +{ + return CSoundCombiner::LoadSentenceFromWavFileUsingIO( wavfile, sentence, io_in ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : store - +//----------------------------------------------------------------------------- +void CSoundCombiner::StoreValveDataChunk( CSentence& sentence ) +{ + // Buffer and dump data + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + sentence.SaveToBuffer( buf ); + + // Copy into store + m_pOutIterator->ChunkWriteData( buf.Base(), buf.TellPut() ); +} + +/* +bool CSoundCombiner::SaveSentenceToWavFile( char const *wavfile, CSentence& sentence ) +{ + char tempfile[ 512 ]; + + Q_StripExtension( wavfile, tempfile, sizeof( tempfile ) ); + Q_DefaultExtension( tempfile, ".tmp", sizeof( tempfile ) ); + + if ( filesystem->FileExists( tempfile, NULL ) ) + { + filesystem->RemoveFile( tempfile, NULL ); + } + + if ( !filesystem->IsFileWritable( wavfile ) ) + { + Msg( "%s is not writable, can't save sentence data to file\n", wavfile ); + return false; + } + + // Rename original wavfile to temp + filesystem->RenameFile( wavfile, tempfile, NULL ); + + // NOTE: Put this in it's own scope so that the destructor for outfileRFF actually closes the file!!!! + { + // Read from Temp + InFileRIFF riff( tempfile, io_in ); + Assert( riff.RIFFName() == RIFF_WAVE ); + + // set up the iterator for the whole file (root RIFF is a chunk) + IterateRIFF walk( riff, riff.RIFFSize() ); + + // And put data back into original wavfile by name + OutFileRIFF riffout( wavfile, io_out ); + + IterateOutputRIFF store( riffout ); + + bool wordtrackwritten = false; + + // Walk input chunks and copy to output + while ( walk.ChunkAvailable() ) + { + m_pOutIterator->ChunkStart( walk.ChunkName() ); + + switch ( walk.ChunkName() ) + { + case WAVE_VALVEDATA: + { + // Overwrite data + CSoundCombiner::StoreValveDataChunk( sentence ); + wordtrackwritten = true; + } + break; + default: + m_pOutIterator->CopyChunkData( walk ); + break; + } + + m_pOutIterator->ChunkFinish(); + + walk.ChunkNext(); + } + + // If we didn't write it above, write it now + if ( !wordtrackwritten ) + { + m_pOutIterator->ChunkStart( WAVE_VALVEDATA ); + CSoundCombiner::StoreValveDataChunk( sentence ); + m_pOutIterator->ChunkFinish(); + } + } + + // Remove temp file + filesystem->RemoveFile( tempfile, NULL ); + + return true; +} +*/ + +typedef struct channel_s +{ + int leftvol; + int rightvol; + int rleftvol; + int rrightvol; + float pitch; +} channel_t; + +bool CSoundCombiner::InitSplicer( IFileSystem *filesystem, int samplerate, int numchannels, int bitspersample ) +{ + m_nSampleRate = samplerate; + m_nNumChannels = numchannels; + m_nBitsPerSample = bitspersample; + m_nBytesPerSample = bitspersample >> 3; + + m_pWaveOutput = ( CAudioWaveOutput * )sound->GetAudioOutput(); + if ( !m_pWaveOutput ) + { + Warning( "CSoundCombiner::InitSplicer m_pWaveOutput == NULL\n" ); + return false; + } + + // Make sure the directory exists + char basepath[ 512 ]; + Q_ExtractFilePath( m_szOutFile, basepath, sizeof( basepath ) ); + filesystem->CreateDirHierarchy( basepath, "GAME" ); + + // Create out put file + m_pOutRIFF = new OutFileRIFF( m_szOutFile, io_out ); + if ( !m_pOutRIFF ) + { + Warning( "CSoundCombiner::InitSplicer m_pOutRIFF == NULL\n" ); + return false; + } + + // Create output iterator + m_pOutIterator = new IterateOutputRIFF( *m_pOutRIFF ); + if ( !m_pOutIterator ) + { + Warning( "CSoundCombiner::InitSplicer m_pOutIterator == NULL\n" ); + return false; + } + + WAVEFORMATEX format; + format.cbSize = sizeof( format ); + + format.wFormatTag = WAVE_FORMAT_PCM; + format.nAvgBytesPerSec = m_nSampleRate * m_nNumChannels * m_nBytesPerSample; + format.nChannels = m_nNumChannels; + format.wBitsPerSample = m_nBitsPerSample; + format.nSamplesPerSec = m_nSampleRate; + format.nBlockAlign = 1; + + // Always store the format chunk first + m_pOutIterator->ChunkWrite( WAVE_FMT, &format, sizeof( format ) ); + + return true; +} + +bool CSoundCombiner::LoadSpliceAudioSources() +{ + int c = m_Work.Count(); + for ( int i = 0; i < c; ++i ) + { + CombinerWork *item = m_Work[ i ]; + + CAudioSource *wave = sound->LoadSound( item->entry->wavefile ); + if ( !wave ) + { + Warning( "CSoundCombiner::LoadSpliceAudioSources LoadSound failed '%s'\n", item->entry->wavefile ); + return false; + } + + CAudioMixer *pMixer = wave->CreateMixer(); + if ( !pMixer ) + { + Warning( "CSoundCombiner::LoadSpliceAudioSources CreateMixer failed '%s'\n", item->entry->wavefile ); + return false; + } + + item->wave = wave; + item->mixer = pMixer; + item->duration = wave->GetRunningLength(); + } + + return true; +} + +bool CSoundCombiner::AppendSilence( int ¤tsample, float duration ) +{ + int numSamples = duration * m_nSampleRate; + +#define MOTION_RANGE 150 +#define MOTION_MAXSTEP 20 + int currentValue = 32767; + int maxValue = currentValue + ( MOTION_RANGE / 2 ); + int minValue = currentValue - ( MOTION_RANGE / 2 ); + + short samples[ 2 ]; + + while ( --numSamples >= 0 ) + { + currentValue += random->RandomInt( -MOTION_MAXSTEP, MOTION_MAXSTEP ); + currentValue = min( maxValue, currentValue ); + currentValue = max( minValue, currentValue ); + + // Downsample to 0 65556 range + short s = (float)currentValue / 32768.0f; + + samples[ 0 ] = s; + samples[ 1 ] = s; + + AppendStereo16Data( samples ); + } + + return true; +} + +bool CSoundCombiner::AppendStereo16Data( short samples[ 2 ] ) +{ +// Convert from 16 bit, 2 channels to output size + if ( m_nNumChannels == 1 ) + { + if ( m_nBytesPerSample == 1 ) + { + // Convert to 8 bit mono + // left + right (2 channels ) * 16 bits + float s1 = (float)( samples[ 0 ] >> 8 ); + float s2 = (float)( samples[ 1 ] >> 8 ); + + float avg = ( s1 + s2 ) * 0.5f; + avg = clamp( avg, -127.0f, 127.0f ); + byte chopped = (byte)( avg+ 127 ); + + m_pOutIterator->ChunkWriteData( &chopped, sizeof( byte ) ); + } + else if ( m_nBytesPerSample == 2 ) + { + // Conver to 16 bit mono + float s1 = (float)( samples[ 0 ] ); + float s2 = (float)( samples[ 1 ] ); + + float avg = ( s1 + s2 ) * 0.5f; + unsigned short chopped = (unsigned short)( avg ); + + m_pOutIterator->ChunkWriteData( &chopped, sizeof( unsigned short ) ); + } + else + { + Assert( 0 ); + return false; + } + } + else if ( m_nNumChannels == 2 ) + { + if ( m_nBytesPerSample == 1 ) + { + // Convert to 8 bit stereo + // left + right (2 channels ) * 16 bits + float s1 = (float)( samples[ 0 ] >> 8 ); + float s2 = (float)( samples[ 1 ] >> 8 ); + + s1 = clamp( s1, -127.0f, 127.0f ); + s2 = clamp( s2, -127.0f, 127.0f ); + + byte chopped1 = (byte)( s1 + 127.0f ); + byte chopped2 = (byte)( s2 + 127.0f ); + + m_pOutIterator->ChunkWriteData( &chopped1, sizeof( byte ) ); + m_pOutIterator->ChunkWriteData( &chopped2, sizeof( byte ) ); + } + else if ( m_nBytesPerSample == 2 ) + { + // Leave as 16 bit stereo + // Directly store values + m_pOutIterator->ChunkWriteData( &samples[ 0 ], sizeof( unsigned short ) ); + m_pOutIterator->ChunkWriteData( &samples[ 1 ], sizeof( unsigned short ) ); + } + else + { + Assert( 0 ); + return false; + } + } + else + { + Assert( 0 ); + return false; + } + + return true; +} + +bool CSoundCombiner::AppendWaveData( int& currentsample, CAudioSource *wave, CAudioMixer *mixer ) +{ + // need a bit of space + short samples[ 2 ]; + channel_t channel; + memset( &channel, 0, sizeof( channel ) ); + channel.leftvol = 255; + channel.rightvol = 255; + channel.pitch = 1.0; + + while ( 1 ) + { + m_pWaveOutput->m_audioDevice.MixBegin(); + + if ( !mixer->MixDataToDevice( &m_pWaveOutput->m_audioDevice, &channel, currentsample, 1, wave->SampleRate(), true ) ) + break; + + m_pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, 1 ); + + currentsample = mixer->GetSamplePosition(); + + AppendStereo16Data( samples ); + } + + return true; +} + +int CSoundCombiner::ComputeBestNumChannels() +{ + // We prefer mono output unless one of the source wav files is stereo, then we'll do stereo output + int c = m_Work.Count(); + for ( int i = 0; i < c; ++i ) + { + CombinerWork *curitem = m_Work[ i ]; + + if ( curitem->wave->GetNumChannels() == 2 ) + { + return 2; + } + } + return 1; +} + +bool CSoundCombiner::PerformSplicingOnWorkItems( IFileSystem *filesystem ) +{ + if ( !LoadSpliceAudioSources() ) + { + return false; + } + + int bestNumChannels = ComputeBestNumChannels(); + int bitsPerChannel = WAVEOUTPUT_BITSPERCHANNEL; + + // Pull in data and write it out + if ( !InitSplicer( filesystem, WAVEOUTPUT_FREQUENCY, bestNumChannels, bitsPerChannel ) ) + { + return false; + } + + m_pOutIterator->ChunkStart( WAVE_DATA ); + + float timeoffset = 0.0f; + + m_Combined.Reset(); + m_Combined.SetText( "" ); + + int c = m_Work.Count(); + for ( int i = 0; i < c; ++i ) + { + int currentsample = 0; + + CombinerWork *curitem = m_Work[ i ]; + CombinerWork *nextitem = NULL; + if ( i != c - 1 ) + { + nextitem = m_Work[ i + 1 ]; + } + + float duration = curitem->duration; + + AppendWaveData( currentsample, curitem->wave, curitem->mixer ); + + AddSentenceToCombined( timeoffset, curitem->sentence ); + + timeoffset += duration; + + if ( nextitem != NULL ) + { + float nextstart = nextitem->entry->startoffset; + float silence_time = nextstart - timeoffset; + + AppendSilence( currentsample, silence_time ); + + timeoffset += silence_time; + } + } + + m_pOutIterator->ChunkFinish(); + + // Checksum the work items + unsigned int checksum = ComputeChecksum(); + + // Make sure the checksum is embedded in the data file + m_Combined.SetDataCheckSum( checksum ); + + // Msg( " checksum computed %u\n", checksum ); + + m_pOutIterator->ChunkStart( WAVE_VALVEDATA ); + StoreValveDataChunk( m_Combined ); + m_pOutIterator->ChunkFinish(); + + + return true; +} + +void CSoundCombiner::AddSentenceToCombined( float offset, CSentence& sentence ) +{ + m_Combined.Append( offset, sentence ); +} diff --git a/public/soundflags.h b/public/soundflags.h new file mode 100644 index 0000000..f1d4563 --- /dev/null +++ b/public/soundflags.h @@ -0,0 +1,162 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SOUNDFLAGS_H +#define SOUNDFLAGS_H + +#if defined( _WIN32 ) +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// channels +//----------------------------------------------------------------------------- +enum +{ + CHAN_REPLACE = -1, + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_STREAM = 5, // allocate stream channel from the static or dynamic area + CHAN_STATIC = 6, // allocate channel from the static area + CHAN_VOICE_BASE = 7, // allocate channel for network voice data +}; + +enum +{ + CHAN_USER_BASE = (CHAN_VOICE_BASE+128) // Anything >= this number is allocated to game code. +}; + +//----------------------------------------------------------------------------- +// common volume values +//----------------------------------------------------------------------------- +#define VOL_NORM 1.0f + + +//----------------------------------------------------------------------------- +// common attenuation values +//----------------------------------------------------------------------------- +#define ATTN_NONE 0.0f +#define ATTN_NORM 0.8f +#define ATTN_IDLE 2.0f +#define ATTN_STATIC 1.25f +#define ATTN_RICOCHET 1.5f + +// HL2 world is 8x bigger now! We want to hear gunfire from farther. +// Don't change this without consulting Kelly or Wedge (sjb). +#define ATTN_GUNFIRE 0.27f + +enum soundlevel_t +{ + SNDLVL_NONE = 0, + + SNDLVL_20dB = 20, // rustling leaves + SNDLVL_25dB = 25, // whispering + SNDLVL_30dB = 30, // library + SNDLVL_35dB = 35, + SNDLVL_40dB = 40, + SNDLVL_45dB = 45, // refrigerator + + SNDLVL_50dB = 50, // 3.9 // average home + SNDLVL_55dB = 55, // 3.0 + + SNDLVL_IDLE = 60, // 2.0 + SNDLVL_60dB = 60, // 2.0 // normal conversation, clothes dryer + + SNDLVL_65dB = 65, // 1.5 // washing machine, dishwasher + SNDLVL_STATIC = 66, // 1.25 + + SNDLVL_70dB = 70, // 1.0 // car, vacuum cleaner, mixer, electric sewing machine + + SNDLVL_NORM = 75, + SNDLVL_75dB = 75, // 0.8 // busy traffic + + SNDLVL_80dB = 80, // 0.7 // mini-bike, alarm clock, noisy restaurant, office tabulator, outboard motor, passing snowmobile + SNDLVL_TALKING = 80, // 0.7 + SNDLVL_85dB = 85, // 0.6 // average factory, electric shaver + SNDLVL_90dB = 90, // 0.5 // screaming child, passing motorcycle, convertible ride on frw + SNDLVL_95dB = 95, + SNDLVL_100dB = 100, // 0.4 // subway train, diesel truck, woodworking shop, pneumatic drill, boiler shop, jackhammer + SNDLVL_105dB = 105, // helicopter, power mower + SNDLVL_110dB = 110, // snowmobile drvrs seat, inboard motorboat, sandblasting + SNDLVL_120dB = 120, // auto horn, propeller aircraft + SNDLVL_130dB = 130, // air raid siren + + SNDLVL_GUNFIRE = 140, // 0.27 // THRESHOLD OF PAIN, gunshot, jet engine + SNDLVL_140dB = 140, // 0.2 + + SNDLVL_150dB = 150, // 0.2 + + SNDLVL_180dB = 180, // rocket launching + + // NOTE: Valid soundlevel_t values are 0-255. + // 256-511 are reserved for sounds using goldsrc compatibility attenuation. +}; + +#define MAX_SNDLVL_BITS 9 // Used to encode 0-255 for regular soundlevel_t's and 256-511 for goldsrc-compatible ones. +#define MIN_SNDLVL_VALUE 0 +#define MAX_SNDLVL_VALUE ((1< 50) ? (20.0f / (float)(a - 50)) : ( (a == 0) ? (0.0f) : (4.0f) ) ) + +// This is a limit due to network encoding. +// It encodes attenuation * 64 in 8 bits, so the maximum is (255 / 64) +#define MAX_ATTENUATION 3.98f + +//----------------------------------------------------------------------------- +// Flags to be or-ed together for the iFlags field +//----------------------------------------------------------------------------- +enum SoundFlags_t +{ + SND_NOFLAGS = 0, // to keep the compiler happy + SND_CHANGE_VOL = (1<<0), // change sound vol + SND_CHANGE_PITCH = (1<<1), // change sound pitch + SND_STOP = (1<<2), // stop the sound + SND_SPAWNING = (1<<3), // we're spawning, used in some cases for ambients + // not sent over net, only a param between dll and server. + SND_DELAY = (1<<4), // sound has an initial delay + SND_STOP_LOOPING = (1<<5), // stop all looping sounds on the entity. + SND_SPEAKER = (1<<6), // being played again by a microphone through a speaker + + SND_SHOULDPAUSE = (1<<7), // this sound should be paused if the game is paused + SND_IGNORE_PHONEMES = (1<<8), + SND_IGNORE_NAME = (1<<9), // used to change all sounds emitted by an entity, regardless of scriptname +}; + +#define SND_FLAG_BITS_ENCODE 9 + +#define MAX_SOUND_INDEX_BITS 13 +#define MAX_SOUNDS (1<name ) \ + buffer.WriteOneBit(0); \ + else \ + { \ + buffer.WriteOneBit(1); \ + buffer.WriteUBitLong( name, length ); \ + } + +#define READ_DELTA_UINT( name, length ) \ + if ( buffer.ReadOneBit() != 0 ) \ + { name = buffer.ReadUBitLong( length ); }\ + else { name = delta->name; } + +#define WRITE_DELTA_SINT( name, length ) \ + if ( name == delta->name ) \ + buffer.WriteOneBit(0); \ + else \ + { \ + buffer.WriteOneBit(1); \ + buffer.WriteSBitLong( name, length ); \ + } + +#define WRITE_DELTA_SINT_SCALE( name, scale, length ) \ + if ( name == delta->name ) \ + buffer.WriteOneBit(0); \ + else \ + { \ + buffer.WriteOneBit(1); \ + buffer.WriteSBitLong( name / scale, length ); \ + } + +#define READ_DELTA_SINT( name, length ) \ + if ( buffer.ReadOneBit() != 0 ) \ + { name = buffer.ReadSBitLong( length ); } \ + else { name = delta->name; } + +#define READ_DELTA_SINT_SCALE( name, scale, length ) \ + if ( buffer.ReadOneBit() != 0 ) \ + { name = scale * buffer.ReadSBitLong( length ); } \ + else { name = delta->name; } + +#define SOUND_SEQNUMBER_BITS 10 +#define SOUND_SEQNUMBER_MASK ( (1<nEntityIndex ) + { + buffer.WriteOneBit( 0 ); + } + else + { + buffer.WriteOneBit( 1 ); + + if ( nEntityIndex <= 31) + { + buffer.WriteOneBit( 1 ); + buffer.WriteUBitLong( nEntityIndex, 5 ); + } + else + { + buffer.WriteOneBit( 0 ); + buffer.WriteUBitLong( nEntityIndex, MAX_EDICT_BITS ); + } + } + + WRITE_DELTA_UINT( nSoundNum, MAX_SOUND_INDEX_BITS ); + + WRITE_DELTA_UINT( nFlags, SND_FLAG_BITS_ENCODE ); + + WRITE_DELTA_UINT( nChannel, 3 ); + + buffer.WriteOneBit( bIsAmbient?1:0 ); + buffer.WriteOneBit( bIsSentence?1:0 ); // NOTE: SND_STOP behavior is different depending on this flag + + if ( nFlags != SND_STOP ) + { + if ( nSequenceNumber == delta->nSequenceNumber ) + { + // didn't change, most often case + buffer.WriteOneBit( 1 ); + } + else if ( nSequenceNumber == (delta->nSequenceNumber+1) ) + { + // increased by one + buffer.WriteOneBit( 0 ); + buffer.WriteOneBit( 1 ); + } + else + { + // send full seqnr + buffer.WriteUBitLong( 0, 2 ); // 2 zero bits + buffer.WriteUBitLong( nSequenceNumber, SOUND_SEQNUMBER_BITS ); + } + + if ( fVolume == delta->fVolume ) + { + buffer.WriteOneBit( 0 ); + } + else + { + buffer.WriteOneBit( 1 ); + buffer.WriteUBitLong( (unsigned int)(fVolume*127.0f), 7 ); + } + + WRITE_DELTA_UINT( Soundlevel, MAX_SNDLVL_BITS ); + + WRITE_DELTA_UINT( nPitch, 8 ); + + float delayValue = fDelay; + if ( (nFlags & SND_DELAY) && fTickTime != finalTickTime ) + { + delayValue += fTickTime - finalTickTime; + } + if ( delayValue == delta->fDelay ) + { + buffer.WriteOneBit( 0 ); + } + else + { + buffer.WriteOneBit( 1 ); + +#if SEND_SOUND_TIME + buffer.WriteFloat( delayValue ); +#else + // skipahead works in 10 ms increments + // bias results so that we only incur the precision loss on relatively large skipaheads + delayValue += SOUND_DELAY_OFFSET; + + // Convert to msecs + int iDelay = delayValue * 1000.0f; + + iDelay = clamp( iDelay, (int)(-10 * MAX_SOUND_DELAY_MSEC), (int)(MAX_SOUND_DELAY_MSEC) ); + + if ( iDelay < 0 ) + { + iDelay /=10; + } + + buffer.WriteSBitLong( iDelay , MAX_SOUND_DELAY_MSEC_ENCODE_BITS ); +#endif + } + + // don't transmit sounds with high precision + WRITE_DELTA_SINT_SCALE( vOrigin.x, 8.0f, COORD_INTEGER_BITS - 2 ); + WRITE_DELTA_SINT_SCALE( vOrigin.y, 8.0f, COORD_INTEGER_BITS - 2 ); + WRITE_DELTA_SINT_SCALE( vOrigin.z, 8.0f, COORD_INTEGER_BITS - 2 ); + + WRITE_DELTA_SINT( nSpeakerEntity, MAX_EDICT_BITS + 1 ); + } + else + { + ClearStopFields(); + } + }; + + void ReadDelta( SoundInfo_t *delta, bf_read &buffer) + { + if ( !buffer.ReadOneBit() ) + { + nEntityIndex = delta->nEntityIndex; + } + else + { + if ( buffer.ReadOneBit() ) + { + nEntityIndex = buffer.ReadUBitLong( 5 ); + } + else + { + nEntityIndex = buffer.ReadUBitLong( MAX_EDICT_BITS ); + } + } + + READ_DELTA_UINT( nSoundNum, MAX_SOUND_INDEX_BITS ); + + READ_DELTA_UINT( nFlags, SND_FLAG_BITS_ENCODE ); + + READ_DELTA_UINT( nChannel, 3 ); + + bIsAmbient = buffer.ReadOneBit() != 0; + bIsSentence = buffer.ReadOneBit() != 0; // NOTE: SND_STOP behavior is different depending on this flag + + if ( nFlags != SND_STOP ) + { + if ( buffer.ReadOneBit() != 0 ) + { + nSequenceNumber = delta->nSequenceNumber; + } + else if ( buffer.ReadOneBit() != 0 ) + { + nSequenceNumber = delta->nSequenceNumber + 1; + } + else + { + nSequenceNumber = buffer.ReadUBitLong( SOUND_SEQNUMBER_BITS ); + } + + if ( buffer.ReadOneBit() != 0 ) + { + fVolume = (float)buffer.ReadUBitLong( 7 )/127.0f; + } + else + { + fVolume = delta->fVolume; + } + + if ( buffer.ReadOneBit() != 0 ) + { + Soundlevel = (soundlevel_t)buffer.ReadUBitLong( MAX_SNDLVL_BITS ); + } + else + { + Soundlevel = delta->Soundlevel; + } + + READ_DELTA_UINT( nPitch, 8 ); + + + if ( buffer.ReadOneBit() != 0 ) + { +#if SEND_SOUND_TIME + fDelay = buffer.ReadFloat(); +#else + // Up to 4096 msec delay + fDelay = (float)buffer.ReadSBitLong( MAX_SOUND_DELAY_MSEC_ENCODE_BITS ) / 1000.0f; ; + + if ( fDelay < 0 ) + { + fDelay *= 10.0f; + } + // bias results so that we only incur the precision loss on relatively large skipaheads + fDelay -= SOUND_DELAY_OFFSET; +#endif + } + else + { + fDelay = delta->fDelay; + } + + READ_DELTA_SINT_SCALE( vOrigin.x, 8.0f, COORD_INTEGER_BITS - 2 ); + READ_DELTA_SINT_SCALE( vOrigin.y, 8.0f, COORD_INTEGER_BITS - 2 ); + READ_DELTA_SINT_SCALE( vOrigin.z, 8.0f, COORD_INTEGER_BITS - 2 ); + + READ_DELTA_SINT( nSpeakerEntity, MAX_EDICT_BITS + 1 ); + } + else + { + ClearStopFields(); + } + } +}; + +struct SpatializationInfo_t +{ + typedef enum + { + SI_INCREATION = 0, + SI_INSPATIALIZATION + } SPATIALIZATIONTYPE; + + // Inputs + SPATIALIZATIONTYPE type; + // Info about the sound, channel, origin, direction, etc. + SoundInfo_t info; + + // Requested Outputs ( NULL == not requested ) + Vector *pOrigin; + QAngle *pAngles; + float *pflRadius; +}; +#pragma pack() + +#endif // SOUNDINFO_H diff --git a/public/soundsystem/isoundsystem.h b/public/soundsystem/isoundsystem.h new file mode 100644 index 0000000..240561d --- /dev/null +++ b/public/soundsystem/isoundsystem.h @@ -0,0 +1,70 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef ISOUNDSYSTEM_H +#define ISOUNDSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/iappsystem.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IAudioDevice; +class CAudioSource; +class CAudioMixer; + + +//----------------------------------------------------------------------------- +// Sound handle +//----------------------------------------------------------------------------- +typedef unsigned short AudioSourceHandle_t; +enum +{ + AUDIOSOURCEHANDLE_INVALID = (AudioSourceHandle_t)~0 +}; + + +//----------------------------------------------------------------------------- +// Flags for FindAudioSource +//----------------------------------------------------------------------------- +enum FindAudioSourceFlags_t +{ + FINDAUDIOSOURCE_NODELAY = 0x1, + FINDAUDIOSOURCE_PREFETCH = 0x2, + FINDAUDIOSOURCE_PLAYONCE = 0x4, +}; + + +//----------------------------------------------------------------------------- +// Purpose: DLL interface for low-level sound utilities +//----------------------------------------------------------------------------- +#define SOUNDSYSTEM_INTERFACE_VERSION "SoundSystem001" + +abstract_class ISoundSystem : public IAppSystem +{ +public: + virtual void Update( float time ) = 0; + virtual void Flush( void ) = 0; + + virtual CAudioSource *FindOrAddSound( const char *filename ) = 0; + virtual CAudioSource *LoadSound( const char *wavfile ) = 0; + + virtual void PlaySound( CAudioSource *source, float volume, CAudioMixer **ppMixer ) = 0; + + virtual bool IsSoundPlaying( CAudioMixer *pMixer ) = 0; + virtual CAudioMixer *FindMixer( CAudioSource *source ) = 0; + + virtual void StopAll( void ) = 0; + virtual void StopSound( CAudioMixer *mixer ) = 0; +}; + + + +#endif // ISOUNDSYSTEM_H diff --git a/public/soundsystem/snd_audio_source.h b/public/soundsystem/snd_audio_source.h new file mode 100644 index 0000000..a455581 --- /dev/null +++ b/public/soundsystem/snd_audio_source.h @@ -0,0 +1,101 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef SND_AUDIO_SOURCE_H +#define SND_AUDIO_SOURCE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CAudioSource; +class IAudioDevice; +struct channel_t; + + +//----------------------------------------------------------------------------- +// Purpose: This is an instance of an audio source. +// Mixers are attached to channels and reference an audio source. +// Mixers are specific to the sample format and source format. +// Mixers are never re-used, so they can track instance data like +// sample position, fractional sample, stream cache, faders, etc. +//----------------------------------------------------------------------------- +abstract_class CAudioMixer +{ +public: + virtual ~CAudioMixer( void ) {} + + // UNDONE: time compress + virtual bool MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true ) = 0; + virtual void IncrementSamples( channel_t *pChannel, int startSample, int sampleCount,int outputRate, bool forward = true ) = 0; + virtual bool SkipSamples( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward = true ) = 0; + + virtual CAudioSource *GetSource( void ) = 0; + + virtual int GetSamplePosition( void ) = 0; + virtual int GetScubPosition( void ) = 0; + + virtual bool SetSamplePosition( int position, bool scrubbing = false ) = 0; + virtual void SetLoopPosition( int position ) = 0; + virtual int GetStartPosition( void ) = 0; + + virtual bool GetActive( void ) = 0; + virtual void SetActive( bool active ) = 0; + + virtual void SetModelIndex( int index ) = 0; + virtual int GetModelIndex( void ) const = 0; + + virtual void SetDirection( bool forward ) = 0; + virtual bool GetDirection( void ) const = 0; + + virtual void SetAutoDelete( bool autodelete ) = 0; + virtual bool GetAutoDelete( void ) const = 0; + + virtual void SetVolume( float volume ) = 0; + virtual channel_t *GetChannel() = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: A source is an abstraction for a stream, cached file, or procedural +// source of audio. +//----------------------------------------------------------------------------- +class CSentence; + +abstract_class CAudioSource +{ +public: + CAudioSource( void ); + virtual ~CAudioSource( void ); + + // Create an instance (mixer) of this audio source + virtual CAudioMixer *CreateMixer( void ) = 0; + virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, bool forward = true ) = 0; + virtual int SampleRate( void ) = 0; + virtual int SampleSize( void ) = 0; + virtual int SampleCount( void ) = 0; + virtual float TrueSampleSize( void ) = 0; + virtual bool IsLooped( void ) = 0; + virtual bool IsStreaming( void ) = 0; + virtual float GetRunningLength( void ) = 0; + virtual int GetNumChannels() = 0; + + virtual CSentence *GetSentence( void ) { return NULL; }; + +}; + + +extern CAudioSource *AudioSource_Create( const char *pName ); + +#endif // SND_AUDIO_SOURCE_H diff --git a/public/soundsystem/snd_device.h b/public/soundsystem/snd_device.h new file mode 100644 index 0000000..76981d0 --- /dev/null +++ b/public/soundsystem/snd_device.h @@ -0,0 +1,105 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef SND_DEVICE_H +#define SND_DEVICE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +//----------------------------------------------------------------------------- +// 4.28 fixed point stuff for real-time resampling +//----------------------------------------------------------------------------- +#define FIX_BITS 28 +#define FIX_SCALE (1 << FIX_BITS) +#define FIX_MASK ((1 << FIX_BITS)-1) +#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE)) +#define FIX(a) (((int)(a)) << FIX_BITS) +#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS) +#define FIX_FRACTION(a,b) (FIX(a)/(b)) +#define FIX_FRACPART(a) ((a) & FIX_MASK) + +typedef unsigned int fixedint; + + +//----------------------------------------------------------------------------- +// sound rate defines +//----------------------------------------------------------------------------- +#define SOUND_DMA_SPEED 44100 // hardware playback rate +#define SOUND_11k 11025 // 11khz sample rate +#define SOUND_22k 22050 // 22khz sample rate +#define SOUND_44k 44100 // 44khz sample rate +#define SOUND_ALL_RATES 1 // mix all sample rates + + +//----------------------------------------------------------------------------- +// Information about the channel +//----------------------------------------------------------------------------- +struct channel_t +{ + int leftvol; + int rightvol; + float pitch; +}; + + +//----------------------------------------------------------------------------- +// The audio device is responsible for mixing +//----------------------------------------------------------------------------- +abstract_class IAudioDevice +{ +public: + // This initializes the sound hardware. true on success, false on failure + virtual bool Init( void ) = 0; + + // This releases all sound hardware + virtual void Shutdown( void ) = 0; + + // device parameters + virtual const char *DeviceName( void ) const = 0; + virtual int DeviceChannels( void ) const = 0; // 1 = mono, 2 = stereo + virtual int DeviceSampleBits( void ) const = 0; // bits per sample (8 or 16) + virtual int DeviceSampleBytes( void ) const = 0; // above / 8 + virtual int DeviceSampleRate( void ) const = 0; // Actual DMA speed + virtual int DeviceSampleCount( void ) const = 0; // Total samples in buffer + + // Called each time a new paint buffer is mixed (may be multiple times per frame) + virtual void MixBegin( void ) = 0; + + // Main mixing routines + virtual void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0; + virtual void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0; + virtual void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0; + virtual void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress, bool forward = true ) = 0; + + // Size of the paint buffer in samples + virtual int PaintBufferSampleCount( void ) const = 0; + + // Adds a mixer to be mixed + virtual void AddSource( CAudioMixer *pSource ) = 0; + + // Stops all sounds + virtual void StopSounds( void ) = 0; + + // Updates sound mixing + virtual void Update( float time ) = 0; + + // Resets the device + virtual void Flush( void ) = 0; + + virtual int FindSourceIndex( CAudioMixer *pSource ) = 0; + virtual CAudioMixer *GetMixerForSource( CAudioSource *source ) = 0; + virtual void FreeChannel( int channelIndex ) = 0; +}; + + +#endif // SND_DEVICE_H diff --git a/public/stdstring.h b/public/stdstring.h new file mode 100644 index 0000000..80b3905 --- /dev/null +++ b/public/stdstring.h @@ -0,0 +1,86 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "isaverestore.h" + +#ifndef STDSTRING_H +#define STDSTRING_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef _WIN32 +#pragma warning(push) +#include // warnings get enabled in yvals.h +#pragma warning(disable:4663) +#pragma warning(disable:4530) +#pragma warning(disable:4245) +#pragma warning(disable:4018) +#pragma warning(disable:4511) +#endif + +#include + +#ifdef _WIN32 +#pragma warning(pop) +#endif + +class CStdStringSaveRestoreOps : public CDefSaveRestoreOps +{ +public: + enum + { + MAX_SAVE_LEN = 4096, + }; + + // save data type interface + virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) + { + std::string *pString = (std::string *)fieldInfo.pField; + Assert( pString->length() < MAX_SAVE_LEN - 1 ); + if ( pString->length() < MAX_SAVE_LEN - 1 ) + pSave->WriteString( pString->c_str() ); + else + pSave->WriteString( "<>" ); + } + + virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore ) + { + std::string *pString = (std::string *)fieldInfo.pField; + char szString[MAX_SAVE_LEN]; + pRestore->ReadString( szString, sizeof(szString), 0 ); + szString[MAX_SAVE_LEN - 1] = 0; + pString->assign( szString ); + } + + virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + std::string *pString = (std::string *)fieldInfo.pField; + pString->erase(); + } + + virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo ) + { + std::string *pString = (std::string *)fieldInfo.pField; + return pString->empty(); + } +}; + +//------------------------------------- + +inline ISaveRestoreOps *GetStdStringDataOps() +{ + static CStdStringSaveRestoreOps ops; + return &ops; +} + +//------------------------------------- + +#define DEFINE_STDSTRING(name) \ + { FIELD_CUSTOM, #name, offsetof(classNameTypedef,name), 1, FTYPEDESC_SAVE, NULL, GetStdStringDataOps(), NULL } + +#endif // STDSTRING_H diff --git a/public/steam/isteamapps.h b/public/steam/isteamapps.h new file mode 100644 index 0000000..fccbb61 --- /dev/null +++ b/public/steam/isteamapps.h @@ -0,0 +1,49 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to app data in Steam +// +//============================================================================= + +#ifndef ISTEAMAPPS_H +#define ISTEAMAPPS_H +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: interface to app data +//----------------------------------------------------------------------------- +class ISteamApps +{ +public: + virtual bool BIsSubscribed() = 0; + virtual bool BIsLowViolence() = 0; + virtual bool BIsCybercafe() = 0; + virtual bool BIsVACBanned() = 0; + virtual const char *GetCurrentGameLanguage() = 0; + virtual const char *GetAvailableGameLanguages() = 0; + + // only use this member if you need to check ownership of another game related to yours, a demo for example + virtual bool BIsSubscribedApp( AppId_t appID ) = 0; + + // Takes AppID of DLC and checks if the user owns the DLC & if the DLC is installed + virtual bool BIsDlcInstalled( AppId_t appID ) = 0; +}; + +#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION003" + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: posted after the user gains ownership of DLC & that DLC is installed +//----------------------------------------------------------------------------- +struct DlcInstalled_t +{ + enum { k_iCallback = k_iSteamAppsCallbacks + 5 }; + AppId_t m_nAppID; // AppID of the DLC +}; + +#pragma pack( pop ) + +#endif // ISTEAMAPPS_H diff --git a/public/steam/isteamclient.h b/public/steam/isteamclient.h new file mode 100644 index 0000000..8d4cff2 --- /dev/null +++ b/public/steam/isteamclient.h @@ -0,0 +1,159 @@ +//====== Copyright � 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: Main interface for loading and accessing Steamworks API's from the +// Steam client. +// For most uses, this code is wrapped inside of SteamAPI_Init() +//============================================================================= + +#ifndef ISTEAMCLIENT_H +#define ISTEAMCLIENT_H +#ifdef _WIN32 +#pragma once +#endif + +#include "steamtypes.h" +#include "steamclientpublic.h" + +// handle to a communication pipe to the Steam client +typedef int32 HSteamPipe; +// handle to single instance of a steam user +typedef int32 HSteamUser; +// function prototype +#if defined( POSIX ) +#define __cdecl +#endif +extern "C" typedef void (__cdecl *SteamAPIWarningMessageHook_t)(int, const char *); + +// interface predec +class ISteamUser; +class ISteamGameServer; +class ISteamFriends; +class ISteamUtils; +class ISteamMatchmaking; +class ISteamContentServer; +class ISteamMasterServerUpdater; +class ISteamMatchmakingServers; +class ISteamUserStats; +class ISteamApps; +class ISteamNetworking; +class ISteamRemoteStorage; +class ISteamGameServerStats; + +//----------------------------------------------------------------------------- +// Purpose: Interface to creating a new steam instance, or to +// connect to an existing steam instance, whether it's in a +// different process or is local. +// +// For most scenarios this is all handled automatically via SteamAPI_Init(). +// You'll only need to use these interfaces if you have a more complex versioning scheme, +// where you want to get different versions of the same interface in different dll's in your project. +//----------------------------------------------------------------------------- +class ISteamClient +{ +public: + // Creates a communication pipe to the Steam client + virtual HSteamPipe CreateSteamPipe() = 0; + + // Releases a previously created communications pipe + virtual bool BReleaseSteamPipe( HSteamPipe hSteamPipe ) = 0; + + // connects to an existing global user, failing if none exists + // used by the game to coordinate with the steamUI + virtual HSteamUser ConnectToGlobalUser( HSteamPipe hSteamPipe ) = 0; + + // used by game servers, create a steam user that won't be shared with anyone else + virtual HSteamUser CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType ) = 0; + + // removes an allocated user + virtual void ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser ) = 0; + + // retrieves the ISteamUser interface associated with the handle + virtual ISteamUser *GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // retrieves the ISteamGameServer interface associated with the handle + virtual ISteamGameServer *GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // set the local IP and Port to bind to + // this must be set before CreateLocalUser() + virtual void SetLocalIPBinding( uint32 unIP, uint16 usPort ) = 0; + + // returns the ISteamFriends interface + virtual ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamUtils interface + virtual ISteamUtils *GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamMatchmaking interface + virtual ISteamMatchmaking *GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamMasterServerUpdater interface + virtual ISteamMasterServerUpdater *GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamMatchmakingServers interface + virtual ISteamMatchmakingServers *GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the a generic interface + virtual void *GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamUserStats interface + virtual ISteamUserStats *GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns the ISteamGameServerStats interface + virtual ISteamGameServerStats *GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // returns apps interface + virtual ISteamApps *GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // networking + virtual ISteamNetworking *GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // remote storage + virtual ISteamRemoteStorage *GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion ) = 0; + + // this needs to be called every frame to process matchmaking results + // redundant if you're already calling SteamAPI_RunCallbacks() + virtual void RunFrame() = 0; + + // returns the number of IPC calls made since the last time this function was called + // Used for perf debugging so you can understand how many IPC calls your game makes per frame + // Every IPC call is at minimum a thread context switch if not a process one so you want to rate + // control how often you do them. + virtual uint32 GetIPCCallCount() = 0; + + // API warning handling + // 'int' is the severity; 0 for msg, 1 for warning + // 'const char *' is the text of the message + // callbacks will occur directly after the API function is called that generated the warning or message + virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0; + +}; + +#define STEAMCLIENT_INTERFACE_VERSION "SteamClient009" + +//----------------------------------------------------------------------------- +// Purpose: Base values for callback identifiers, each callback must +// have a unique ID. +//----------------------------------------------------------------------------- +enum { k_iSteamUserCallbacks = 100 }; +enum { k_iSteamGameServerCallbacks = 200 }; +enum { k_iSteamFriendsCallbacks = 300 }; +enum { k_iSteamBillingCallbacks = 400 }; +enum { k_iSteamMatchmakingCallbacks = 500 }; +enum { k_iSteamContentServerCallbacks = 600 }; +enum { k_iSteamUtilsCallbacks = 700 }; +enum { k_iClientFriendsCallbacks = 800 }; +enum { k_iClientUserCallbacks = 900 }; +enum { k_iSteamAppsCallbacks = 1000 }; +enum { k_iSteamUserStatsCallbacks = 1100 }; +enum { k_iSteamNetworkingCallbacks = 1200 }; +enum { k_iClientRemoteStorageCallbacks = 1300 }; +enum { k_iSteamUserItemsCallbacks = 1400 }; +enum { k_iSteamGameServerItemsCallbacks = 1500 }; +enum { k_iClientUtilsCallbacks = 1600 }; +enum { k_iSteamGameCoordinatorCallbacks = 1700 }; +enum { k_iSteamGameServerStatsCallbacks = 1800 }; +enum { k_iSteam2AsyncCallbacks = 1900 }; +enum { k_iSteamGameStatsCallbacks = 2000 }; +enum { k_iClientHTTPCallbacks = 2100 }; + +#endif // ISTEAMCLIENT_H diff --git a/public/steam/isteamfriends.h b/public/steam/isteamfriends.h new file mode 100644 index 0000000..2a2d5b2 --- /dev/null +++ b/public/steam/isteamfriends.h @@ -0,0 +1,274 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to both friends list data and general information about users +// +//============================================================================= + +#ifndef ISTEAMFRIENDS_H +#define ISTEAMFRIENDS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" +#include "steamclientpublic.h" + + +//----------------------------------------------------------------------------- +// Purpose: set of relationships to other users +//----------------------------------------------------------------------------- +enum EFriendRelationship +{ + k_EFriendRelationshipNone = 0, + k_EFriendRelationshipBlocked = 1, + k_EFriendRelationshipRequestRecipient = 2, + k_EFriendRelationshipFriend = 3, + k_EFriendRelationshipRequestInitiator = 4, + k_EFriendRelationshipIgnored = 5, + k_EFriendRelationshipIgnoredFriend = 6, +}; + + +//----------------------------------------------------------------------------- +// Purpose: list of states a friend can be in +//----------------------------------------------------------------------------- +enum EPersonaState +{ + k_EPersonaStateOffline = 0, // friend is not currently logged on + k_EPersonaStateOnline = 1, // friend is logged on + k_EPersonaStateBusy = 2, // user is on, but busy + k_EPersonaStateAway = 3, // auto-away feature + k_EPersonaStateSnooze = 4, // auto-away for a long time + k_EPersonaStateMax, +}; + + +//----------------------------------------------------------------------------- +// Purpose: flags for enumerating friends list, or quickly checking a the relationship between users +//----------------------------------------------------------------------------- +enum EFriendFlags +{ + k_EFriendFlagNone = 0x00, + k_EFriendFlagBlocked = 0x01, + k_EFriendFlagFriendshipRequested = 0x02, + k_EFriendFlagImmediate = 0x04, // "regular" friend + k_EFriendFlagClanMember = 0x08, + k_EFriendFlagOnGameServer = 0x10, + // k_EFriendFlagHasPlayedWith = 0x20, // not currently used + // k_EFriendFlagFriendOfFriend = 0x40, // not currently used + k_EFriendFlagRequestingFriendship = 0x80, + k_EFriendFlagRequestingInfo = 0x100, + k_EFriendFlagIgnored = 0x200, + k_EFriendFlagIgnoredFriend = 0x400, + k_EFriendFlagAll = 0xFFFF, +}; + + +//----------------------------------------------------------------------------- +// Purpose: avatar sizes, used in ISteamFriends::GetFriendAvatar() +//----------------------------------------------------------------------------- +enum EAvatarSize +{ + k_EAvatarSize32x32 = 0, + k_EAvatarSize64x64 = 1, +}; + + +// friend game played information +#pragma pack( push, 8 ) +struct FriendGameInfo_t +{ + CGameID m_gameID; + uint32 m_unGameIP; + uint16 m_usGamePort; + uint16 m_usQueryPort; + CSteamID m_steamIDLobby; +}; +#pragma pack( pop ) + + +// maximum number of characters in a user's name. Two flavors; one for UTF-8 and one for UTF-16. +// The UTF-8 version has to be very generous to accomodate characters that get large when encoded +// in UTF-8. +enum +{ + k_cchPersonaNameMax = 128, + k_cwchPersonaNameMax = 32, +}; + +// size limit on chat room or member metadata +const uint32 k_cubChatMetadataMax = 8192; + +//----------------------------------------------------------------------------- +// Purpose: interface to accessing information about individual users, +// that can be a friend, in a group, on a game server or in a lobby with the local user +//----------------------------------------------------------------------------- +class ISteamFriends +{ +public: + // returns the local players name - guaranteed to not be NULL. + // this is the same name as on the users community profile page + // this is stored in UTF-8 format + // like all the other interface functions that return a char *, it's important that this pointer is not saved + // off; it will eventually be free'd or re-allocated + virtual const char *GetPersonaName() = 0; + + // sets the player name, stores it on the server and publishes the changes to all friends who are online + virtual void SetPersonaName( const char *pchPersonaName ) = 0; + + // gets the status of the current user + virtual EPersonaState GetPersonaState() = 0; + + // friend iteration + // takes a set of k_EFriendFlags, and returns the number of users the client knows about who meet that criteria + // then GetFriendByIndex() can then be used to return the id's of each of those users + virtual int GetFriendCount( int iFriendFlags ) = 0; + + // returns the steamID of a user + // iFriend is a index of range [0, GetFriendCount()) + // iFriendsFlags must be the same value as used in GetFriendCount() + // the returned CSteamID can then be used by all the functions below to access details about the user + virtual CSteamID GetFriendByIndex( int iFriend, int iFriendFlags ) = 0; + + // returns a relationship to a user + virtual EFriendRelationship GetFriendRelationship( CSteamID steamIDFriend ) = 0; + + // returns the current status of the specified user + // this will only be known by the local user if steamIDFriend is in their friends list; on the same game server; in a chat room or lobby; or in a small group with the local user + virtual EPersonaState GetFriendPersonaState( CSteamID steamIDFriend ) = 0; + + // returns the name another user - guaranteed to not be NULL. + // same rules as GetFriendPersonaState() apply as to whether or not the user knowns the name of the other user + // note that on first joining a lobby, chat room or game server the local user will not known the name of the other users automatically; that information will arrive asyncronously + // + virtual const char *GetFriendPersonaName( CSteamID steamIDFriend ) = 0; + + // gets the avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set + virtual int GetFriendAvatar( CSteamID steamIDFriend, int eAvatarSize ) = 0; + // returns true if the friend is actually in a game, and fills in pFriendGameInfo with an extra details + virtual bool GetFriendGamePlayed( CSteamID steamIDFriend, FriendGameInfo_t *pFriendGameInfo ) = 0; + // accesses old friends names - returns an empty string when their are no more items in the history + virtual const char *GetFriendPersonaNameHistory( CSteamID steamIDFriend, int iPersonaName ) = 0; + + // returns true if the specified user meets any of the criteria specified in iFriendFlags + // iFriendFlags can be the union (binary or, |) of one or more k_EFriendFlags values + virtual bool HasFriend( CSteamID steamIDFriend, int iFriendFlags ) = 0; + + // clan (group) iteration and access functions + virtual int GetClanCount() = 0; + virtual CSteamID GetClanByIndex( int iClan ) = 0; + virtual const char *GetClanName( CSteamID steamIDClan ) = 0; + virtual const char *GetClanTag( CSteamID steamIDClan ) = 0; + + // iterators for getting users in a chat room, lobby, game server or clan + // note that large clans that cannot be iterated by the local user + // steamIDSource can be the steamID of a group, game server, lobby or chat room + virtual int GetFriendCountFromSource( CSteamID steamIDSource ) = 0; + virtual CSteamID GetFriendFromSourceByIndex( CSteamID steamIDSource, int iFriend ) = 0; + + // returns true if the local user can see that steamIDUser is a member or in steamIDSource + virtual bool IsUserInSource( CSteamID steamIDUser, CSteamID steamIDSource ) = 0; + + // User is in a game pressing the talk button (will suppress the microphone for all voice comms from the Steam friends UI) + virtual void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking ) = 0; + + // activates the game overlay, with an optional dialog to open + // valid options are "Friends", "Community", "Players", "Settings", "LobbyInvite", "OfficialGameGroup", "Stats", "Achievements" + virtual void ActivateGameOverlay( const char *pchDialog ) = 0; + + // activates game overlay to a specific place + // valid options are + // "steamid" - opens the overlay web browser to the specified user or groups profile + // "chat" - opens a chat window to the specified user, or joins the group chat + // "stats" - opens the overlay web browser to the specified user's stats + // "achievements" - opens the overlay web browser to the specified user's achievements + virtual void ActivateGameOverlayToUser( const char *pchDialog, CSteamID steamID ) = 0; + + // activates game overlay web browser directly to the specified URL + // full address with protocol type is required, e.g. http://www.steamgames.com/ + virtual void ActivateGameOverlayToWebPage( const char *pchURL ) = 0; + + // activates game overlay to store page for app + virtual void ActivateGameOverlayToStore( AppId_t nAppID ) = 0; + + // Mark a target user as 'played with'. This is a client-side only feature that requires that the calling user is + // in game + virtual void SetPlayedWith( CSteamID steamIDUserPlayedWith ) = 0; + + // activates game overlay to open the invite dialog. Invitations will be sent for the provided lobby. + // You can also use ActivateGameOverlay( "LobbyInvite" ) to allow the user to create invitations for their current public lobby. + virtual void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby ) = 0; +}; + +#define STEAMFRIENDS_INTERFACE_VERSION "SteamFriends006" + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: called when a friends' status changes +//----------------------------------------------------------------------------- +struct PersonaStateChange_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 4 }; + + uint64 m_ulSteamID; // steamID of the friend who changed + int m_nChangeFlags; // what's changed +}; + + +// used in PersonaStateChange_t::m_nChangeFlags to describe what's changed about a user +// these flags describe what the client has learned has changed recently, so on startup you'll see a name, avatar & relationship change for every friend +enum EPersonaChange +{ + k_EPersonaChangeName = 0x001, + k_EPersonaChangeStatus = 0x002, + k_EPersonaChangeComeOnline = 0x004, + k_EPersonaChangeGoneOffline = 0x008, + k_EPersonaChangeGamePlayed = 0x010, + k_EPersonaChangeGameServer = 0x020, + k_EPersonaChangeAvatar = 0x040, + k_EPersonaChangeJoinedSource= 0x080, + k_EPersonaChangeLeftSource = 0x100, + k_EPersonaChangeRelationshipChanged = 0x200, + k_EPersonaChangeNameFirstSet = 0x400, +}; + + +//----------------------------------------------------------------------------- +// Purpose: posted when game overlay activates or deactivates +// the game can use this to be pause or resume single player games +//----------------------------------------------------------------------------- +struct GameOverlayActivated_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 31 }; + uint8 m_bActive; // true if it's just been activated, false otherwise +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when the user tries to join a different game server from their friends list +// game client should attempt to connect to specified server when this is received +//----------------------------------------------------------------------------- +struct GameServerChangeRequested_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 32 }; + char m_rgchServer[64]; // server address ("127.0.0.1:27015", "tf2.valvesoftware.com") + char m_rgchPassword[64]; // server password, if any +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when the user tries to join a lobby from their friends list +// game client should attempt to connect to specified lobby when this is received +//----------------------------------------------------------------------------- +struct GameLobbyJoinRequested_t +{ + enum { k_iCallback = k_iSteamFriendsCallbacks + 33 }; + CSteamID m_steamIDLobby; + CSteamID m_steamIDFriend; // the friend they did the join via (will be invalid if not directly via a friend) +}; + +#pragma pack( pop ) + +#endif // ISTEAMFRIENDS_H diff --git a/public/steam/isteamgameserver.h b/public/steam/isteamgameserver.h new file mode 100644 index 0000000..62e6c3e --- /dev/null +++ b/public/steam/isteamgameserver.h @@ -0,0 +1,226 @@ +//====== Copyright (c) 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam for game servers +// +//============================================================================= + +#ifndef ISTEAMGAMESERVER_H +#define ISTEAMGAMESERVER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + + +//----------------------------------------------------------------------------- +// Purpose: Functions for authenticating users via Steam to play on a game server +//----------------------------------------------------------------------------- +class ISteamGameServer +{ +public: + // connection functions + virtual void LogOn() = 0; + virtual void LogOff() = 0; + + // status functions + virtual bool BLoggedOn() = 0; + virtual bool BSecure() = 0; + virtual CSteamID GetSteamID() = 0; + + // Handles receiving a new connection from a Steam user. This call will ask the Steam + // servers to validate the users identity, app ownership, and VAC status. If the Steam servers + // are off-line, then it will validate the cached ticket itself which will validate app ownership + // and identity. The AuthBlob here should be acquired on the game client using SteamUser()->InitiateGameConnection() + // and must then be sent up to the game server for authentication. + // + // Return Value: returns true if the users ticket passes basic checks. pSteamIDUser will contain the Steam ID of this user. pSteamIDUser must NOT be NULL + // If the call succeeds then you should expect a GSClientApprove_t or GSClientDeny_t callback which will tell you whether authentication + // for the user has succeeded or failed (the steamid in the callback will match the one returned by this call) + virtual bool SendUserConnectAndAuthenticate( uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser ) = 0; + + // Creates a fake user (ie, a bot) which will be listed as playing on the server, but skips validation. + // + // Return Value: Returns a SteamID for the user to be tracked with, you should call HandleUserDisconnect() + // when this user leaves the server just like you would for a real user. + virtual CSteamID CreateUnauthenticatedUserConnection() = 0; + + // Should be called whenever a user leaves our game server, this lets Steam internally + // track which users are currently on which servers for the purposes of preventing a single + // account being logged into multiple servers, showing who is currently on a server, etc. + virtual void SendUserDisconnect( CSteamID steamIDUser ) = 0; + + // Update the data to be displayed in the server browser and matchmaking interfaces for a user + // currently connected to the server. For regular users you must call this after you receive a + // GSUserValidationSuccess callback. + // + // Return Value: true if successful, false if failure (ie, steamIDUser wasn't for an active player) + virtual bool BUpdateUserData( CSteamID steamIDUser, const char *pchPlayerName, uint32 uScore ) = 0; + + // You shouldn't need to call this as it is called internally by SteamGameServer_Init() and can only be called once. + // + // To update the data in this call which may change during the servers lifetime see UpdateServerStatus() below. + // + // Input: nGameAppID - The Steam assigned AppID for the game + // unServerFlags - Any applicable combination of flags (see k_unServerFlag____ constants below) + // unGameIP - The IP Address the server is listening for client connections on (might be INADDR_ANY) + // unGamePort - The port which the server is listening for client connections on + // unSpectatorPort - the port on which spectators can join to observe the server, 0 if spectating is not supported + // usQueryPort - The port which the ISteamMasterServerUpdater API should use in order to listen for matchmaking requests + // pchGameDir - A unique string identifier for your game + // pchVersion - The current version of the server as a string like 1.0.0.0 + // bLanMode - Is this a LAN only server? + // + // bugbug jmccaskey - figure out how to remove this from the API and only expose via SteamGameServer_Init... or make this actually used, + // and stop calling it in SteamGameServer_Init()? + virtual bool BSetServerType( uint32 unServerFlags, uint32 unGameIP, uint16 unGamePort, + uint16 unSpectatorPort, uint16 usQueryPort, const char *pchGameDir, const char *pchVersion, bool bLANMode ) = 0; + + // Updates server status values which shows up in the server browser and matchmaking APIs + virtual void UpdateServerStatus( int cPlayers, int cPlayersMax, int cBotPlayers, + const char *pchServerName, const char *pSpectatorServerName, + const char *pchMapName ) = 0; + + // This can be called if spectator goes away or comes back (passing 0 means there is no spectator server now). + virtual void UpdateSpectatorPort( uint16 unSpectatorPort ) = 0; + + // Sets a string defining the "gametags" for this server, this is optional, but if it is set + // it allows users to filter in the matchmaking/server-browser interfaces based on the value + virtual void SetGameTags( const char *pchGameTags ) = 0; + + // Ask for the gameplay stats for the server. Results returned in a callback + virtual void GetGameplayStats( ) = 0; + + // Gets the reputation score for the game server. This API also checks if the server or some + // other server on the same IP is banned from the Steam master servers. + virtual SteamAPICall_t GetServerReputation( ) = 0; + + // Ask if a user in in the specified group, results returns async by GSUserGroupStatus_t + // returns false if we're not connected to the steam servers and thus cannot ask + virtual bool RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup ) = 0; + + // Returns the public IP of the server according to Steam, useful when the server is + // behind NAT and you want to advertise its IP in a lobby for other clients to directly + // connect to + virtual uint32 GetPublicIP() = 0; + + // Sets a string defining the "gamedata" for this server, this is optional, but if it is set + // it allows users to filter in the matchmaking/server-browser interfaces based on the value + // don't set this unless it actually changes, its only uploaded to the master once (when + // acknowledged) + virtual void SetGameData( const char *pchGameData) = 0; + + // After receiving a user's authentication data, and passing it to SendUserConnectAndAuthenticate, use this function + // to determine if the user owns downloadable content specified by the provided AppID. + virtual EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID ) = 0; +}; + +#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer010" + +// game server flags +const uint32 k_unServerFlagNone = 0x00; +const uint32 k_unServerFlagActive = 0x01; // server has users playing +const uint32 k_unServerFlagSecure = 0x02; // server wants to be secure +const uint32 k_unServerFlagDedicated = 0x04; // server is dedicated +const uint32 k_unServerFlagLinux = 0x08; // linux build +const uint32 k_unServerFlagPassworded = 0x10; // password protected +const uint32 k_unServerFlagPrivate = 0x20; // server shouldn't list on master server and + // won't enforce authentication of users that connect to the server. + // Useful when you run a server where the clients may not + // be connected to the internet but you want them to play (i.e LANs) + + +// callbacks +#pragma pack( push, 8 ) + + +// client has been approved to connect to this game server +struct GSClientApprove_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 1 }; + CSteamID m_SteamID; +}; + + +// client has been denied to connection to this game server +struct GSClientDeny_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 2 }; + CSteamID m_SteamID; + EDenyReason m_eDenyReason; + char m_rgchOptionalText[128]; +}; + + +// request the game server should kick the user +struct GSClientKick_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 3 }; + CSteamID m_SteamID; + EDenyReason m_eDenyReason; +}; + +// NOTE: callback values 4 and 5 are skipped because they are used for old deprecated callbacks, +// do not reuse them here. + + +// client achievement info +struct GSClientAchievementStatus_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 6 }; + uint64 m_SteamID; + char m_pchAchievement[128]; + bool m_bUnlocked; +}; + +// received when the game server requests to be displayed as secure (VAC protected) +// m_bSecure is true if the game server should display itself as secure to users, false otherwise +struct GSPolicyResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 15 }; + uint8 m_bSecure; +}; + +// GS gameplay stats info +struct GSGameplayStats_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 7 }; + EResult m_eResult; // Result of the call + int32 m_nRank; // Overall rank of the server (0-based) + uint32 m_unTotalConnects; // Total number of clients who have ever connected to the server + uint32 m_unTotalMinutesPlayed; // Total number of minutes ever played on the server +}; + +// send as a reply to RequestUserGroupStatus() +struct GSClientGroupStatus_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 8 }; + CSteamID m_SteamIDUser; + CSteamID m_SteamIDGroup; + bool m_bMember; + bool m_bOfficer; +}; + +// Sent as a reply to GetServerReputation() +struct GSReputation_t +{ + enum { k_iCallback = k_iSteamGameServerCallbacks + 9 }; + EResult m_eResult; // Result of the call; + uint32 m_unReputationScore; // The reputation score for the game server + bool m_bBanned; // True if the server is banned from the Steam + // master servers + + // The following members are only filled out if m_bBanned is true. They will all + // be set to zero otherwise. Master server bans are by IP so it is possible to be + // banned even when the score is good high if there is a bad server on another port. + // This information can be used to determine which server is bad. + + uint32 m_unBannedIP; // The IP of the banned server + uint16 m_usBannedPort; // The port of the banned server + uint64 m_ulBannedGameID; // The game ID the banned server is serving + uint32 m_unBanExpires; // Time the ban expires, expressed in the Unix epoch (seconds since 1/1/1970) +}; + +#pragma pack( pop ) + +#endif // ISTEAMGAMESERVER_H diff --git a/public/steam/isteamgameserverstats.h b/public/steam/isteamgameserverstats.h new file mode 100644 index 0000000..a5516f8 --- /dev/null +++ b/public/steam/isteamgameserverstats.h @@ -0,0 +1,93 @@ +//====== Copyright © Valve Corporation, All rights reserved. ======= +// +// Purpose: interface for game servers to steam stats and achievements +// +//============================================================================= + +#ifndef ISTEAMGAMESERVERSTATS_H +#define ISTEAMGAMESERVERSTATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +//----------------------------------------------------------------------------- +// Purpose: Functions for authenticating users via Steam to play on a game server +//----------------------------------------------------------------------------- +class ISteamGameServerStats +{ +public: + // downloads stats for the user + // returns a GSStatsReceived_t callback when completed + // if the user has no stats, GSStatsReceived_t.m_eResult will be set to k_EResultFail + // these stats will only be auto-updated for clients playing on the server. For other + // users you'll need to call RequestUserStats() again to refresh any data + virtual SteamAPICall_t RequestUserStats( CSteamID steamIDUser ) = 0; + + // requests stat information for a user, usable after a successful call to RequestUserStats() + virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData ) = 0; + virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData ) = 0; + virtual bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved ) = 0; + + // Set / update stats and achievements. + // Note: These updates will work only on stats game servers are allowed to edit and only for + // game servers that have been declared as officially controlled by the game creators. + // Set the IP range of your official servers on the Steamworks page + virtual bool SetUserStat( CSteamID steamIDUser, const char *pchName, int32 nData ) = 0; + virtual bool SetUserStat( CSteamID steamIDUser, const char *pchName, float fData ) = 0; + virtual bool UpdateUserAvgRateStat( CSteamID steamIDUser, const char *pchName, float flCountThisSession, double dSessionLength ) = 0; + + virtual bool SetUserAchievement( CSteamID steamIDUser, const char *pchName ) = 0; + virtual bool ClearUserAchievement( CSteamID steamIDUser, const char *pchName ) = 0; + + // Store the current data on the server, will get a GSStatsStored_t callback when set. + // + // If the callback has a result of k_EResultInvalidParam, one or more stats + // uploaded has been rejected, either because they broke constraints + // or were out of date. In this case the server sends back updated values. + // The stats should be re-iterated to keep in sync. + virtual SteamAPICall_t StoreUserStats( CSteamID steamIDUser ) = 0; +}; + +#define STEAMGAMESERVERSTATS_INTERFACE_VERSION "SteamGameServerStats001" + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: called when the latests stats and achievements have been received +// from the server +//----------------------------------------------------------------------------- +struct GSStatsReceived_t +{ + enum { k_iCallback = k_iSteamGameServerStatsCallbacks }; + EResult m_eResult; // Success / error fetching the stats + CSteamID m_steamIDUser; // The user for whom the stats are retrieved for +}; + + +//----------------------------------------------------------------------------- +// Purpose: result of a request to store the user stats for a game +//----------------------------------------------------------------------------- +struct GSStatsStored_t +{ + enum { k_iCallback = k_iSteamGameServerStatsCallbacks + 1 }; + EResult m_eResult; // success / error + CSteamID m_steamIDUser; // The user for whom the stats were stored +}; + +//----------------------------------------------------------------------------- +// Purpose: Callback indicating that a user's stats have been unloaded. +// Call RequestUserStats again to access stats for this user +//----------------------------------------------------------------------------- +struct GSStatsUnloaded_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 8 }; + CSteamID m_steamIDUser; // User whose stats have been unloaded +}; + +#pragma pack( pop ) + + +#endif // ISTEAMGAMESERVERSTATS_H diff --git a/public/steam/isteammasterserverupdater.h b/public/steam/isteammasterserverupdater.h new file mode 100644 index 0000000..47a0a30 --- /dev/null +++ b/public/steam/isteammasterserverupdater.h @@ -0,0 +1,103 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam for retrieving list of game servers +// +//============================================================================= + +#ifndef ISTEAMMASTERSERVERUPDATER_H +#define ISTEAMMASTERSERVERUPDATER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +#define MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE ((uint16)-1) + + +//----------------------------------------------------------------------------- +// Purpose: Game engines use this to tell the Steam master servers +// about their games so their games can show up in the server browser. +//----------------------------------------------------------------------------- +class ISteamMasterServerUpdater +{ +public: + + // Call this as often as you like to tell the master server updater whether or not + // you want it to be active (default: off). + virtual void SetActive( bool bActive ) = 0; + + // You usually don't need to modify this. + // Pass -1 to use the default value for iHeartbeatInterval. + // Some mods change this. + virtual void SetHeartbeatInterval( int iHeartbeatInterval ) = 0; + + +// These are in GameSocketShare mode, where instead of ISteamMasterServerUpdater creating its own +// socket to talk to the master server on, it lets the game use its socket to forward messages +// back and forth. This prevents us from requiring server ops to open up yet another port +// in their firewalls. +// +// the IP address and port should be in host order, i.e 127.0.0.1 == 0x7f000001 + + // These are used when you've elected to multiplex the game server's UDP socket + // rather than having the master server updater use its own sockets. + // + // Source games use this to simplify the job of the server admins, so they + // don't have to open up more ports on their firewalls. + + // Call this when a packet that starts with 0xFFFFFFFF comes in. That means + // it's for us. + virtual bool HandleIncomingPacket( const void *pData, int cbData, uint32 srcIP, uint16 srcPort ) = 0; + + // AFTER calling HandleIncomingPacket for any packets that came in that frame, call this. + // This gets a packet that the master server updater needs to send out on UDP. + // It returns the length of the packet it wants to send, or 0 if there are no more packets to send. + // Call this each frame until it returns 0. + virtual int GetNextOutgoingPacket( void *pOut, int cbMaxOut, uint32 *pNetAdr, uint16 *pPort ) = 0; + + +// Functions to set various fields that are used to respond to queries. + + // Call this to set basic data that is passed to the server browser. + virtual void SetBasicServerData( + unsigned short nProtocolVersion, + bool bDedicatedServer, + const char *pRegionName, + const char *pProductName, + unsigned short nMaxReportedClients, + bool bPasswordProtected, + const char *pGameDescription ) = 0; + + // Call this to clear the whole list of key/values that are sent in rules queries. + virtual void ClearAllKeyValues() = 0; + + // Call this to add/update a key/value pair. + virtual void SetKeyValue( const char *pKey, const char *pValue ) = 0; + + + // You can call this upon shutdown to clear out data stored for this game server and + // to tell the master servers that this server is going away. + virtual void NotifyShutdown() = 0; + + // Returns true if the master server has requested a restart. + // Only returns true once per request. + virtual bool WasRestartRequested() = 0; + + // Force it to request a heartbeat from the master servers. + virtual void ForceHeartbeat() = 0; + + // Manually edit and query the master server list. + // It will provide name resolution and use the default master server port if none is provided. + virtual bool AddMasterServer( const char *pServerAddress ) = 0; + virtual bool RemoveMasterServer( const char *pServerAddress ) = 0; + + virtual int GetNumMasterServers() = 0; + + // Returns the # of bytes written to pOut. + virtual int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize ) = 0; +}; + +#define STEAMMASTERSERVERUPDATER_INTERFACE_VERSION "SteamMasterServerUpdater001" + +#endif // ISTEAMMASTERSERVERUPDATER_H diff --git a/public/steam/isteammatchmaking.h b/public/steam/isteammatchmaking.h new file mode 100644 index 0000000..33dcbbd --- /dev/null +++ b/public/steam/isteammatchmaking.h @@ -0,0 +1,634 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam managing game server/client match making +// +//============================================================================= + +#ifndef ISTEAMMATCHMAKING +#define ISTEAMMATCHMAKING +#ifdef _WIN32 +#pragma once +#endif + +#include "steamtypes.h" +#include "steamclientpublic.h" +#include "matchmakingtypes.h" +#include "isteamclient.h" +#include "isteamfriends.h" + +// lobby type description +enum ELobbyType +{ + k_ELobbyTypeFriendsOnly = 1, // shows for friends or invitees, but not in lobby list + k_ELobbyTypePublic = 2, // visible for friends and in lobby list + k_ELobbyTypeInvisible = 3, // returned by search, but not visible to other friends + // useful if you want a user in two lobbies, for example matching groups together + // a user can be in only one regular lobby, and up to two invisible lobbies +}; + +// lobby search filter tools +enum ELobbyComparison +{ + k_ELobbyComparisonEqualToOrLessThan = -2, + k_ELobbyComparisonLessThan = -1, + k_ELobbyComparisonEqual = 0, + k_ELobbyComparisonGreaterThan = 1, + k_ELobbyComparisonEqualToOrGreaterThan = 2, + k_ELobbyComparisonNotEqual = 3, +}; + +// lobby search distance +enum ELobbyDistanceFilter +{ + k_ELobbyDistanceFilterClose, // only lobbies in the same immediate region will be returned + k_ELobbyDistanceFilterDefault, // only lobbies in the same region or close, but looking further if the current region has infrequent lobby activity (the default) + k_ELobbyDistanceFilterFar, // for games that don't have many latency requirements, will return lobbies about half-way around the globe + k_ELobbyDistanceFilterWorldwide, // no filtering, will match lobbies as far as India to NY (not recommended, expect multiple seconds of latency between the clients) +}; + +// maximum number of characters a lobby metadata key can be +#define k_nMaxLobbyKeyLength 255 + +//----------------------------------------------------------------------------- +// Purpose: Functions for match making services for clients to get to favorites +// and to operate on game lobbies. +//----------------------------------------------------------------------------- +class ISteamMatchmaking +{ +public: + // game server favorites storage + // saves basic details about a multiplayer game server locally + + // returns the number of favorites servers the user has stored + virtual int GetFavoriteGameCount() = 0; + + // returns the details of the game server + // iGame is of range [0,GetFavoriteGameCount()) + // *pnIP, *pnConnPort are filled in the with IP:port of the game server + // *punFlags specify whether the game server was stored as an explicit favorite or in the history of connections + // *pRTime32LastPlayedOnServer is filled in the with the Unix time the favorite was added + virtual bool GetFavoriteGame( int iGame, AppId_t *pnAppID, uint32 *pnIP, uint16 *pnConnPort, uint16 *pnQueryPort, uint32 *punFlags, uint32 *pRTime32LastPlayedOnServer ) = 0; + + // adds the game server to the local list; updates the time played of the server if it already exists in the list + virtual int AddFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags, uint32 rTime32LastPlayedOnServer ) =0; + + // removes the game server from the local storage; returns true if one was removed + virtual bool RemoveFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQueryPort, uint32 unFlags ) = 0; + + /////// + // Game lobby functions + + // Get a list of relevant lobbies + // this is an asynchronous request + // results will be returned by LobbyMatchList_t callback & call result, with the number of lobbies found + // this will never return lobbies that are full + // to add more filter, the filter calls below need to be call before each and every RequestLobbyList() call + // use the CCallResult<> object in steam_api.h to match the SteamAPICall_t call result to a function in an object, e.g. + /* + class CMyLobbyListManager + { + CCallResult m_CallResultLobbyMatchList; + void FindLobbies() + { + // SteamMatchmaking()->AddRequestLobbyListFilter*() functions would be called here, before RequestLobbyList() + SteamAPICall_t hSteamAPICall = SteamMatchmaking()->RequestLobbyList(); + m_CallResultLobbyMatchList.Set( hSteamAPICall, this, &CMyLobbyListManager::OnLobbyMatchList ); + } + + void OnLobbyMatchList( LobbyMatchList_t *pLobbyMatchList, bool bIOFailure ) + { + // lobby list has be retrieved from Steam back-end, use results + } + } + */ + // + virtual SteamAPICall_t RequestLobbyList() = 0; + // filters for lobbies + // this needs to be called before RequestLobbyList() to take effect + // these are cleared on each call to RequestLobbyList() + virtual void AddRequestLobbyListStringFilter( const char *pchKeyToMatch, const char *pchValueToMatch, ELobbyComparison eComparisonType ) = 0; + // numerical comparison + virtual void AddRequestLobbyListNumericalFilter( const char *pchKeyToMatch, int nValueToMatch, ELobbyComparison eComparisonType ) = 0; + // returns results closest to the specified value. Multiple near filters can be added, with early filters taking precedence + virtual void AddRequestLobbyListNearValueFilter( const char *pchKeyToMatch, int nValueToBeCloseTo ) = 0; + // returns only lobbies with the specified number of slots available + virtual void AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable ) = 0; + // sets the distance for which we should search for lobbies (based on users IP address to location map on the Steam backed) + virtual void AddRequestLobbyListDistanceFilter( ELobbyDistanceFilter eLobbyDistanceFilter ) = 0; + // sets how many results to return, the lower the count the faster it is to download the lobby results & details to the client + virtual void AddRequestLobbyListResultCountFilter( int cMaxResults ) = 0; + + // returns the CSteamID of a lobby, as retrieved by a RequestLobbyList call + // should only be called after a LobbyMatchList_t callback is received + // iLobby is of the range [0, LobbyMatchList_t::m_nLobbiesMatching) + // the returned CSteamID::IsValid() will be false if iLobby is out of range + virtual CSteamID GetLobbyByIndex( int iLobby ) = 0; + + // Create a lobby on the Steam servers. + // If private, then the lobby will not be returned by any RequestLobbyList() call; the CSteamID + // of the lobby will need to be communicated via game channels or via InviteUserToLobby() + // this is an asynchronous request + // results will be returned by LobbyCreated_t callback and call result; lobby is joined & ready to use at this pointer + // a LobbyEnter_t callback will also be received (since the local user is joining their own lobby) + virtual SteamAPICall_t CreateLobby( ELobbyType eLobbyType, int cMaxMembers ) = 0; + + // Joins an existing lobby + // this is an asynchronous request + // results will be returned by LobbyEnter_t callback & call result, check m_EChatRoomEnterResponse to see if was successful + // lobby metadata is available to use immediately on this call completing + virtual SteamAPICall_t JoinLobby( CSteamID steamIDLobby ) = 0; + + // Leave a lobby; this will take effect immediately on the client side + // other users in the lobby will be notified by a LobbyChatUpdate_t callback + virtual void LeaveLobby( CSteamID steamIDLobby ) = 0; + + // Invite another user to the lobby + // the target user will receive a LobbyInvite_t callback + // will return true if the invite is successfully sent, whether or not the target responds + // returns false if the local user is not connected to the Steam servers + // if the other user clicks the join link, a GameLobbyJoinRequested_t will be posted if the user is in-game, + // or if the game isn't running yet the game will be launched with the parameter +connect_lobby <64-bit lobby id> + virtual bool InviteUserToLobby( CSteamID steamIDLobby, CSteamID steamIDInvitee ) = 0; + + // Lobby iteration, for viewing details of users in a lobby + // only accessible if the lobby user is a member of the specified lobby + // persona information for other lobby members (name, avatar, etc.) will be asynchronously received + // and accessible via ISteamFriends interface + + // returns the number of users in the specified lobby + virtual int GetNumLobbyMembers( CSteamID steamIDLobby ) = 0; + // returns the CSteamID of a user in the lobby + // iMember is of range [0,GetNumLobbyMembers()) + virtual CSteamID GetLobbyMemberByIndex( CSteamID steamIDLobby, int iMember ) = 0; + + // Get data associated with this lobby + // takes a simple key, and returns the string associated with it + // "" will be returned if no value is set, or if steamIDLobby is invalid + virtual const char *GetLobbyData( CSteamID steamIDLobby, const char *pchKey ) = 0; + // Sets a key/value pair in the lobby metadata + // each user in the lobby will be broadcast this new value, and any new users joining will receive any existing data + // this can be used to set lobby names, map, etc. + // to reset a key, just set it to "" + // other users in the lobby will receive notification of the lobby data change via a LobbyDataUpdate_t callback + virtual bool SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue ) = 0; + + // returns the number of metadata keys set on the specified lobby + virtual int GetLobbyDataCount( CSteamID steamIDLobby ) = 0; + + // returns a lobby metadata key/values pair by index, of range [0, GetLobbyDataCount()) + virtual bool GetLobbyDataByIndex( CSteamID steamIDLobby, int iLobbyData, char *pchKey, int cchKeyBufferSize, char *pchValue, int cchValueBufferSize ) = 0; + + // removes a metadata key from the lobby + virtual bool DeleteLobbyData( CSteamID steamIDLobby, const char *pchKey ) = 0; + + // Gets per-user metadata for someone in this lobby + virtual const char *GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, const char *pchKey ) = 0; + // Sets per-user metadata (for the local user implicitly) + virtual void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue ) = 0; + + // Broadcasts a chat message to the all the users in the lobby + // users in the lobby (including the local user) will receive a LobbyChatMsg_t callback + // returns true if the message is successfully sent + // pvMsgBody can be binary or text data, up to 4k + // if pvMsgBody is text, cubMsgBody should be strlen( text ) + 1, to include the null terminator + virtual bool SendLobbyChatMsg( CSteamID steamIDLobby, const void *pvMsgBody, int cubMsgBody ) = 0; + // Get a chat message as specified in a LobbyChatMsg_t callback + // iChatID is the LobbyChatMsg_t::m_iChatID value in the callback + // *pSteamIDUser is filled in with the CSteamID of the member + // *pvData is filled in with the message itself + // return value is the number of bytes written into the buffer + virtual int GetLobbyChatEntry( CSteamID steamIDLobby, int iChatID, CSteamID *pSteamIDUser, void *pvData, int cubData, EChatEntryType *peChatEntryType ) = 0; + + // Refreshes metadata for a lobby you're not necessarily in right now + // you never do this for lobbies you're a member of, only if your + // this will send down all the metadata associated with a lobby + // this is an asynchronous call + // returns false if the local user is not connected to the Steam servers + // restart are returned by a LobbyDataUpdate_t callback + virtual bool RequestLobbyData( CSteamID steamIDLobby ) = 0; + + // sets the game server associated with the lobby + // usually at this point, the users will join the specified game server + // either the IP/Port or the steamID of the game server has to be valid, depending on how you want the clients to be able to connect + virtual void SetLobbyGameServer( CSteamID steamIDLobby, uint32 unGameServerIP, uint16 unGameServerPort, CSteamID steamIDGameServer ) = 0; + // returns the details of a game server set in a lobby - returns false if there is no game server set, or that lobby doesn't exist + virtual bool GetLobbyGameServer( CSteamID steamIDLobby, uint32 *punGameServerIP, uint16 *punGameServerPort, CSteamID *psteamIDGameServer ) = 0; + + // set the limit on the # of users who can join the lobby + virtual bool SetLobbyMemberLimit( CSteamID steamIDLobby, int cMaxMembers ) = 0; + // returns the current limit on the # of users who can join the lobby; returns 0 if no limit is defined + virtual int GetLobbyMemberLimit( CSteamID steamIDLobby ) = 0; + + // updates which type of lobby it is + // only lobbies that are k_ELobbyTypePublic or k_ELobbyTypeInvisible, and are set to joinable, will be returned by RequestLobbyList() calls + virtual bool SetLobbyType( CSteamID steamIDLobby, ELobbyType eLobbyType ) = 0; + + // sets whether or not a lobby is joinable - defaults to true for a new lobby + // if set to false, no user can join, even if they are a friend or have been invited + virtual bool SetLobbyJoinable( CSteamID steamIDLobby, bool bLobbyJoinable ) = 0; + + // returns the current lobby owner + // you must be a member of the lobby to access this + // there always one lobby owner - if the current owner leaves, another user will become the owner + // it is possible (bur rare) to join a lobby just as the owner is leaving, thus entering a lobby with self as the owner + virtual CSteamID GetLobbyOwner( CSteamID steamIDLobby ) = 0; + + // changes who the lobby owner is + // you must be the lobby owner for this to succeed, and steamIDNewOwner must be in the lobby + // after completion, the local user will no longer be the owner + virtual bool SetLobbyOwner( CSteamID steamIDLobby, CSteamID steamIDNewOwner ) = 0; +}; +#define STEAMMATCHMAKING_INTERFACE_VERSION "SteamMatchMaking008" + + +//----------------------------------------------------------------------------- +// Callback interfaces for server list functions (see ISteamMatchmakingServers below) +// +// The idea here is that your game code implements objects that implement these +// interfaces to receive callback notifications after calling asynchronous functions +// inside the ISteamMatchmakingServers() interface below. +// +// This is different than normal Steam callback handling due to the potentially +// large size of server lists. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Typedef for handle type you will receive when requesting server list. +//----------------------------------------------------------------------------- +typedef void* HServerListRequest; + +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after a server list refresh +// or an individual server update. +// +// Since you get these callbacks after requesting full list refreshes you will +// usually implement this interface inside an object like CServerBrowser. If that +// object is getting destructed you should use ISteamMatchMakingServers()->CancelQuery() +// to cancel any in-progress queries so you don't get a callback into the destructed +// object and crash. +//----------------------------------------------------------------------------- +class ISteamMatchmakingServerListResponse +{ +public: + // Server has responded ok with updated data + virtual void ServerResponded( HServerListRequest hRequest, int iServer ) = 0; + + // Server has failed to respond + virtual void ServerFailedToRespond( HServerListRequest hRequest, int iServer ) = 0; + + // A list refresh you had initiated is now 100% completed + virtual void RefreshComplete( HServerListRequest hRequest, EMatchMakingServerResponse response ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after pinging an individual server +// +// These callbacks all occur in response to querying an individual server +// via the ISteamMatchmakingServers()->PingServer() call below. If you are +// destructing an object that implements this interface then you should call +// ISteamMatchmakingServers()->CancelServerQuery() passing in the handle to the query +// which is in progress. Failure to cancel in progress queries when destructing +// a callback handler may result in a crash when a callback later occurs. +//----------------------------------------------------------------------------- +class ISteamMatchmakingPingResponse +{ +public: + // Server has responded successfully and has updated data + virtual void ServerResponded( gameserveritem_t &server ) = 0; + + // Server failed to respond to the ping request + virtual void ServerFailedToRespond() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after requesting details on +// who is playing on a particular server. +// +// These callbacks all occur in response to querying an individual server +// via the ISteamMatchmakingServers()->PlayerDetails() call below. If you are +// destructing an object that implements this interface then you should call +// ISteamMatchmakingServers()->CancelServerQuery() passing in the handle to the query +// which is in progress. Failure to cancel in progress queries when destructing +// a callback handler may result in a crash when a callback later occurs. +//----------------------------------------------------------------------------- +class ISteamMatchmakingPlayersResponse +{ +public: + // Got data on a new player on the server -- you'll get this callback once per player + // on the server which you have requested player data on. + virtual void AddPlayerToList( const char *pchName, int nScore, float flTimePlayed ) = 0; + + // The server failed to respond to the request for player details + virtual void PlayersFailedToRespond() = 0; + + // The server has finished responding to the player details request + // (ie, you won't get anymore AddPlayerToList callbacks) + virtual void PlayersRefreshComplete() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback interface for receiving responses after requesting rules +// details on a particular server. +// +// These callbacks all occur in response to querying an individual server +// via the ISteamMatchmakingServers()->ServerRules() call below. If you are +// destructing an object that implements this interface then you should call +// ISteamMatchmakingServers()->CancelServerQuery() passing in the handle to the query +// which is in progress. Failure to cancel in progress queries when destructing +// a callback handler may result in a crash when a callback later occurs. +//----------------------------------------------------------------------------- +class ISteamMatchmakingRulesResponse +{ +public: + // Got data on a rule on the server -- you'll get one of these per rule defined on + // the server you are querying + virtual void RulesResponded( const char *pchRule, const char *pchValue ) = 0; + + // The server failed to respond to the request for rule details + virtual void RulesFailedToRespond() = 0; + + // The server has finished responding to the rule details request + // (ie, you won't get anymore RulesResponded callbacks) + virtual void RulesRefreshComplete() = 0; +}; + + +//----------------------------------------------------------------------------- +// Typedef for handle type you will receive when querying details on an individual server. +//----------------------------------------------------------------------------- +typedef int HServerQuery; +const int HSERVERQUERY_INVALID = 0xffffffff; + +//----------------------------------------------------------------------------- +// Purpose: Functions for match making services for clients to get to game lists and details +//----------------------------------------------------------------------------- +class ISteamMatchmakingServers +{ +public: + // Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values. + // Each call allocates a new asynchronous request object. + // Request object must be released by calling ReleaseRequest( hServerListRequest ) + virtual HServerListRequest RequestInternetServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestLANServerList( AppId_t iApp, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestFriendsServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestFavoritesServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestHistoryServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + virtual HServerListRequest RequestSpectatorServerList( AppId_t iApp, MatchMakingKeyValuePair_t **ppchFilters, uint32 nFilters, ISteamMatchmakingServerListResponse *pRequestServersResponse ) = 0; + + // Releases the asynchronous request object and cancels any pending query on it if there's a pending query in progress. + // RefreshComplete callback is not posted when request is released. + virtual void ReleaseRequest( HServerListRequest hServerListRequest ) = 0; + + /* the filters that are available in the ppchFilters params are: + + "map" - map the server is running, as set in the dedicated server api + "dedicated" - reports bDedicated from the API + "secure" - VAC-enabled + "full" - not full + "empty" - not empty + "noplayers" - is empty + "proxy" - a relay server + + */ + + // Get details on a given server in the list, you can get the valid range of index + // values by calling GetServerCount(). You will also receive index values in + // ISteamMatchmakingServerListResponse::ServerResponded() callbacks + virtual gameserveritem_t *GetServerDetails( HServerListRequest hRequest, int iServer ) = 0; + + // Cancel an request which is operation on the given list type. You should call this to cancel + // any in-progress requests before destructing a callback object that may have been passed + // to one of the above list request calls. Not doing so may result in a crash when a callback + // occurs on the destructed object. + // Canceling a query does not release the allocated request handle. + // The request handle must be released using ReleaseRequest( hRequest ) + virtual void CancelQuery( HServerListRequest hRequest ) = 0; + + // Ping every server in your list again but don't update the list of servers + // Query callback installed when the server list was requested will be used + // again to post notifications and RefreshComplete, so the callback must remain + // valid until another RefreshComplete is called on it or the request + // is released with ReleaseRequest( hRequest ) + virtual void RefreshQuery( HServerListRequest hRequest ) = 0; + + // Returns true if the list is currently refreshing its server list + virtual bool IsRefreshing( HServerListRequest hRequest ) = 0; + + // How many servers in the given list, GetServerDetails above takes 0... GetServerCount() - 1 + virtual int GetServerCount( HServerListRequest hRequest ) = 0; + + // Refresh a single server inside of a query (rather than all the servers ) + virtual void RefreshServer( HServerListRequest hRequest, int iServer ) = 0; + + + //----------------------------------------------------------------------------- + // Queries to individual servers directly via IP/Port + //----------------------------------------------------------------------------- + + // Request updated ping time and other details from a single server + virtual HServerQuery PingServer( uint32 unIP, uint16 usPort, ISteamMatchmakingPingResponse *pRequestServersResponse ) = 0; + + // Request the list of players currently playing on a server + virtual HServerQuery PlayerDetails( uint32 unIP, uint16 usPort, ISteamMatchmakingPlayersResponse *pRequestServersResponse ) = 0; + + // Request the list of rules that the server is running (See ISteamMasterServerUpdater->SetKeyValue() to set the rules server side) + virtual HServerQuery ServerRules( uint32 unIP, uint16 usPort, ISteamMatchmakingRulesResponse *pRequestServersResponse ) = 0; + + // Cancel an outstanding Ping/Players/Rules query from above. You should call this to cancel + // any in-progress requests before destructing a callback object that may have been passed + // to one of the above calls to avoid crashing when callbacks occur. + virtual void CancelServerQuery( HServerQuery hServerQuery ) = 0; +}; +#define STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION "SteamMatchMakingServers002" + +// game server flags +const uint32 k_unFavoriteFlagNone = 0x00; +const uint32 k_unFavoriteFlagFavorite = 0x01; // this game favorite entry is for the favorites list +const uint32 k_unFavoriteFlagHistory = 0x02; // this game favorite entry is for the history list + + +//----------------------------------------------------------------------------- +// Purpose: Used in ChatInfo messages - fields specific to a chat member - must fit in a uint32 +//----------------------------------------------------------------------------- +enum EChatMemberStateChange +{ + // Specific to joining / leaving the chatroom + k_EChatMemberStateChangeEntered = 0x0001, // This user has joined or is joining the chat room + k_EChatMemberStateChangeLeft = 0x0002, // This user has left or is leaving the chat room + k_EChatMemberStateChangeDisconnected = 0x0004, // User disconnected without leaving the chat first + k_EChatMemberStateChangeKicked = 0x0008, // User kicked + k_EChatMemberStateChangeBanned = 0x0010, // User kicked and banned +}; + +// returns true of the flags indicate that a user has been removed from the chat +#define BChatMemberStateChangeRemoved( rgfChatMemberStateChangeFlags ) ( rgfChatMemberStateChangeFlags & ( k_EChatMemberStateChangeDisconnected | k_EChatMemberStateChangeLeft | k_EChatMemberStateChangeKicked | k_EChatMemberStateChangeBanned ) ) + + +//----------------------------------------------------------------------------- +// Callbacks for ISteamMatchmaking (which go through the regular Steam callback registration system) +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: a server was added/removed from the favorites list, you should refresh now +//----------------------------------------------------------------------------- +struct FavoritesListChanged_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 2 }; + uint32 m_nIP; // an IP of 0 means reload the whole list, any other value means just one server + uint32 m_nQueryPort; + uint32 m_nConnPort; + uint32 m_nAppID; + uint32 m_nFlags; + bool m_bAdd; // true if this is adding the entry, otherwise it is a remove +}; + + +//----------------------------------------------------------------------------- +// Purpose: Someone has invited you to join a Lobby +// normally you don't need to do anything with this, since +// the Steam UI will also display a ' has invited you to the lobby, join?' dialog +// +// if the user outside a game chooses to join, your game will be launched with the parameter "+connect_lobby <64-bit lobby id>", +// or with the callback GameLobbyJoinRequested_t if they're already in-game +//----------------------------------------------------------------------------- +struct LobbyInvite_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 3 }; + + uint64 m_ulSteamIDUser; // Steam ID of the person making the invite + uint64 m_ulSteamIDLobby; // Steam ID of the Lobby +}; + + +//----------------------------------------------------------------------------- +// Purpose: Sent on entering a lobby, or on failing to enter +// m_EChatRoomEnterResponse will be set to k_EChatRoomEnterResponseSuccess on success, +// or a higher value on failure (see enum EChatRoomEnterResponse) +//----------------------------------------------------------------------------- +struct LobbyEnter_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 4 }; + + uint64 m_ulSteamIDLobby; // SteamID of the Lobby you have entered + uint32 m_rgfChatPermissions; // Permissions of the current user + bool m_bLocked; // If true, then only invited users may join + uint32 m_EChatRoomEnterResponse; // EChatRoomEnterResponse +}; + + +//----------------------------------------------------------------------------- +// Purpose: The lobby metadata has changed +// if m_ulSteamIDMember is the steamID of a lobby member, use GetLobbyMemberData() to access per-user details +// if m_ulSteamIDMember == m_ulSteamIDLobby, use GetLobbyData() to access lobby metadata +//----------------------------------------------------------------------------- +struct LobbyDataUpdate_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 5 }; + + uint64 m_ulSteamIDLobby; // steamID of the Lobby + uint64 m_ulSteamIDMember; // steamID of the member whose data changed, or the room itself +}; + + +//----------------------------------------------------------------------------- +// Purpose: The lobby chat room state has changed +// this is usually sent when a user has joined or left the lobby +//----------------------------------------------------------------------------- +struct LobbyChatUpdate_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 6 }; + + uint64 m_ulSteamIDLobby; // Lobby ID + uint64 m_ulSteamIDUserChanged; // user who's status in the lobby just changed - can be recipient + uint64 m_ulSteamIDMakingChange; // Chat member who made the change (different from SteamIDUserChange if kicking, muting, etc.) + // for example, if one user kicks another from the lobby, this will be set to the id of the user who initiated the kick + uint32 m_rgfChatMemberStateChange; // bitfield of EChatMemberStateChange values +}; + + +//----------------------------------------------------------------------------- +// Purpose: A chat message for this lobby has been sent +// use GetLobbyChatEntry( m_iChatID ) to retrieve the contents of this message +//----------------------------------------------------------------------------- +struct LobbyChatMsg_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 7 }; + + uint64 m_ulSteamIDLobby; // the lobby id this is in + uint64 m_ulSteamIDUser; // steamID of the user who has sent this message + uint8 m_eChatEntryType; // type of message + uint32 m_iChatID; // index of the chat entry to lookup +}; + + +//----------------------------------------------------------------------------- +// Purpose: A game created a game for all the members of the lobby to join, +// as triggered by a SetLobbyGameServer() +// it's up to the individual clients to take action on this; the usual +// game behavior is to leave the lobby and connect to the specified game server +//----------------------------------------------------------------------------- +struct LobbyGameCreated_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 9 }; + + uint64 m_ulSteamIDLobby; // the lobby we were in + uint64 m_ulSteamIDGameServer; // the new game server that has been created or found for the lobby members + uint32 m_unIP; // IP & Port of the game server (if any) + uint16 m_usPort; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Number of matching lobbies found +// iterate the returned lobbies with GetLobbyByIndex(), from values 0 to m_nLobbiesMatching-1 +//----------------------------------------------------------------------------- +struct LobbyMatchList_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 10 }; + uint32 m_nLobbiesMatching; // Number of lobbies that matched search criteria and we have SteamIDs for +}; + + +//----------------------------------------------------------------------------- +// Purpose: posted if a user is forcefully removed from a lobby +// can occur if a user loses connection to Steam +//----------------------------------------------------------------------------- +struct LobbyKicked_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 12 }; + uint64 m_ulSteamIDLobby; // Lobby + uint64 m_ulSteamIDAdmin; // User who kicked you - possibly the ID of the lobby itself + uint8 m_bKickedDueToDisconnect; // true if you were kicked from the lobby due to the user losing connection to Steam (currently always true) +}; + + +//----------------------------------------------------------------------------- +// Purpose: Result of our request to create a Lobby +// m_eResult == k_EResultOK on success +// at this point, the local user may not have finishing joining this lobby; +// game code should wait until the subsequent LobbyEnter_t callback is received +//----------------------------------------------------------------------------- +struct LobbyCreated_t +{ + enum { k_iCallback = k_iSteamMatchmakingCallbacks + 13 }; + + EResult m_eResult; // k_EResultOK - the lobby was successfully created + // k_EResultNoConnection - your Steam client doesn't have a connection to the back-end + // k_EResultTimeout - you the message to the Steam servers, but it didn't respond + // k_EResultFail - the server responded, but with an unknown internal error + // k_EResultAccessDenied - your game isn't set to allow lobbies, or your client does haven't rights to play the game + // k_EResultLimitExceeded - your game client has created too many lobbies + + uint64 m_ulSteamIDLobby; // chat room, zero if failed +}; + +// used by now obsolete RequestFriendsLobbiesResponse_t +// enum { k_iCallback = k_iSteamMatchmakingCallbacks + 14 }; +#pragma pack( pop ) + + + +#endif // ISTEAMMATCHMAKING diff --git a/public/steam/isteamnetworking.h b/public/steam/isteamnetworking.h new file mode 100644 index 0000000..fcffacb --- /dev/null +++ b/public/steam/isteamnetworking.h @@ -0,0 +1,278 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to steam managing network connections between game clients & servers +// +//============================================================================= + +#ifndef ISTEAMNETWORKING +#define ISTEAMNETWORKING +#ifdef _WIN32 +#pragma once +#endif + +#include "steamtypes.h" +#include "steamclientpublic.h" + + +// list of possible errors returned by SendP2PPacket() API +// these will be posted in the P2PSessionConnectFail_t callback +enum EP2PSessionError +{ + k_EP2PSessionErrorNone = 0, + k_EP2PSessionErrorNotRunningApp = 1, // target is not running the same game + k_EP2PSessionErrorNoRightsToApp = 2, // local user doesn't own the app that is running + k_EP2PSessionErrorDestinationNotLoggedIn = 3, // target user isn't connected to Steam + k_EP2PSessionErrorTimeout = 4, // target isn't responding, perhaps not calling AcceptP2PSessionWithUser() + // corporate firewalls can also block this (NAT traversal is not firewall traversal) + // make sure that UDP ports 3478, 4379, and 4380 are open in an outbound direction + +}; + +// SendP2PPacket() send types +// Typically k_EP2PSendUnreliable is what you want for UDP-like packets, k_EP2PSendReliable for TCP-like packets +enum EP2PSend +{ + // Basic UDP send. Packets can't be bigger than 1200 bytes (your typical MTU size). Can be lost, or arrive out of order (rare). + // The sending API does have some knowledge of the underlying connection, so if there is no NAT-traversal accomplished or + // there is a recognized adjustment happening on the connection, the packet will be batched until the connection is open again. + k_EP2PSendUnreliable = 0, + + // As above, but if the underlying p2p connection isn't yet established the packet will just be thrown away. Using this on the first + // packet sent to a remote host almost guarantees the packet will be dropped. + // This is only really useful for kinds of data that should never buffer up, i.e. voice payload packets + k_EP2PSendUnreliableNoDelay = 1, + + // Reliable message send. Can send up to 1MB of data in a single message. + // Does fragmentation/re-assembly of messages under the hood, as well as a sliding window for efficient sends of large chunks of data. + k_EP2PSendReliable = 2, + + // As above, but applies the Nagle algorithm to the send - sends will accumulate + // until the current MTU size (typically ~1200 bytes, but can change) or ~200ms has passed (Nagle algorithm). + // Useful if you want to send a set of smaller messages but have the coalesced into a single packet + // Since the reliable stream is all ordered, you can do several small message sends with k_EP2PSendReliableWithBuffering and then + // do a normal k_EP2PSendReliable to force all the buffered data to be sent. + k_EP2PSendReliableWithBuffering = 3, + +}; + + +// connection state to a specified user, returned by GetP2PSessionState() +// this is under-the-hood info about what's going on with a SendP2PPacket(), shouldn't be needed except for debuggin +#pragma pack( push, 8 ) +struct P2PSessionState_t +{ + uint8 m_bConnectionActive; // true if we've got an active open connection + uint8 m_bConnecting; // true if we're currently trying to establish a connection + uint8 m_eP2PSessionError; // last error recorded (see enum above) + uint8 m_bUsingRelay; // true if it's going through a relay server (TURN) + int32 m_nBytesQueuedForSend; + int32 m_nPacketsQueuedForSend; + uint32 m_nRemoteIP; // potential IP:Port of remote host. Could be TURN server. + uint16 m_nRemotePort; // Only exists for compatibility with older authentication api's +}; +#pragma pack( pop ) + + +// handle to a socket +typedef uint32 SNetSocket_t; // CreateP2PConnectionSocket() +typedef uint32 SNetListenSocket_t; // CreateListenSocket() + +// connection progress indicators, used by CreateP2PConnectionSocket() +enum ESNetSocketState +{ + k_ESNetSocketStateInvalid = 0, + + // communication is valid + k_ESNetSocketStateConnected = 1, + + // states while establishing a connection + k_ESNetSocketStateInitiated = 10, // the connection state machine has started + + // p2p connections + k_ESNetSocketStateLocalCandidatesFound = 11, // we've found our local IP info + k_ESNetSocketStateReceivedRemoteCandidates = 12,// we've received information from the remote machine, via the Steam back-end, about their IP info + + // direct connections + k_ESNetSocketStateChallengeHandshake = 15, // we've received a challenge packet from the server + + // failure states + k_ESNetSocketStateDisconnecting = 21, // the API shut it down, and we're in the process of telling the other end + k_ESNetSocketStateLocalDisconnect = 22, // the API shut it down, and we've completed shutdown + k_ESNetSocketStateTimeoutDuringConnect = 23, // we timed out while trying to creating the connection + k_ESNetSocketStateRemoteEndDisconnected = 24, // the remote end has disconnected from us + k_ESNetSocketStateConnectionBroken = 25, // connection has been broken; either the other end has disappeared or our local network connection has broke + +}; + +// describes how the socket is currently connected +enum ESNetSocketConnectionType +{ + k_ESNetSocketConnectionTypeNotConnected = 0, + k_ESNetSocketConnectionTypeUDP = 1, + k_ESNetSocketConnectionTypeUDPRelay = 2, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Functions for making connections and sending data between clients, +// traversing NAT's where possible +//----------------------------------------------------------------------------- +class ISteamNetworking +{ +public: + //////////////////////////////////////////////////////////////////////////////////////////// + // Session-less connection functions + // automatically establishes NAT-traversing or Relay server connections + + // Sends a P2P packet to the specified user + // UDP-like, unreliable and a max packet size of 1200 bytes + // the first packet send may be delayed as the NAT-traversal code runs + // if we can't get through to the user, an error will be posted via the callback P2PSessionConnectFail_t + // see EP2PSend enum above for the descriptions of the different ways of sending packets + virtual bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData, EP2PSend eP2PSendType ) = 0; + + // returns true if any data is available for read, and the amount of data that will need to be read + virtual bool IsP2PPacketAvailable( uint32 *pcubMsgSize ) = 0; + + // reads in a packet that has been sent from another user via SendP2PPacket() + // returns the size of the message and the steamID of the user who sent it in the last two parameters + // if the buffer passed in is too small, the message will be truncated + // this call is not blocking, and will return false if no data is available + virtual bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote ) = 0; + + // AcceptP2PSessionWithUser() should only be called in response to a P2PSessionRequest_t callback + // P2PSessionRequest_t will be posted if another user tries to send you a packet that you haven't talked to yet + // if you don't want to talk to the user, just ignore the request + // if the user continues to send you packets, another P2PSessionRequest_t will be posted periodically + // this may be called multiple times for a single user + // (if you've called SendP2PPacket() on the other user, this implicitly accepts the session request) + virtual bool AcceptP2PSessionWithUser( CSteamID steamIDRemote ) = 0; + + // call CloseP2PSessionWithUser() when you're done talking to a user, will free up resources under-the-hood + // if the remote user tries to send data to you again, another P2PSessionRequest_t callback will be posted + virtual bool CloseP2PSessionWithUser( CSteamID steamIDRemote ) = 0; + + // fills out P2PSessionState_t structure with details about the underlying connection to the user + // should only needed for debugging purposes + // returns false if no connection exists to the specified user + virtual bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionState ) = 0; + + + //////////////////////////////////////////////////////////////////////////////////////////// + // LISTEN / CONNECT style interface functions + // + // This is an older set of functions designed around the Berkeley TCP sockets model + // it's preferential that you use the above P2P functions, they're more robust + // and these older functions will be removed eventually + // + //////////////////////////////////////////////////////////////////////////////////////////// + + + // creates a socket and listens others to connect + // will trigger a SocketStatusCallback_t callback on another client connecting + // nVirtualP2PPort is the unique ID that the client will connect to, in case you have multiple ports + // this can usually just be 0 unless you want multiple sets of connections + // unIP is the local IP address to bind to + // pass in 0 if you just want the default local IP + // unPort is the port to use + // pass in 0 if you don't want users to be able to connect via IP/Port, but expect to be always peer-to-peer connections only + virtual SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay ) = 0; + + // creates a socket and begin connection to a remote destination + // can connect via a known steamID (client or game server), or directly to an IP + // on success will trigger a SocketStatusCallback_t callback + // on failure or timeout will trigger a SocketStatusCallback_t callback with a failure code in m_eSNetSocketState + virtual SNetSocket_t CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort, int nTimeoutSec, bool bAllowUseOfPacketRelay ) = 0; + virtual SNetSocket_t CreateConnectionSocket( uint32 nIP, uint16 nPort, int nTimeoutSec ) = 0; + + // disconnects the connection to the socket, if any, and invalidates the handle + // any unread data on the socket will be thrown away + // if bNotifyRemoteEnd is set, socket will not be completely destroyed until the remote end acknowledges the disconnect + virtual bool DestroySocket( SNetSocket_t hSocket, bool bNotifyRemoteEnd ) = 0; + // destroying a listen socket will automatically kill all the regular sockets generated from it + virtual bool DestroyListenSocket( SNetListenSocket_t hSocket, bool bNotifyRemoteEnd ) = 0; + + // sending data + // must be a handle to a connected socket + // data is all sent via UDP, and thus send sizes are limited to 1200 bytes; after this, many routers will start dropping packets + // use the reliable flag with caution; although the resend rate is pretty aggressive, + // it can still cause stalls in receiving data (like TCP) + virtual bool SendDataOnSocket( SNetSocket_t hSocket, void *pubData, uint32 cubData, bool bReliable ) = 0; + + // receiving data + // returns false if there is no data remaining + // fills out *pcubMsgSize with the size of the next message, in bytes + virtual bool IsDataAvailableOnSocket( SNetSocket_t hSocket, uint32 *pcubMsgSize ) = 0; + + // fills in pubDest with the contents of the message + // messages are always complete, of the same size as was sent (i.e. packetized, not streaming) + // if *pcubMsgSize < cubDest, only partial data is written + // returns false if no data is available + virtual bool RetrieveDataFromSocket( SNetSocket_t hSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize ) = 0; + + // checks for data from any socket that has been connected off this listen socket + // returns false if there is no data remaining + // fills out *pcubMsgSize with the size of the next message, in bytes + // fills out *phSocket with the socket that data is available on + virtual bool IsDataAvailable( SNetListenSocket_t hListenSocket, uint32 *pcubMsgSize, SNetSocket_t *phSocket ) = 0; + + // retrieves data from any socket that has been connected off this listen socket + // fills in pubDest with the contents of the message + // messages are always complete, of the same size as was sent (i.e. packetized, not streaming) + // if *pcubMsgSize < cubDest, only partial data is written + // returns false if no data is available + // fills out *phSocket with the socket that data is available on + virtual bool RetrieveData( SNetListenSocket_t hListenSocket, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, SNetSocket_t *phSocket ) = 0; + + // returns information about the specified socket, filling out the contents of the pointers + virtual bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, uint32 *punIPRemote, uint16 *punPortRemote ) = 0; + + // returns which local port the listen socket is bound to + // *pnIP and *pnPort will be 0 if the socket is set to listen for P2P connections only + virtual bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16 *pnPort ) = 0; + + // returns true to describe how the socket ended up connecting + virtual ESNetSocketConnectionType GetSocketConnectionType( SNetSocket_t hSocket ) = 0; + + // max packet size, in bytes + virtual int GetMaxPacketSize( SNetSocket_t hSocket ) = 0; +}; +#define STEAMNETWORKING_INTERFACE_VERSION "SteamNetworking003" + +// callbacks +#pragma pack( push, 8 ) + +// callback notification - a user wants to talk to us over the P2P channel via the SendP2PPacket() API +// in response, a call to AcceptP2PPacketsFromUser() needs to be made, if you want to talk with them +struct P2PSessionRequest_t +{ + enum { k_iCallback = k_iSteamNetworkingCallbacks + 2 }; + CSteamID m_steamIDRemote; // user who wants to talk to us +}; + + +// callback notification - packets can't get through to the specified user via the SendP2PPacket() API +// all packets queued packets unsent at this point will be dropped +// further attempts to send will retry making the connection (but will be dropped if we fail again) +struct P2PSessionConnectFail_t +{ + enum { k_iCallback = k_iSteamNetworkingCallbacks + 3 }; + CSteamID m_steamIDRemote; // user we were sending packets to + uint8 m_eP2PSessionError; // EP2PSessionError indicating why we're having trouble +}; + + +// callback notification - status of a socket has changed +// used as part of the CreateListenSocket() / CreateP2PConnectionSocket() +struct SocketStatusCallback_t +{ + enum { k_iCallback = k_iSteamNetworkingCallbacks + 1 }; + SNetSocket_t m_hSocket; // the socket used to send/receive data to the remote host + SNetListenSocket_t m_hListenSocket; // this is the server socket that we were listening on; NULL if this was an outgoing connection + CSteamID m_steamIDRemote; // remote steamID we have connected to, if it has one + int m_eSNetSocketState; // socket state, ESNetSocketState +}; + +#pragma pack( pop ) + +#endif // ISTEAMNETWORKING diff --git a/public/steam/isteamremotestorage.h b/public/steam/isteamremotestorage.h new file mode 100644 index 0000000..813a945 --- /dev/null +++ b/public/steam/isteamremotestorage.h @@ -0,0 +1,45 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: public interface to user remote file storage in Steam +// +//============================================================================= + +#ifndef ISTEAMREMOTESTORAGE_H +#define ISTEAMREMOTESTORAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +//----------------------------------------------------------------------------- +// Purpose: Functions for accessing, reading and writing files stored remotely +// and cached locally +//----------------------------------------------------------------------------- +class ISteamRemoteStorage +{ + public: + // NOTE + // + // Filenames are case-insensitive, and will be converted to lowercase automatically. + // So "foo.bar" and "Foo.bar" are the same file, and if you write "Foo.bar" then + // iterate the files, the filename returned will be "foo.bar". + // + + // file operations + virtual bool FileWrite( const char *pchFile, const void *pvData, int32 cubData ) = 0; + virtual int32 GetFileSize( const char *pchFile ) = 0; + virtual int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead ) = 0; + virtual bool FileExists( const char *pchFile ) = 0; + + // iteration + virtual int32 GetFileCount() = 0; + virtual const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes ) = 0; + + // quota management + virtual bool GetQuota( int32 *pnTotalBytes, int32 *puAvailableBytes ) = 0; +}; + +#define STEAMREMOTESTORAGE_INTERFACE_VERSION "STEAMREMOTESTORAGE_INTERFACE_VERSION002" + +#endif // ISTEAMREMOTESTORAGE_H diff --git a/public/steam/isteamuser.h b/public/steam/isteamuser.h new file mode 100644 index 0000000..6f00da5 --- /dev/null +++ b/public/steam/isteamuser.h @@ -0,0 +1,233 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to user account information in Steam +// +//============================================================================= + +#ifndef ISTEAMUSER_H +#define ISTEAMUSER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +// structure that contains client callback data +// see callbacks documentation for more details +#pragma pack( push, 8 ) +struct CallbackMsg_t +{ + HSteamUser m_hSteamUser; + int m_iCallback; + uint8 *m_pubParam; + int m_cubParam; +}; +#pragma pack( pop ) + +// reference to a steam call, to filter results by +typedef int32 HSteamCall; + +//----------------------------------------------------------------------------- +// Purpose: Functions for accessing and manipulating a steam account +// associated with one client instance +//----------------------------------------------------------------------------- +class ISteamUser +{ +public: + // returns the HSteamUser this interface represents + // this is only used internally by the API, and by a few select interfaces that support multi-user + virtual HSteamUser GetHSteamUser() = 0; + + // returns true if the Steam client current has a live connection to the Steam servers. + // If false, it means there is no active connection due to either a networking issue on the local machine, or the Steam server is down/busy. + // The Steam client will automatically be trying to recreate the connection as often as possible. + virtual bool BLoggedOn() = 0; + + // returns the CSteamID of the account currently logged into the Steam client + // a CSteamID is a unique identifier for an account, and used to differentiate users in all parts of the Steamworks API + virtual CSteamID GetSteamID() = 0; + + // Multiplayer Authentication functions + + // InitiateGameConnection() starts the state machine for authenticating the game client with the game server + // It is the client portion of a three-way handshake between the client, the game server, and the steam servers + // + // Parameters: + // void *pAuthBlob - a pointer to empty memory that will be filled in with the authentication token. + // int cbMaxAuthBlob - the number of bytes of allocated memory in pBlob. Should be at least 2048 bytes. + // CSteamID steamIDGameServer - the steamID of the game server, received from the game server by the client + // CGameID gameID - the ID of the current game. For games without mods, this is just CGameID( ) + // uint32 unIPServer, uint16 usPortServer - the IP address of the game server + // bool bSecure - whether or not the client thinks that the game server is reporting itself as secure (i.e. VAC is running) + // + // return value - returns the number of bytes written to pBlob. If the return is 0, then the buffer passed in was too small, and the call has failed + // The contents of pBlob should then be sent to the game server, for it to use to complete the authentication process. + virtual int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure ) = 0; + + // notify of disconnect + // needs to occur when the game client leaves the specified game server, needs to match with the InitiateGameConnection() call + virtual void TerminateGameConnection( uint32 unIPServer, uint16 usPortServer ) = 0; + + // Legacy functions + + // used by only a few games to track usage events + virtual void TrackAppUsageEvent( CGameID gameID, int eAppUsageEvent, const char *pchExtraInfo = "" ) = 0; + + // get the local storage folder for current Steam account to write application data, e.g. save games, configs etc. + // this will usually be something like "C:\Progam Files\Steam\userdata\\\local" + virtual bool GetUserDataFolder( char *pchBuffer, int cubBuffer ) = 0; + + // Starts voice recording. Once started, use GetVoice() to get the data + virtual void StartVoiceRecording( ) = 0; + + // Stops voice recording. Because people often release push-to-talk keys early, the system will keep recording for + // a little bit after this function is called. GetVoice() should continue to be called until it returns + // k_eVoiceResultNotRecording + virtual void StopVoiceRecording( ) = 0; + + // Determine the amount of captured audio data that is available in bytes. + // This provides both the compressed and uncompressed data. Please note that the uncompressed + // data is not the raw feed from the microphone: data may only be available if audible + // levels of speech are detected. + virtual EVoiceResult GetAvailableVoice(uint32 *pcbCompressed, uint32 *pcbUncompressed) = 0; + + // Gets the latest voice data from the microphone. Compressed data is an arbitrary format, and is meant to be handed back to + // DecompressVoice() for playback later as a binary blob. Uncompressed data is 16-bit, signed integer, 11025Hz PCM format. + // Please note that the uncompressed data is not the raw feed from the microphone: data may only be available if audible + // levels of speech are detected, and may have passed through denoising filters, etc. + // This function should be called as often as possible once recording has started; once per frame at least. + // nBytesWritten is set to the number of bytes written to pDestBuffer. + // nUncompressedBytesWritten is set to the number of bytes written to pUncompressedDestBuffer. + // You must grab both compressed and uncompressed here at the same time, if you want both. + // Matching data that is not read during this call will be thrown away. + // GetAvailableVoice() can be used to determine how much data is actually available. + virtual EVoiceResult GetVoice( bool bWantCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten, bool bWantUncompressed, void *pUncompressedDestBuffer, uint32 cbUncompressedDestBufferSize, uint32 *nUncompressBytesWritten ) = 0; + + // Decompresses a chunk of compressed data produced by GetVoice(). + // nBytesWritten is set to the number of bytes written to pDestBuffer unless the return value is k_EVoiceResultBufferTooSmall. + // In that case, nBytesWritten is set to the size of the buffer required to decompress the given + // data. The suggested buffer size for the destination buffer is 22 kilobytes. + // The output format of the data is 16-bit signed at 11025 samples per second. + virtual EVoiceResult DecompressVoice( const void *pCompressed, uint32 cbCompressed, void *pDestBuffer, uint32 cbDestBufferSize, uint32 *nBytesWritten ) = 0; + + // Retrieve ticket to be sent to the entity who wishes to authenticate you. + // pcbTicket retrieves the length of the actual ticket. + virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0; + + // Authenticate ticket from entity steamID to be sure it is valid and isnt reused + // Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse ) + virtual EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID ) = 0; + + // Stop tracking started by BeginAuthSession - called when no longer playing game with this entity + virtual void EndAuthSession( CSteamID steamID ) = 0; + + // Cancel auth ticket from GetAuthSessionTicket, called when no longer playing game with the entity you gave the ticket to + virtual void CancelAuthTicket( HAuthTicket hAuthTicket ) = 0; + + // After receiving a user's authentication data, and passing it to BeginAuthSession, use this function + // to determine if the user owns downloadable content specified by the provided AppID. + virtual EUserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID ) = 0; +}; + +#define STEAMUSER_INTERFACE_VERSION "SteamUser013" + + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: called when a connections to the Steam back-end has been established +// this means the Steam client now has a working connection to the Steam servers +// usually this will have occurred before the game has launched, and should +// only be seen if the user has dropped connection due to a networking issue +// or a Steam server update +//----------------------------------------------------------------------------- +struct SteamServersConnected_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 1 }; +}; + +//----------------------------------------------------------------------------- +// Purpose: called when a connection attempt has failed +// this will occur periodically if the Steam client is not connected, +// and has failed in it's retry to establish a connection +//----------------------------------------------------------------------------- +struct SteamServerConnectFailure_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 2 }; + EResult m_eResult; +}; + + +//----------------------------------------------------------------------------- +// Purpose: called if the client has lost connection to the Steam servers +// real-time services will be disabled until a matching SteamServersConnected_t has been posted +//----------------------------------------------------------------------------- +struct SteamServersDisconnected_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 3 }; + EResult m_eResult; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Sent by the Steam server to the client telling it to disconnect from the specified game server, +// which it may be in the process of or already connected to. +// The game client should immediately disconnect upon receiving this message. +// This can usually occur if the user doesn't have rights to play on the game server. +//----------------------------------------------------------------------------- +struct ClientGameServerDeny_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 13 }; + + uint32 m_uAppID; + uint32 m_unGameServerIP; + uint16 m_usGameServerPort; + uint16 m_bSecure; + uint32 m_uReason; +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when the callback system for this client is in an error state (and has flushed pending callbacks) +// When getting this message the client should disconnect from Steam, reset any stored Steam state and reconnect. +// This usually occurs in the rare event the Steam client has some kind of fatal error. +//----------------------------------------------------------------------------- +struct IPCFailure_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 17 }; + enum EFailureType + { + k_EFailureFlushedCallbackQueue, + k_EFailurePipeFail, + }; + uint8 m_eFailureType; +}; + + +//----------------------------------------------------------------------------- +// callback for BeginAuthSession +//----------------------------------------------------------------------------- +struct ValidateAuthTicketResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 43 }; + CSteamID m_SteamID; + EAuthSessionResponse m_eAuthSessionResponse; +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when a user has responded to a microtransaction authorization request +//----------------------------------------------------------------------------- +struct MicroTxnAuthorizationResponse_t +{ + enum { k_iCallback = k_iSteamUserCallbacks + 52 }; + + uint32 m_unAppID; // AppID for this microtransaction + uint64 m_ulOrderID; // OrderID provided for the microtransaction + uint8 m_bAuthorized; // if user authorized transaction +}; + +#pragma pack( pop ) + +#endif // ISTEAMUSER_H diff --git a/public/steam/isteamuserstats.h b/public/steam/isteamuserstats.h new file mode 100644 index 0000000..971d197 --- /dev/null +++ b/public/steam/isteamuserstats.h @@ -0,0 +1,331 @@ +//====== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to stats, achievements, and leaderboards +// +//============================================================================= + +#ifndef ISTEAMUSERSTATS_H +#define ISTEAMUSERSTATS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + +// size limit on stat or achievement name (UTF-8 encoded) +enum { k_cchStatNameMax = 128 }; + +// maximum number of bytes for a leaderboard name (UTF-8 encoded) +enum { k_cchLeaderboardNameMax = 128 }; + +// maximum number of details int32's storable for a single leaderboard entry +enum { k_cLeaderboardDetailsMax = 64 }; + +// handle to a single leaderboard +typedef uint64 SteamLeaderboard_t; + +// handle to a set of downloaded entries in a leaderboard +typedef uint64 SteamLeaderboardEntries_t; + +// type of data request, when downloading leaderboard entries +enum ELeaderboardDataRequest +{ + k_ELeaderboardDataRequestGlobal = 0, + k_ELeaderboardDataRequestGlobalAroundUser = 1, + k_ELeaderboardDataRequestFriends = 2, +}; + +// the sort order of a leaderboard +enum ELeaderboardSortMethod +{ + k_ELeaderboardSortMethodNone = 0, + k_ELeaderboardSortMethodAscending = 1, // top-score is lowest number + k_ELeaderboardSortMethodDescending = 2, // top-score is highest number +}; + +// the display type (used by the Steam Community web site) for a leaderboard +enum ELeaderboardDisplayType +{ + k_ELeaderboardDisplayTypeNone = 0, + k_ELeaderboardDisplayTypeNumeric = 1, // simple numerical score + k_ELeaderboardDisplayTypeTimeSeconds = 2, // the score represents a time, in seconds + k_ELeaderboardDisplayTypeTimeMilliSeconds = 3, // the score represents a time, in milliseconds +}; + +enum ELeaderboardUploadScoreMethod +{ + k_ELeaderboardUploadScoreMethodNone = 0, + k_ELeaderboardUploadScoreMethodKeepBest = 1, // Leaderboard will keep user's best score + k_ELeaderboardUploadScoreMethodForceUpdate = 2, // Leaderboard will always replace score with specified +}; + +// a single entry in a leaderboard, as returned by GetDownloadedLeaderboardEntry() +#pragma pack( push, 8 ) + +struct LeaderboardEntry_t +{ + CSteamID m_steamIDUser; // user with the entry - use SteamFriends()->GetFriendPersonaName() & SteamFriends()->GetFriendAvatar() to get more info + int32 m_nGlobalRank; // [1..N], where N is the number of users with an entry in the leaderboard + int32 m_nScore; // score as set in the leaderboard + int32 m_cDetails; // number of int32 details available for this entry +}; + +#pragma pack( pop ) + + +//----------------------------------------------------------------------------- +// Purpose: Functions for accessing stats, achievements, and leaderboard information +//----------------------------------------------------------------------------- +class ISteamUserStats +{ +public: + // Ask the server to send down this user's data and achievements for this game + virtual bool RequestCurrentStats() = 0; + + // Data accessors + virtual bool GetStat( const char *pchName, int32 *pData ) = 0; + virtual bool GetStat( const char *pchName, float *pData ) = 0; + + // Set / update data + virtual bool SetStat( const char *pchName, int32 nData ) = 0; + virtual bool SetStat( const char *pchName, float fData ) = 0; + virtual bool UpdateAvgRateStat( const char *pchName, float flCountThisSession, double dSessionLength ) = 0; + + // Achievement flag accessors + virtual bool GetAchievement( const char *pchName, bool *pbAchieved ) = 0; + virtual bool SetAchievement( const char *pchName ) = 0; + virtual bool ClearAchievement( const char *pchName ) = 0; + + // Get the achievement status, and the time it was unlocked if unlocked. + // If the return value is true, but the unlock time is zero, that means it was unlocked before Steam + // began tracking achievement unlock times (December 2009). Time is seconds since January 1, 1970. + virtual bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) = 0; + + // Store the current data on the server, will get a callback when set + // And one callback for every new achievement + // + // If the callback has a result of k_EResultInvalidParam, one or more stats + // uploaded has been rejected, either because they broke constraints + // or were out of date. In this case the server sends back updated values. + // The stats should be re-iterated to keep in sync. + virtual bool StoreStats() = 0; + + // Achievement / GroupAchievement metadata + + // Gets the icon of the achievement, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set. + // A return value of 0 may indicate we are still fetching data, and you can wait for the UserAchievementIconReady_t callback + // which will notify you when the bits are actually read. If the callback still returns zero, then there is no image set + // and there never will be. + virtual int GetAchievementIcon( const char *pchName ) = 0; + // Get general attributes (display name / text, etc) for an Achievement + virtual const char *GetAchievementDisplayAttribute( const char *pchName, const char *pchKey ) = 0; + + // Achievement progress - triggers an AchievementProgress callback, that is all. + // Calling this w/ N out of N progress will NOT set the achievement, the game must still do that. + virtual bool IndicateAchievementProgress( const char *pchName, uint32 nCurProgress, uint32 nMaxProgress ) = 0; + + // Friends stats & achievements + + // downloads stats for the user + // returns a UserStatsReceived_t received when completed + // if the other user has no stats, UserStatsReceived_t.m_eResult will be set to k_EResultFail + // these stats won't be auto-updated; you'll need to call RequestUserStats() again to refresh any data + virtual SteamAPICall_t RequestUserStats( CSteamID steamIDUser ) = 0; + + // requests stat information for a user, usable after a successful call to RequestUserStats() + virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, int32 *pData ) = 0; + virtual bool GetUserStat( CSteamID steamIDUser, const char *pchName, float *pData ) = 0; + virtual bool GetUserAchievement( CSteamID steamIDUser, const char *pchName, bool *pbAchieved ) = 0; + // See notes for GetAchievementAndUnlockTime above + virtual bool GetUserAchievementAndUnlockTime( CSteamID steamIDUser, const char *pchName, bool *pbAchieved, uint32 *punUnlockTime ) = 0; + + // Reset stats + virtual bool ResetAllStats( bool bAchievementsToo ) = 0; + + // Leaderboard functions + + // asks the Steam back-end for a leaderboard by name, and will create it if it's not yet + // This call is asynchronous, with the result returned in LeaderboardFindResult_t + virtual SteamAPICall_t FindOrCreateLeaderboard( const char *pchLeaderboardName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType ) = 0; + + // as above, but won't create the leaderboard if it's not found + // This call is asynchronous, with the result returned in LeaderboardFindResult_t + virtual SteamAPICall_t FindLeaderboard( const char *pchLeaderboardName ) = 0; + + // returns the name of a leaderboard + virtual const char *GetLeaderboardName( SteamLeaderboard_t hSteamLeaderboard ) = 0; + + // returns the total number of entries in a leaderboard, as of the last request + virtual int GetLeaderboardEntryCount( SteamLeaderboard_t hSteamLeaderboard ) = 0; + + // returns the sort method of the leaderboard + virtual ELeaderboardSortMethod GetLeaderboardSortMethod( SteamLeaderboard_t hSteamLeaderboard ) = 0; + + // returns the display type of the leaderboard + virtual ELeaderboardDisplayType GetLeaderboardDisplayType( SteamLeaderboard_t hSteamLeaderboard ) = 0; + + // Asks the Steam back-end for a set of rows in the leaderboard. + // This call is asynchronous, with the result returned in LeaderboardScoresDownloaded_t + // LeaderboardScoresDownloaded_t will contain a handle to pull the results from GetDownloadedLeaderboardEntries() (below) + // You can ask for more entries than exist, and it will return as many as do exist. + // k_ELeaderboardDataRequestGlobal requests rows in the leaderboard from the full table, with nRangeStart & nRangeEnd in the range [1, TotalEntries] + // k_ELeaderboardDataRequestGlobalAroundUser requests rows around the current user, nRangeStart being negate + // e.g. DownloadLeaderboardEntries( hLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -3, 3 ) will return 7 rows, 3 before the user, 3 after + // k_ELeaderboardDataRequestFriends requests all the rows for friends of the current user + virtual SteamAPICall_t DownloadLeaderboardEntries( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardDataRequest eLeaderboardDataRequest, int nRangeStart, int nRangeEnd ) = 0; + + // Returns data about a single leaderboard entry + // use a for loop from 0 to LeaderboardScoresDownloaded_t::m_cEntryCount to get all the downloaded entries + // e.g. + // void OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded ) + // { + // for ( int index = 0; index < pLeaderboardScoresDownloaded->m_cEntryCount; index++ ) + // { + // LeaderboardEntry_t leaderboardEntry; + // int32 details[3]; // we know this is how many we've stored previously + // GetDownloadedLeaderboardEntry( pLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, index, &leaderboardEntry, details, 3 ); + // assert( leaderboardEntry.m_cDetails == 3 ); + // ... + // } + // once you've accessed all the entries, the data will be free'd, and the SteamLeaderboardEntries_t handle will become invalid + virtual bool GetDownloadedLeaderboardEntry( SteamLeaderboardEntries_t hSteamLeaderboardEntries, int index, LeaderboardEntry_t *pLeaderboardEntry, int32 *pDetails, int cDetailsMax ) = 0; + + // Uploads a user score to the Steam back-end. + // This call is asynchronous, with the result returned in LeaderboardScoreUploaded_t + // Details are extra game-defined information regarding how the user got that score + // pScoreDetails points to an array of int32's, cScoreDetailsCount is the number of int32's in the list + virtual SteamAPICall_t UploadLeaderboardScore( SteamLeaderboard_t hSteamLeaderboard, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int32 nScore, const int32 *pScoreDetails, int cScoreDetailsCount ) = 0; + + // Retrieves the number of players currently playing your game (online + offline) + // This call is asynchronous, with the result returned in NumberOfCurrentPlayers_t + virtual SteamAPICall_t GetNumberOfCurrentPlayers() = 0; + +}; + +#define STEAMUSERSTATS_INTERFACE_VERSION "STEAMUSERSTATS_INTERFACE_VERSION007" + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: called when the latests stats and achievements have been received +// from the server +//----------------------------------------------------------------------------- +struct UserStatsReceived_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 1 }; + uint64 m_nGameID; // Game these stats are for + EResult m_eResult; // Success / error fetching the stats + CSteamID m_steamIDUser; // The user for whom the stats are retrieved for +}; + + +//----------------------------------------------------------------------------- +// Purpose: result of a request to store the user stats for a game +//----------------------------------------------------------------------------- +struct UserStatsStored_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 2 }; + uint64 m_nGameID; // Game these stats are for + EResult m_eResult; // success / error +}; + + +//----------------------------------------------------------------------------- +// Purpose: result of a request to store the achievements for a game, or an +// "indicate progress" call. If both m_nCurProgress and m_nMaxProgress +// are zero, that means the achievement has been fully unlocked. +//----------------------------------------------------------------------------- +struct UserAchievementStored_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 3 }; + + uint64 m_nGameID; // Game this is for + bool m_bGroupAchievement; // if this is a "group" achievement + char m_rgchAchievementName[k_cchStatNameMax]; // name of the achievement + uint32 m_nCurProgress; // current progress towards the achievement + uint32 m_nMaxProgress; // "out of" this many +}; + + +//----------------------------------------------------------------------------- +// Purpose: call result for finding a leaderboard, returned as a result of FindOrCreateLeaderboard() or FindLeaderboard() +// use CCallResult<> to map this async result to a member function +//----------------------------------------------------------------------------- +struct LeaderboardFindResult_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 4 }; + SteamLeaderboard_t m_hSteamLeaderboard; // handle to the leaderboard serarched for, 0 if no leaderboard found + uint8 m_bLeaderboardFound; // 0 if no leaderboard found +}; + + +//----------------------------------------------------------------------------- +// Purpose: call result indicating scores for a leaderboard have been downloaded and are ready to be retrieved, returned as a result of DownloadLeaderboardEntries() +// use CCallResult<> to map this async result to a member function +//----------------------------------------------------------------------------- +struct LeaderboardScoresDownloaded_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 5 }; + SteamLeaderboard_t m_hSteamLeaderboard; + SteamLeaderboardEntries_t m_hSteamLeaderboardEntries; // the handle to pass into GetDownloadedLeaderboardEntries() + int m_cEntryCount; // the number of entries downloaded +}; + + +//----------------------------------------------------------------------------- +// Purpose: call result indicating scores has been uploaded, returned as a result of UploadLeaderboardScore() +// use CCallResult<> to map this async result to a member function +//----------------------------------------------------------------------------- +struct LeaderboardScoreUploaded_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 6 }; + uint8 m_bSuccess; // 1 if the call was successful + SteamLeaderboard_t m_hSteamLeaderboard; // the leaderboard handle that was + int32 m_nScore; // the score that was attempted to set + uint8 m_bScoreChanged; // true if the score in the leaderboard change, false if the existing score was better + int m_nGlobalRankNew; // the new global rank of the user in this leaderboard + int m_nGlobalRankPrevious; // the previous global rank of the user in this leaderboard; 0 if the user had no existing entry in the leaderboard +}; + +struct NumberOfCurrentPlayers_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 7 }; + uint8 m_bSuccess; // 1 if the call was successful + int32 m_cPlayers; // Number of players currently playing +}; + + +//----------------------------------------------------------------------------- +// Purpose: Callback indicating that a user's stats have been unloaded. +// Call RequestUserStats again to access stats for this user +//----------------------------------------------------------------------------- +struct UserStatsUnloaded_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 8 }; + CSteamID m_steamIDUser; // User whose stats have been unloaded +}; + + + +//----------------------------------------------------------------------------- +// Purpose: Callback indicating that an achievement icon has been fetched +//----------------------------------------------------------------------------- +struct UserAchievementIconFetched_t +{ + enum { k_iCallback = k_iSteamUserStatsCallbacks + 9 }; + + CGameID m_nGameID; // Game this is for + char m_rgchAchievementName[k_cchStatNameMax]; // name of the achievement + bool m_bAchieved; // Is the icon for the achieved or not achieved version? + int m_nIconHandle; // Handle to the image, which can be used in ClientUtils()->GetImageRGBA(), 0 means no image is set for the achievement +}; + +// +// IMPORTANT! k_iSteamUserStatsCallbacks + 10 is used, see iclientuserstats.h +// + +#pragma pack( pop ) + + +#endif // ISTEAMUSER_H diff --git a/public/steam/isteamutils.h b/public/steam/isteamutils.h new file mode 100644 index 0000000..1210896 --- /dev/null +++ b/public/steam/isteamutils.h @@ -0,0 +1,182 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: interface to utility functions in Steam +// +//============================================================================= + +#ifndef ISTEAMUTILS_H +#define ISTEAMUTILS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" + + +// Steam API call failure results +enum ESteamAPICallFailure +{ + k_ESteamAPICallFailureNone = -1, // no failure + k_ESteamAPICallFailureSteamGone = 0, // the local Steam process has gone away + k_ESteamAPICallFailureNetworkFailure = 1, // the network connection to Steam has been broken, or was already broken + // SteamServersDisconnected_t callback will be sent around the same time + // SteamServersConnected_t will be sent when the client is able to talk to the Steam servers again + k_ESteamAPICallFailureInvalidHandle = 2, // the SteamAPICall_t handle passed in no longer exists + k_ESteamAPICallFailureMismatchedCallback = 3,// GetAPICallResult() was called with the wrong callback type for this API call +}; + +// function prototype for warning message hook +#if defined( POSIX ) +#define __cdecl +#endif +extern "C" typedef void (__cdecl *SteamAPIWarningMessageHook_t)(int, const char *); + +//----------------------------------------------------------------------------- +// Purpose: interface to user independent utility functions +//----------------------------------------------------------------------------- +class ISteamUtils +{ +public: + // return the number of seconds since the user + virtual uint32 GetSecondsSinceAppActive() = 0; + virtual uint32 GetSecondsSinceComputerActive() = 0; + + // the universe this client is connecting to + virtual EUniverse GetConnectedUniverse() = 0; + + // Steam server time - in PST, number of seconds since January 1, 1970 (i.e unix time) + virtual uint32 GetServerRealTime() = 0; + + // returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database) + // e.g "US" or "UK". + virtual const char *GetIPCountry() = 0; + + // returns true if the image exists, and valid sizes were filled out + virtual bool GetImageSize( int iImage, uint32 *pnWidth, uint32 *pnHeight ) = 0; + + // returns true if the image exists, and the buffer was successfully filled out + // results are returned in RGBA format + // the destination buffer size should be 4 * height * width * sizeof(char) + virtual bool GetImageRGBA( int iImage, uint8 *pubDest, int nDestBufferSize ) = 0; + + // returns the IP of the reporting server for valve - currently only used in Source engine games + virtual bool GetCSERIPPort( uint32 *unIP, uint16 *usPort ) = 0; + + // return the amount of battery power left in the current system in % [0..100], 255 for being on AC power + virtual uint8 GetCurrentBatteryPower() = 0; + + // returns the appID of the current process + virtual uint32 GetAppID() = 0; + + // Sets the position where the overlay instance for the currently calling game should show notifications. + // This position is per-game and if this function is called from outside of a game context it will do nothing. + virtual void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition ) = 0; + + // API asynchronous call results + // can be used directly, but more commonly used via the callback dispatch API (see steam_api.h) + virtual bool IsAPICallCompleted( SteamAPICall_t hSteamAPICall, bool *pbFailed ) = 0; + virtual ESteamAPICallFailure GetAPICallFailureReason( SteamAPICall_t hSteamAPICall ) = 0; + virtual bool GetAPICallResult( SteamAPICall_t hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed ) = 0; + + // this needs to be called every frame to process matchmaking results + // redundant if you're already calling SteamAPI_RunCallbacks() + virtual void RunFrame() = 0; + + // returns the number of IPC calls made since the last time this function was called + // Used for perf debugging so you can understand how many IPC calls your game makes per frame + // Every IPC call is at minimum a thread context switch if not a process one so you want to rate + // control how often you do them. + virtual uint32 GetIPCCallCount() = 0; + + // API warning handling + // 'int' is the severity; 0 for msg, 1 for warning + // 'const char *' is the text of the message + // callbacks will occur directly after the API function is called that generated the warning or message + virtual void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction ) = 0; + + // Returns true if the overlay is running & the user can access it. The overlay process could take a few seconds to + // start & hook the game process, so this function will initially return false while the overlay is loading. + virtual bool IsOverlayEnabled() = 0; + + // Normally this call is unneeded if your game has a constantly running frame loop that calls the + // D3D Present API, or OGL SwapBuffers API every frame. + // + // However, if you have a game that only refreshes the screen on an event driven basis then that can break + // the overlay, as it uses your Present/SwapBuffers calls to drive it's internal frame loop and it may also + // need to Present() to the screen any time an even needing a notification happens or when the overlay is + // brought up over the game by a user. You can use this API to ask the overlay if it currently need a present + // in that case, and then you can check for this periodically (roughly 33hz is desirable) and make sure you + // refresh the screen with Present or SwapBuffers to allow the overlay to do it's work. + virtual bool BOverlayNeedsPresent() = 0; + + // Asynchronous call to check if file is signed, result is returned in CheckFileSignature_t + virtual SteamAPICall_t CheckFileSignature( const char *szFileName ) = 0; +}; + +#define STEAMUTILS_INTERFACE_VERSION "SteamUtils005" + + +// callbacks +#pragma pack( push, 8 ) + +//----------------------------------------------------------------------------- +// Purpose: The country of the user changed +//----------------------------------------------------------------------------- +struct IPCountry_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 1 }; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Fired when running on a laptop and less than 10 minutes of battery is left, fires then every minute +//----------------------------------------------------------------------------- +struct LowBatteryPower_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 2 }; + uint8 m_nMinutesBatteryLeft; +}; + + +//----------------------------------------------------------------------------- +// Purpose: called when a SteamAsyncCall_t has completed (or failed) +//----------------------------------------------------------------------------- +struct SteamAPICallCompleted_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 3 }; + SteamAPICall_t m_hAsyncCall; +}; + + +//----------------------------------------------------------------------------- +// called when Steam wants to shutdown +//----------------------------------------------------------------------------- +struct SteamShutdown_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 4 }; +}; + +//----------------------------------------------------------------------------- +// results for CheckFileSignature +//----------------------------------------------------------------------------- +enum ECheckFileSignature +{ + k_ECheckFileSignatureInvalidSignature = 0, + k_ECheckFileSignatureValidSignature = 1, + k_ECheckFileSignatureFileNotFound = 2, + k_ECheckFileSignatureNoSignaturesFoundForThisApp = 3, + k_ECheckFileSignatureNoSignaturesFoundForThisFile = 4, +}; + +//----------------------------------------------------------------------------- +// callback for CheckFileSignature +//----------------------------------------------------------------------------- +struct CheckFileSignature_t +{ + enum { k_iCallback = k_iSteamUtilsCallbacks + 5 }; + ECheckFileSignature m_eCheckFileSignature; +}; + +#pragma pack( pop ) + +#endif // ISTEAMUTILS_H diff --git a/public/steam/matchmakingtypes.h b/public/steam/matchmakingtypes.h new file mode 100644 index 0000000..f451893 --- /dev/null +++ b/public/steam/matchmakingtypes.h @@ -0,0 +1,230 @@ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef MATCHMAKINGTYPES_H +#define MATCHMAKINGTYPES_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef POSIX +#ifndef _snprintf +#define _snprintf snprintf +#endif +#endif + +#include +#include + +struct MatchMakingKeyValuePair_t +{ + MatchMakingKeyValuePair_t() { m_szKey[0] = m_szValue[0] = 0; } + MatchMakingKeyValuePair_t( const char *pchKey, const char *pchValue ) + { + strncpy( m_szKey, pchKey, sizeof(m_szKey) ); // this is a public header, use basic c library string funcs only! + strncpy( m_szValue, pchValue, sizeof(m_szValue) ); + } + char m_szKey[ 256 ]; + char m_szValue[ 256 ]; +}; + + +enum EMatchMakingServerResponse +{ + eServerResponded = 0, + eServerFailedToRespond, + eNoServersListedOnMasterServer // for the Internet query type, returned in response callback if no servers of this type match +}; + +// servernetadr_t is all the addressing info the serverbrowser needs to know about a game server, +// namely: its IP, its connection port, and its query port. +class servernetadr_t +{ +public: + + void Init( unsigned int ip, uint16 usQueryPort, uint16 usConnectionPort ); +#ifdef NETADR_H + void Init( const netadr_t &ipAndQueryPort, uint16 usConnectionPort ); + netadr_t& GetIPAndQueryPort(); +#endif + + // Access the query port. + uint16 GetQueryPort() const; + void SetQueryPort( uint16 usPort ); + + // Access the connection port. + uint16 GetConnectionPort() const; + void SetConnectionPort( uint16 usPort ); + + // Access the IP + uint32 GetIP() const; + void SetIP( uint32 ); + + // This gets the 'a.b.c.d:port' string with the connection port (instead of the query port). + const char *GetConnectionAddressString() const; + const char *GetQueryAddressString() const; + + // Comparison operators and functions. + bool operator<(const servernetadr_t &netadr) const; + void operator=( const servernetadr_t &that ) + { + m_usConnectionPort = that.m_usConnectionPort; + m_usQueryPort = that.m_usQueryPort; + m_unIP = that.m_unIP; + } + + +private: + const char *ToString( uint32 unIP, uint16 usPort ) const; + uint16 m_usConnectionPort; // (in HOST byte order) + uint16 m_usQueryPort; + uint32 m_unIP; +}; + + +inline void servernetadr_t::Init( unsigned int ip, uint16 usQueryPort, uint16 usConnectionPort ) +{ + m_unIP = ip; + m_usQueryPort = usQueryPort; + m_usConnectionPort = usConnectionPort; +} + +#ifdef NETADR_H +inline void servernetadr_t::Init( const netadr_t &ipAndQueryPort, uint16 usConnectionPort ) +{ + Init( ipAndQueryPort.GetIP(), ipAndQueryPort.GetPort(), usConnectionPort ); +} + +inline netadr_t& servernetadr_t::GetIPAndQueryPort() +{ + static netadr_t netAdr; + netAdr.SetIP( m_unIP ); + netAdr.SetPort( m_usQueryPort ); + return netAdr; +} +#endif + +inline uint16 servernetadr_t::GetQueryPort() const +{ + return m_usQueryPort; +} + +inline void servernetadr_t::SetQueryPort( uint16 usPort ) +{ + m_usQueryPort = usPort; +} + +inline uint16 servernetadr_t::GetConnectionPort() const +{ + return m_usConnectionPort; +} + +inline void servernetadr_t::SetConnectionPort( uint16 usPort ) +{ + m_usConnectionPort = usPort; +} + +inline uint32 servernetadr_t::GetIP() const +{ + return m_unIP; +} + +inline void servernetadr_t::SetIP( uint32 unIP ) +{ + m_unIP = unIP; +} + +inline const char *servernetadr_t::ToString( uint32 unIP, uint16 usPort ) const +{ + static char s[4][64]; + static int nBuf = 0; + unsigned char *ipByte = (unsigned char *)&unIP; + _snprintf (s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[3]), (int)(ipByte[2]), (int)(ipByte[1]), (int)(ipByte[0]), usPort ); + const char *pchRet = s[nBuf]; + ++nBuf; + nBuf %= ( (sizeof(s)/sizeof(s[0])) ); + return pchRet; +} + +inline const char* servernetadr_t::GetConnectionAddressString() const +{ + return ToString( m_unIP, m_usConnectionPort ); +} + +inline const char* servernetadr_t::GetQueryAddressString() const +{ + return ToString( m_unIP, m_usQueryPort ); +} + +inline bool servernetadr_t::operator<(const servernetadr_t &netadr) const +{ + return ( m_unIP < netadr.m_unIP ) || ( m_unIP == netadr.m_unIP && m_usQueryPort < netadr.m_usQueryPort ); +} + +//----------------------------------------------------------------------------- +// Purpose: Data describing a single server +//----------------------------------------------------------------------------- +class gameserveritem_t +{ +public: + gameserveritem_t(); + + const char* GetName() const; + void SetName( const char *pName ); + +public: + servernetadr_t m_NetAdr; // IP/Query Port/Connection Port for this server + int m_nPing; // current ping time in milliseconds + bool m_bHadSuccessfulResponse; // server has responded successfully in the past + bool m_bDoNotRefresh; // server is marked as not responding and should no longer be refreshed + char m_szGameDir[32]; // current game directory + char m_szMap[32]; // current map + char m_szGameDescription[64]; // game description + uint32 m_nAppID; // Steam App ID of this server + int m_nPlayers; // current number of players on the server + int m_nMaxPlayers; // Maximum players that can join this server + int m_nBotPlayers; // Number of bots (i.e simulated players) on this server + bool m_bPassword; // true if this server needs a password to join + bool m_bSecure; // Is this server protected by VAC + uint32 m_ulTimeLastPlayed; // time (in unix time) when this server was last played on (for favorite/history servers) + int m_nServerVersion; // server version as reported to Steam + +private: + char m_szServerName[64]; // Game server name + + // For data added after SteamMatchMaking001 add it here +public: + char m_szGameTags[128]; // the tags this server exposes + CSteamID m_steamID; // steamID of the game server - invalid if it's doesn't have one (old server, or not connected to Steam) +}; + + +inline gameserveritem_t::gameserveritem_t() +{ + m_szGameDir[0] = m_szMap[0] = m_szGameDescription[0] = m_szServerName[0] = 0; + m_bHadSuccessfulResponse = m_bDoNotRefresh = m_bPassword = m_bSecure = false; + m_nPing = m_nAppID = m_nPlayers = m_nMaxPlayers = m_nBotPlayers = m_ulTimeLastPlayed = m_nServerVersion = 0; + m_szGameTags[0] = 0; +} + +inline const char* gameserveritem_t::GetName() const +{ + // Use the IP address as the name if nothing is set yet. + if ( m_szServerName[0] == 0 ) + return m_NetAdr.GetConnectionAddressString(); + else + return m_szServerName; +} + +inline void gameserveritem_t::SetName( const char *pName ) +{ + strncpy( m_szServerName, pName, sizeof( m_szServerName ) ); +} + + +#endif // MATCHMAKINGTYPES_H diff --git a/public/steam/steam_api.h b/public/steam/steam_api.h new file mode 100644 index 0000000..389dea0 --- /dev/null +++ b/public/steam/steam_api.h @@ -0,0 +1,467 @@ +//====== Copyright 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef STEAM_API_H +#define STEAM_API_H +#ifdef _WIN32 +#pragma once +#endif + +#include "isteamclient.h" +#include "isteamuser.h" +#include "isteamfriends.h" +#include "isteamutils.h" +#include "isteammatchmaking.h" +#include "isteamuserstats.h" +#include "isteamapps.h" +#include "isteamnetworking.h" +#include "isteamremotestorage.h" + +// Steam API export macro +#if defined( _WIN32 ) && !defined( _X360 ) + #if defined( STEAM_API_EXPORTS ) + #define S_API extern "C" __declspec( dllexport ) + #elif defined( STEAM_API_NODLL ) + #define S_API extern "C" + #else + #define S_API extern "C" __declspec( dllimport ) + #endif // STEAM_API_EXPORTS +#elif defined( GNUC ) + #if defined( STEAM_API_EXPORTS ) + #define S_API extern "C" __attribute__ ((visibility("default"))) + #else + #define S_API extern "C" + #endif // STEAM_API_EXPORTS +#else // !WIN32 + #if defined( STEAM_API_EXPORTS ) + #define S_API extern "C" + #else + #define S_API extern "C" + #endif // STEAM_API_EXPORTS +#endif + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// Steam API setup & shutdown +// +// These functions manage loading, initializing and shutdown of the steamclient.dll +// +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +// S_API void SteamAPI_Init(); (see below) +S_API void SteamAPI_Shutdown(); + +// checks if a local Steam client is running +S_API bool SteamAPI_IsSteamRunning(); + +// Detects if your executable was launched through the Steam client, and restarts your game through +// the client if necessary. The Steam client will be started if it is not running. +// +// Returns: true if your executable was NOT launched through the Steam client. This function will +// then start your application through the client. Your current process should exit. +// +// false if your executable was started through the Steam client or a steam_appid.txt file +// is present in your game's directory (for development). Your current process should continue. +// +// NOTE: This function should be used only if you are using CEG or not using Steam's DRM. Once applied +// to your executable, Steam's DRM will handle restarting through Steam if necessary. +S_API bool SteamAPI_RestartAppIfNecessary( uint32 unOwnAppID ); + +// crash dump recording functions +S_API void SteamAPI_WriteMiniDump( uint32 uStructuredExceptionCode, void* pvExceptionInfo, uint32 uBuildID ); +S_API void SteamAPI_SetMiniDumpComment( const char *pchMsg ); + +// this should be called before the game initialized the steam APIs +// pchDate should be of the format "Mmm dd yyyy" (such as from the __DATE__ macro ) +// pchTime should be of the format "hh:mm:ss" (such as from the __TIME__ macro ) +// bFullMemoryDumps (Win32 only) -- writes out a uuid-full.dmp in the client/dumps folder +// pvContext-- can be NULL, will be the void * context passed into m_pfnPreMinidumpCallback +// PFNPreMinidumpCallback m_pfnPreMinidumpCallback -- optional callback which occurs just before a .dmp file is written during a crash. Applications can hook this to allow adding additional information into the .dmp comment stream. +S_API void SteamAPI_UseBreakpadCrashHandler( char const *pchVersion, char const *pchDate, char const *pchTime, bool bFullMemoryDumps, void *pvContext, PFNPreMinidumpCallback m_pfnPreMinidumpCallback ); +S_API void SteamAPI_SetBreakpadAppID( uint32 unAppID ); + +// interface pointers, configured by SteamAPI_Init() +S_API ISteamClient *SteamClient(); + + +// +// VERSION_SAFE_STEAM_API_INTERFACES is usually not necessary, but it provides safety against releasing +// new steam_api.dll's without recompiling/rereleasing modules that use it. +// +// If you use VERSION_SAFE_STEAM_API_INTERFACES, then you should call SteamAPI_InitSafe(). Also, to get the +// Steam interfaces, you must create and Init() a CSteamAPIContext (below) and use the interfaces in there. +// +// If you don't use VERSION_SAFE_STEAM_API_INTERFACES, then you can use SteamAPI_Init() and the SteamXXXX() +// functions below to get at the Steam interfaces. +// +#ifdef VERSION_SAFE_STEAM_API_INTERFACES +S_API bool SteamAPI_InitSafe(); +#else +S_API bool SteamAPI_Init(); + +S_API ISteamUser *SteamUser(); +S_API ISteamFriends *SteamFriends(); +S_API ISteamUtils *SteamUtils(); +S_API ISteamMatchmaking *SteamMatchmaking(); +S_API ISteamUserStats *SteamUserStats(); +S_API ISteamApps *SteamApps(); +S_API ISteamNetworking *SteamNetworking(); +S_API ISteamMatchmakingServers *SteamMatchmakingServers(); +S_API ISteamRemoteStorage *SteamRemoteStorage(); +#endif // VERSION_SAFE_STEAM_API_INTERFACES + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// steam callback helper functions +// +// The following classes/macros are used to be able to easily multiplex callbacks +// from the Steam API into various objects in the app in a thread-safe manner +// +// These functors are triggered via the SteamAPI_RunCallbacks() function, mapping the callback +// to as many functions/objects as are registered to it +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +S_API void SteamAPI_RunCallbacks(); + + + +// functions used by the utility CCallback objects to receive callbacks +S_API void SteamAPI_RegisterCallback( class CCallbackBase *pCallback, int iCallback ); +S_API void SteamAPI_UnregisterCallback( class CCallbackBase *pCallback ); +// functions used by the utility CCallResult objects to receive async call results +S_API void SteamAPI_RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); +S_API void SteamAPI_UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall ); + + +//----------------------------------------------------------------------------- +// Purpose: base for callbacks, +// used only by CCallback, shouldn't be used directly +//----------------------------------------------------------------------------- +class CCallbackBase +{ +public: + CCallbackBase() { m_nCallbackFlags = 0; m_iCallback = 0; } + // don't add a virtual destructor because we export this binary interface across dll's + virtual void Run( void *pvParam ) = 0; + virtual void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) = 0; + int GetICallback() { return m_iCallback; } + virtual int GetCallbackSizeBytes() = 0; + +protected: + enum { k_ECallbackFlagsRegistered = 0x01, k_ECallbackFlagsGameServer = 0x02 }; + uint8 m_nCallbackFlags; + int m_iCallback; + friend class CCallbackMgr; +}; + + +//----------------------------------------------------------------------------- +// Purpose: maps a steam async call result to a class member function +// template params: T = local class, P = parameter struct +//----------------------------------------------------------------------------- +template< class T, class P > +class CCallResult : private CCallbackBase +{ +public: + typedef void (T::*func_t)( P*, bool ); + + CCallResult() + { + m_hAPICall = k_uAPICallInvalid; + m_pObj = NULL; + m_Func = NULL; + m_iCallback = P::k_iCallback; + } + + void Set( SteamAPICall_t hAPICall, T *p, func_t func ) + { + if ( m_hAPICall ) + SteamAPI_UnregisterCallResult( this, m_hAPICall ); + + m_hAPICall = hAPICall; + m_pObj = p; + m_Func = func; + + if ( hAPICall ) + SteamAPI_RegisterCallResult( this, hAPICall ); + } + + bool IsActive() const + { + return ( m_hAPICall != k_uAPICallInvalid ); + } + + void Cancel() + { + if ( m_hAPICall != k_uAPICallInvalid ) + { + SteamAPI_UnregisterCallResult( this, m_hAPICall ); + m_hAPICall = k_uAPICallInvalid; + } + + } + + ~CCallResult() + { + Cancel(); + } + +private: + virtual void Run( void *pvParam ) + { + m_hAPICall = k_uAPICallInvalid; // caller unregisters for us + (m_pObj->*m_Func)( (P *)pvParam, false ); + } + void Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall ) + { + if ( hSteamAPICall == m_hAPICall ) + { + m_hAPICall = k_uAPICallInvalid; // caller unregisters for us + (m_pObj->*m_Func)( (P *)pvParam, bIOFailure ); + } + } + int GetCallbackSizeBytes() + { + return sizeof( P ); + } + + SteamAPICall_t m_hAPICall; + T *m_pObj; + func_t m_Func; +}; + + + +//----------------------------------------------------------------------------- +// Purpose: maps a steam callback to a class member function +// template params: T = local class, P = parameter struct +//----------------------------------------------------------------------------- +template< class T, class P, bool bGameServer > +class CCallback : private CCallbackBase +{ +public: + typedef void (T::*func_t)( P* ); + + // If you can't support constructing a callback with the correct parameters + // then uncomment the empty constructor below and manually call + // ::Register() for your object + // Or, just call the regular constructor with (NULL, NULL) + // CCallback() {} + + // constructor for initializing this object in owner's constructor + CCallback( T *pObj, func_t func ) : m_pObj( pObj ), m_Func( func ) + { + if ( pObj && func ) + Register( pObj, func ); + } + + ~CCallback() + { + if ( m_nCallbackFlags & k_ECallbackFlagsRegistered ) + Unregister(); + } + + // manual registration of the callback + void Register( T *pObj, func_t func ) + { + if ( !pObj || !func ) + return; + + if ( m_nCallbackFlags & k_ECallbackFlagsRegistered ) + Unregister(); + + if ( bGameServer ) + { + m_nCallbackFlags |= k_ECallbackFlagsGameServer; + } + m_pObj = pObj; + m_Func = func; + // SteamAPI_RegisterCallback sets k_ECallbackFlagsRegistered + SteamAPI_RegisterCallback( this, P::k_iCallback ); + } + + void Unregister() + { + // SteamAPI_UnregisterCallback removes k_ECallbackFlagsRegistered + SteamAPI_UnregisterCallback( this ); + } + + void SetGameserverFlag() { m_nCallbackFlags |= k_ECallbackFlagsGameServer; } +private: + virtual void Run( void *pvParam ) + { + (m_pObj->*m_Func)( (P *)pvParam ); + } + virtual void Run( void *pvParam, bool, SteamAPICall_t ) + { + (m_pObj->*m_Func)( (P *)pvParam ); + } + int GetCallbackSizeBytes() + { + return sizeof( P ); + } + + T *m_pObj; + func_t m_Func; +}; + +// Allows you to defer registration of the callback +template< class T, class P, bool bGameServer > +class CCallbackManual : public CCallback< T, P, bGameServer > +{ +public: + CCallbackManual() : CCallback< T, P, bGameServer >( NULL, NULL ) {} +}; + +// utility macro for declaring the function and callback object together +#define STEAM_CALLBACK( thisclass, func, param, var ) CCallback< thisclass, param, false > var; void func( param *pParam ) + +// same as above, but lets you defer the callback binding by calling Register later +#define STEAM_CALLBACK_MANUAL( thisclass, func, param, var ) CCallbackManual< thisclass, param, false > var; void func( param *pParam ) + + +#ifdef _WIN32 +// disable this warning; this pattern need for steam callback registration +#pragma warning( disable: 4355 ) // 'this' : used in base member initializer list +#endif + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// steamclient.dll private wrapper functions +// +// The following functions are part of abstracting API access to the steamclient.dll, but should only be used in very specific cases +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +// pumps out all the steam messages, calling the register callback +S_API void Steam_RunCallbacks( HSteamPipe hSteamPipe, bool bGameServerCallbacks ); + +// register the callback funcs to use to interact with the steam dll +S_API void Steam_RegisterInterfaceFuncs( void *hModule ); + +// returns the HSteamUser of the last user to dispatch a callback +S_API HSteamUser Steam_GetHSteamUserCurrent(); + +// returns the filename path of the current running Steam process, used if you need to load an explicit steam dll by name +S_API const char *SteamAPI_GetSteamInstallPath(); + +// returns the pipe we are communicating to Steam with +S_API HSteamPipe SteamAPI_GetHSteamPipe(); + +// sets whether or not Steam_RunCallbacks() should do a try {} catch (...) {} around calls to issuing callbacks +S_API void SteamAPI_SetTryCatchCallbacks( bool bTryCatchCallbacks ); + +// backwards compat export, passes through to SteamAPI_ variants +S_API HSteamPipe GetHSteamPipe(); +S_API HSteamUser GetHSteamUser(); + +#ifdef VERSION_SAFE_STEAM_API_INTERFACES +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// VERSION_SAFE_STEAM_API_INTERFACES uses CSteamAPIContext to provide interfaces to each module in a way that +// lets them each specify the interface versions they are compiled with. +// +// It's important that these stay inlined in the header so the calling module specifies the interface versions +// for whatever Steam API version it has. +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +S_API HSteamUser SteamAPI_GetHSteamUser(); + +class CSteamAPIContext +{ +public: + CSteamAPIContext(); + void Clear(); + + bool Init(); + + ISteamUser* SteamUser() { return m_pSteamUser; } + ISteamFriends* SteamFriends() { return m_pSteamFriends; } + ISteamUtils* SteamUtils() { return m_pSteamUtils; } + ISteamMatchmaking* SteamMatchmaking() { return m_pSteamMatchmaking; } + ISteamUserStats* SteamUserStats() { return m_pSteamUserStats; } + ISteamApps* SteamApps() { return m_pSteamApps; } + ISteamMatchmakingServers* SteamMatchmakingServers() { return m_pSteamMatchmakingServers; } + ISteamNetworking* SteamNetworking() { return m_pSteamNetworking; } + ISteamRemoteStorage* SteamRemoteStorage() { return m_pSteamRemoteStorage; } + +private: + ISteamUser *m_pSteamUser; + ISteamFriends *m_pSteamFriends; + ISteamUtils *m_pSteamUtils; + ISteamMatchmaking *m_pSteamMatchmaking; + ISteamUserStats *m_pSteamUserStats; + ISteamApps *m_pSteamApps; + ISteamMatchmakingServers *m_pSteamMatchmakingServers; + ISteamNetworking *m_pSteamNetworking; + ISteamRemoteStorage *m_pSteamRemoteStorage; +}; + +inline CSteamAPIContext::CSteamAPIContext() +{ + Clear(); +} + +inline void CSteamAPIContext::Clear() +{ + m_pSteamUser = NULL; + m_pSteamFriends = NULL; + m_pSteamUtils = NULL; + m_pSteamMatchmaking = NULL; + m_pSteamUserStats = NULL; + m_pSteamApps = NULL; + m_pSteamMatchmakingServers = NULL; + m_pSteamNetworking = NULL; + m_pSteamRemoteStorage = NULL; +} + +// This function must be inlined so the module using steam_api.dll gets the version names they want. +inline bool CSteamAPIContext::Init() +{ + if ( !SteamClient() ) + return false; + + HSteamUser hSteamUser = SteamAPI_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamAPI_GetHSteamPipe(); + + m_pSteamUser = SteamClient()->GetISteamUser( hSteamUser, hSteamPipe, STEAMUSER_INTERFACE_VERSION ); + if ( !m_pSteamUser ) + return false; + + m_pSteamFriends = SteamClient()->GetISteamFriends( hSteamUser, hSteamPipe, STEAMFRIENDS_INTERFACE_VERSION ); + if ( !m_pSteamFriends ) + return false; + + m_pSteamUtils = SteamClient()->GetISteamUtils( hSteamPipe, STEAMUTILS_INTERFACE_VERSION ); + if ( !m_pSteamUtils ) + return false; + + m_pSteamMatchmaking = SteamClient()->GetISteamMatchmaking( hSteamUser, hSteamPipe, STEAMMATCHMAKING_INTERFACE_VERSION ); + if ( !m_pSteamMatchmaking ) + return false; + + m_pSteamMatchmakingServers = SteamClient()->GetISteamMatchmakingServers( hSteamUser, hSteamPipe, STEAMMATCHMAKINGSERVERS_INTERFACE_VERSION ); + if ( !m_pSteamMatchmakingServers ) + return false; + + m_pSteamUserStats = SteamClient()->GetISteamUserStats( hSteamUser, hSteamPipe, STEAMUSERSTATS_INTERFACE_VERSION ); + if ( !m_pSteamUserStats ) + return false; + + m_pSteamApps = SteamClient()->GetISteamApps( hSteamUser, hSteamPipe, STEAMAPPS_INTERFACE_VERSION ); + if ( !m_pSteamApps ) + return false; + + m_pSteamNetworking = SteamClient()->GetISteamNetworking( hSteamUser, hSteamPipe, STEAMNETWORKING_INTERFACE_VERSION ); + if ( !m_pSteamNetworking ) + return false; + + m_pSteamRemoteStorage = SteamClient()->GetISteamRemoteStorage( hSteamUser, hSteamPipe, STEAMREMOTESTORAGE_INTERFACE_VERSION ); + if ( !m_pSteamRemoteStorage ) + return false; + + return true; +} + +#endif // VERSION_SAFE_STEAM_API_INTERFACES + +#endif // STEAM_API_H diff --git a/public/steam/steam_gameserver.h b/public/steam/steam_gameserver.h new file mode 100644 index 0000000..2a74a04 --- /dev/null +++ b/public/steam/steam_gameserver.h @@ -0,0 +1,143 @@ +//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef STEAM_GAMESERVER_H +#define STEAM_GAMESERVER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "steam_api.h" +#include "isteamgameserver.h" +#include "isteammasterserverupdater.h" +#include "isteamgameserverstats.h" + +enum EServerMode +{ + eServerModeInvalid = 0, // DO NOT USE + eServerModeNoAuthentication = 1, // Don't authenticate user logins and don't list on the server list + eServerModeAuthentication = 2, // Authenticate users, list on the server list, don't run VAC on clients that connect + eServerModeAuthenticationAndSecure = 3, // Authenticate users, list on the server list and VAC protect clients +}; + +// Note: if you pass MASTERSERVERUPDATERPORT_USEGAMESOCKETSHARE for usQueryPort, then it will use "GameSocketShare" mode, +// which means that the game is responsible for sending and receiving UDP packets for the master +// server updater. See references to GameSocketShare in isteammasterserverupdater.h. +// +// Pass 0 for usGamePort or usSpectatorPort if you're not using that. Then, the master server updater will report +// what's running based on that. +#ifdef VERSION_SAFE_STEAM_API_INTERFACES +S_API bool SteamGameServer_InitSafe( uint32 unIP, uint16 usPort, uint16 usGamePort, uint16 usSpectatorPort, uint16 usQueryPort, EServerMode eServerMode, const char *pchGameDir, const char *pchVersionString ); +#else +S_API bool SteamGameServer_Init( uint32 unIP, uint16 usPort, uint16 usGamePort, uint16 usSpectatorPort, uint16 usQueryPort, EServerMode eServerMode, const char *pchGameDir, const char *pchVersionString ); + +S_API ISteamGameServer *SteamGameServer(); +S_API ISteamUtils *SteamGameServerUtils(); +S_API ISteamMasterServerUpdater *SteamMasterServerUpdater(); +S_API ISteamNetworking *SteamGameServerNetworking(); +S_API ISteamGameServerStats *SteamGameServerStats(); +#endif + +S_API void SteamGameServer_Shutdown(); +S_API void SteamGameServer_RunCallbacks(); + +S_API bool SteamGameServer_BSecure(); +S_API uint64 SteamGameServer_GetSteamID(); + +#define STEAM_GAMESERVER_CALLBACK( thisclass, func, param, var ) CCallback< thisclass, param, true > var; void func( param *pParam ) + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// steamclient.dll private wrapper functions +// +// The following functions are part of abstracting API access to the steamclient.dll, but should only be used in very specific cases +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +S_API HSteamPipe SteamGameServer_GetHSteamPipe(); + +#ifdef VERSION_SAFE_STEAM_API_INTERFACES +//----------------------------------------------------------------------------------------------------------------------------------------------------------// +// VERSION_SAFE_STEAM_API_INTERFACES uses CSteamAPIContext to provide interfaces to each module in a way that +// lets them each specify the interface versions they are compiled with. +// +// It's important that these stay inlined in the header so the calling module specifies the interface versions +// for whatever Steam API version it has. +//----------------------------------------------------------------------------------------------------------------------------------------------------------// + +S_API HSteamUser SteamGameServer_GetHSteamUser(); + +class CSteamGameServerAPIContext +{ +public: + CSteamGameServerAPIContext(); + void Clear(); + + bool Init(); + + ISteamGameServer *SteamGameServer() { return m_pSteamGameServer; } + ISteamUtils *SteamGameServerUtils() { return m_pSteamGameServerUtils; } + ISteamMasterServerUpdater *SteamMasterServerUpdater() { return m_pSteamMasterServerUpdater; } + ISteamNetworking *SteamGameServerNetworking() { return m_pSteamGameServerNetworking; } + ISteamGameServerStats *SteamGameServerStats() { return m_pSteamGameServerStats; } + +private: + ISteamGameServer *m_pSteamGameServer; + ISteamUtils *m_pSteamGameServerUtils; + ISteamMasterServerUpdater *m_pSteamMasterServerUpdater; + ISteamNetworking *m_pSteamGameServerNetworking; + ISteamGameServerStats *m_pSteamGameServerStats; +}; + +inline CSteamGameServerAPIContext::CSteamGameServerAPIContext() +{ + Clear(); +} + +inline void CSteamGameServerAPIContext::Clear() +{ + m_pSteamGameServer = NULL; + m_pSteamGameServerUtils = NULL; + m_pSteamMasterServerUpdater = NULL; + m_pSteamGameServerNetworking = NULL; + m_pSteamGameServerStats = NULL; +} + +S_API ISteamClient *g_pSteamClientGameServer; +// This function must be inlined so the module using steam_api.dll gets the version names they want. +inline bool CSteamGameServerAPIContext::Init() +{ + if ( !g_pSteamClientGameServer ) + return false; + + HSteamUser hSteamUser = SteamGameServer_GetHSteamUser(); + HSteamPipe hSteamPipe = SteamGameServer_GetHSteamPipe(); + + m_pSteamGameServer = g_pSteamClientGameServer->GetISteamGameServer( hSteamUser, hSteamPipe, STEAMGAMESERVER_INTERFACE_VERSION ); + if ( !m_pSteamGameServer ) + return false; + + m_pSteamGameServerUtils = g_pSteamClientGameServer->GetISteamUtils( hSteamPipe, STEAMUTILS_INTERFACE_VERSION ); + if ( !m_pSteamGameServerUtils ) + return false; + + m_pSteamMasterServerUpdater = g_pSteamClientGameServer->GetISteamMasterServerUpdater( hSteamUser, hSteamPipe, STEAMMASTERSERVERUPDATER_INTERFACE_VERSION ); + if ( !m_pSteamMasterServerUpdater ) + return false; + + m_pSteamGameServerNetworking = g_pSteamClientGameServer->GetISteamNetworking( hSteamUser, hSteamPipe, STEAMNETWORKING_INTERFACE_VERSION ); + if ( !m_pSteamGameServerNetworking ) + return false; + + m_pSteamGameServerStats = g_pSteamClientGameServer->GetISteamGameServerStats( hSteamUser, hSteamPipe, STEAMGAMESERVERSTATS_INTERFACE_VERSION ); + if ( !m_pSteamGameServerStats ) + return false; + + return true; +} + +#endif // VERSION_SAFE_STEAM_API_INTERFACES + + +#endif // STEAM_GAMESERVER_H diff --git a/public/steam/steamclientpublic.h b/public/steam/steamclientpublic.h new file mode 100644 index 0000000..f5e5b3c --- /dev/null +++ b/public/steam/steamclientpublic.h @@ -0,0 +1,917 @@ +//========= Copyright � 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +//============================================================================= + +#ifndef STEAMCLIENTPUBLIC_H +#define STEAMCLIENTPUBLIC_H +#ifdef _WIN32 +#pragma once +#endif +//lint -save -e1931 -e1927 -e1924 -e613 -e726 + +// This header file defines the interface between the calling application and the code that +// knows how to communicate with the connection manager (CM) from the Steam service + +// This header file is intended to be portable; ideally this 1 header file plus a lib or dll +// is all you need to integrate the client library into some other tree. So please avoid +// including or requiring other header files if possible. This header should only describe the +// interface layer, no need to include anything about the implementation. + +#include "steamtypes.h" + + +// General result codes +enum EResult +{ + k_EResultOK = 1, // success + k_EResultFail = 2, // generic failure + k_EResultNoConnection = 3, // no/failed network connection +// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed + k_EResultInvalidPassword = 5, // password/ticket is invalid + k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere + k_EResultInvalidProtocolVer = 7, // protocol version is incorrect + k_EResultInvalidParam = 8, // a parameter is incorrect + k_EResultFileNotFound = 9, // file was not found + k_EResultBusy = 10, // called method busy - action not taken + k_EResultInvalidState = 11, // called object was in an invalid state + k_EResultInvalidName = 12, // name is invalid + k_EResultInvalidEmail = 13, // email is invalid + k_EResultDuplicateName = 14, // name is not unique + k_EResultAccessDenied = 15, // access is denied + k_EResultTimeout = 16, // operation timed out + k_EResultBanned = 17, // VAC2 banned + k_EResultAccountNotFound = 18, // account not found + k_EResultInvalidSteamID = 19, // steamID is invalid + k_EResultServiceUnavailable = 20, // The requested service is currently unavailable + k_EResultNotLoggedOn = 21, // The user is not logged on + k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party) + k_EResultEncryptionFailure = 23, // Encryption or Decryption failed + k_EResultInsufficientPrivilege = 24, // Insufficient privilege + k_EResultLimitExceeded = 25, // Too much of a good thing + k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes) + k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired + k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again + k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time + k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user + k_EResultIPNotFound = 31, // IP address not found + k_EResultPersistFailed = 32, // failed to write change to the data store + k_EResultLockingFailed = 33, // failed to acquire access lock for this operation + k_EResultLogonSessionReplaced = 34, + k_EResultConnectFailed = 35, + k_EResultHandshakeFailed = 36, + k_EResultIOFailure = 37, + k_EResultRemoteDisconnect = 38, + k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested + k_EResultBlocked = 40, // a user didn't allow it + k_EResultIgnored = 41, // target is ignoring sender + k_EResultNoMatch = 42, // nothing matching the request found + k_EResultAccountDisabled = 43, + k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now + k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available + k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin + k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol. + k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another. + k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed. + k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait + k_EResultSuspended = 51, // Long running operation (content download) suspended/paused + k_EResultCancelled = 52, // Operation canceled (typically by user: content download) + k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable + k_EResultDiskFull = 54, // Operation canceled - not enough disk space. + k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed +}; + +// Error codes for use with the voice functions +enum EVoiceResult +{ + k_EVoiceResultOK = 0, + k_EVoiceResultNotInitialized = 1, + k_EVoiceResultNotRecording = 2, + k_EVoiceResultNoData = 3, + k_EVoiceResultBufferTooSmall = 4, + k_EVoiceResultDataCorrupted = 5, +}; + +// Result codes to GSHandleClientDeny/Kick +typedef enum +{ + k_EDenyInvalid = 0, + k_EDenyInvalidVersion = 1, + k_EDenyGeneric = 2, + k_EDenyNotLoggedOn = 3, + k_EDenyNoLicense = 4, + k_EDenyCheater = 5, + k_EDenyLoggedInElseWhere = 6, + k_EDenyUnknownText = 7, + k_EDenyIncompatibleAnticheat = 8, + k_EDenyMemoryCorruption = 9, + k_EDenyIncompatibleSoftware = 10, + k_EDenySteamConnectionLost = 11, + k_EDenySteamConnectionError = 12, + k_EDenySteamResponseTimedOut = 13, + k_EDenySteamValidationStalled = 14, + k_EDenySteamOwnerLeftGuestUser = 15, +} EDenyReason; + +// return type of GetAuthSessionTicket +typedef uint32 HAuthTicket; +const HAuthTicket k_HAuthTicketInvalid = 0; + +// results from BeginAuthSession +typedef enum +{ + k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID. + k_EBeginAuthSessionResultInvalidTicket = 1, // Ticket is not valid. + k_EBeginAuthSessionResultDuplicateRequest = 2, // A ticket has already been submitted for this steamID + k_EBeginAuthSessionResultInvalidVersion = 3, // Ticket is from an incompatible interface version + k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game + k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired +} EBeginAuthSessionResult; + +// Callback values for callback ValidateAuthTicketResponse_t which is a response to BeginAuthSession +typedef enum +{ + k_EAuthSessionResponseOK = 0, // Steam has verified the user is online, the ticket is valid and ticket has not been reused. + k_EAuthSessionResponseUserNotConnectedToSteam = 1, // The user in question is not connected to steam + k_EAuthSessionResponseNoLicenseOrExpired = 2, // The license has expired. + k_EAuthSessionResponseVACBanned = 3, // The user is VAC banned for this game. + k_EAuthSessionResponseLoggedInElseWhere = 4, // The user account has logged in elsewhere and the session containing the game instance has been disconnected. + k_EAuthSessionResponseVACCheckTimedOut = 5, // VAC has been unable to perform anti-cheat checks on this user + k_EAuthSessionResponseAuthTicketCanceled = 6, // The ticket has been canceled by the issuer + k_EAuthSessionResponseAuthTicketInvalidAlreadyUsed = 7, // This ticket has already been used, it is not valid. + k_EAuthSessionResponseAuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam. +} EAuthSessionResponse; + +// results from UserHasLicenseForApp +typedef enum +{ + k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app + k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app + k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated +} EUserHasLicenseForAppResult; + + +// Steam universes. Each universe is a self-contained Steam instance. +enum EUniverse +{ + k_EUniverseInvalid = 0, + k_EUniversePublic = 1, + k_EUniverseBeta = 2, + k_EUniverseInternal = 3, + k_EUniverseDev = 4, + k_EUniverseRC = 5, + k_EUniverseMax +}; + +// Steam account types +enum EAccountType +{ + k_EAccountTypeInvalid = 0, + k_EAccountTypeIndividual = 1, // single user account + k_EAccountTypeMultiseat = 2, // multiseat (e.g. cybercafe) account + k_EAccountTypeGameServer = 3, // game server account + k_EAccountTypeAnonGameServer = 4, // anonymous game server account + k_EAccountTypePending = 5, // pending + k_EAccountTypeContentServer = 6, // content server + k_EAccountTypeClan = 7, + k_EAccountTypeChat = 8, + // k_EAccountTypeP2PSuperSeeder = 9, // unused + k_EAccountTypeAnonUser = 10, + + // Max of 16 items in this field + k_EAccountTypeMax +}; + + +//----------------------------------------------------------------------------- +// types of user game stats fields +// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE +//----------------------------------------------------------------------------- +enum ESteamUserStatType +{ + k_ESteamUserStatTypeINVALID = 0, + k_ESteamUserStatTypeINT = 1, + k_ESteamUserStatTypeFLOAT = 2, + // Read as FLOAT, set with count / session length + k_ESteamUserStatTypeAVGRATE = 3, + k_ESteamUserStatTypeACHIEVEMENTS = 4, + k_ESteamUserStatTypeGROUPACHIEVEMENTS = 5, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Chat Entry Types (previously was only friend-to-friend message types) +//----------------------------------------------------------------------------- +enum EChatEntryType +{ + k_EChatEntryTypeInvalid = 0, + k_EChatEntryTypeChatMsg = 1, // Normal text message from another user + k_EChatEntryTypeTyping = 2, // Another user is typing (not used in multi-user chat) + k_EChatEntryTypeInviteGame = 3, // Invite from other user into that users current game + k_EChatEntryTypeEmote = 4, // text emote message + k_EChatEntryTypeLobbyGameStart = 5, // lobby game is starting + k_EChatEntryTypeLeftConversation = 6, // user has left the conversation ( closed chat window ) + // Above are previous FriendMsgType entries, now merged into more generic chat entry types +}; + + +//----------------------------------------------------------------------------- +// Purpose: Chat Room Enter Responses +//----------------------------------------------------------------------------- +enum EChatRoomEnterResponse +{ + k_EChatRoomEnterResponseSuccess = 1, // Success + k_EChatRoomEnterResponseDoesntExist = 2, // Chat doesn't exist (probably closed) + k_EChatRoomEnterResponseNotAllowed = 3, // General Denied - You don't have the permissions needed to join the chat + k_EChatRoomEnterResponseFull = 4, // Chat room has reached its maximum size + k_EChatRoomEnterResponseError = 5, // Unexpected Error + k_EChatRoomEnterResponseBanned = 6, // You are banned from this chat room and may not join + k_EChatRoomEnterResponseLimited = 7, // Joining this chat is not allowed because you are a limited user (no value on account) +}; + + +//----------------------------------------------------------------------------- +// Purpose: Status of a given depot version, these are stored in the DB, don't renumber +//----------------------------------------------------------------------------- +enum EStatusDepotVersion +{ + k_EStatusDepotVersionInvalid = 0, + k_EStatusDepotVersionCompleteDisabled = 1, + k_EStatusDepotVersionCompleteEnabledBeta = 2, + k_EStatusDepotVersionCompleteEnabledPublic = 3, +}; + + +typedef void (*PFNLegacyKeyRegistration)( const char *pchCDKey, const char *pchInstallPath ); +typedef bool (*PFNLegacyKeyInstalled)(); + +const int k_unSteamAccountIDMask = 0xFFFFFFFF; +const int k_unSteamAccountInstanceMask = 0x000FFFFF; + +// Special flags for Chat accounts - they go in the top 8 bits +// of the steam ID's "instance", leaving 12 for the actual instances +enum EChatSteamIDInstanceFlags +{ + k_EChatAccountInstanceMask = 0x00000FFF, // top 8 bits are flags + + k_EChatInstanceFlagClan = ( k_unSteamAccountInstanceMask + 1 ) >> 1, // top bit + k_EChatInstanceFlagLobby = ( k_unSteamAccountInstanceMask + 1 ) >> 2, // next one down, etc + k_EChatInstanceFlagMMSLobby = ( k_unSteamAccountInstanceMask + 1 ) >> 3, // next one down, etc + + // Max of 8 flags +}; + + +//----------------------------------------------------------------------------- +// Purpose: Marketing message flags that change how a client should handle them +//----------------------------------------------------------------------------- +enum EMarketingMessageFlags +{ + k_EMarketingMessageFlagsNone = 0, + k_EMarketingMessageFlagsHighPriority = 1 << 0, + k_EMarketingMessageFlagsPlatformWindows = 1 << 1, + k_EMarketingMessageFlagsPlatformMac = 1 << 2, + + //aggregate flags + k_EMarketingMessageFlagsPlatformRestrictions = + k_EMarketingMessageFlagsPlatformWindows | k_EMarketingMessageFlagsPlatformMac, +}; + + + +//----------------------------------------------------------------------------- +// Purpose: Possible positions to tell the overlay to show notifications in +//----------------------------------------------------------------------------- +enum ENotificationPosition +{ + k_EPositionTopLeft = 0, + k_EPositionTopRight = 1, + k_EPositionBottomLeft = 2, + k_EPositionBottomRight = 3, +}; + + +#pragma pack( push, 1 ) + +// Steam ID structure (64 bits total) +class CSteamID +{ +public: + + //----------------------------------------------------------------------------- + // Purpose: Constructor + //----------------------------------------------------------------------------- + CSteamID() + { + m_steamid.m_comp.m_unAccountID = 0; + m_steamid.m_comp.m_EAccountType = k_EAccountTypeInvalid; + m_steamid.m_comp.m_EUniverse = k_EUniverseInvalid; + m_steamid.m_comp.m_unAccountInstance = 0; + } + + + //----------------------------------------------------------------------------- + // Purpose: Constructor + // Input : unAccountID - 32-bit account ID + // eUniverse - Universe this account belongs to + // eAccountType - Type of account + //----------------------------------------------------------------------------- + CSteamID( uint32 unAccountID, EUniverse eUniverse, EAccountType eAccountType ) + { + Set( unAccountID, eUniverse, eAccountType ); + } + + + //----------------------------------------------------------------------------- + // Purpose: Constructor + // Input : unAccountID - 32-bit account ID + // unAccountInstance - instance + // eUniverse - Universe this account belongs to + // eAccountType - Type of account + //----------------------------------------------------------------------------- + CSteamID( uint32 unAccountID, unsigned int unAccountInstance, EUniverse eUniverse, EAccountType eAccountType ) + { +#if defined(_SERVER) && defined(Assert) + Assert( ! ( ( k_EAccountTypeIndividual == eAccountType ) && ( 1 != unAccountInstance ) ) ); // enforce that for individual accounts, instance is always 1 +#endif // _SERVER + InstancedSet( unAccountID, unAccountInstance, eUniverse, eAccountType ); + } + + + //----------------------------------------------------------------------------- + // Purpose: Constructor + // Input : ulSteamID - 64-bit representation of a Steam ID + // Note: Will not accept a uint32 or int32 as input, as that is a probable mistake. + // See the stubbed out overloads in the private: section for more info. + //----------------------------------------------------------------------------- + CSteamID( uint64 ulSteamID ) + { + SetFromUint64( ulSteamID ); + } + + + //----------------------------------------------------------------------------- + // Purpose: Sets parameters for steam ID + // Input : unAccountID - 32-bit account ID + // eUniverse - Universe this account belongs to + // eAccountType - Type of account + //----------------------------------------------------------------------------- + void Set( uint32 unAccountID, EUniverse eUniverse, EAccountType eAccountType ) + { + m_steamid.m_comp.m_unAccountID = unAccountID; + m_steamid.m_comp.m_EUniverse = eUniverse; + m_steamid.m_comp.m_EAccountType = eAccountType; + + if ( eAccountType == k_EAccountTypeClan ) + { + m_steamid.m_comp.m_unAccountInstance = 0; + } + else + { + m_steamid.m_comp.m_unAccountInstance = 1; + } + } + + + //----------------------------------------------------------------------------- + // Purpose: Sets parameters for steam ID + // Input : unAccountID - 32-bit account ID + // eUniverse - Universe this account belongs to + // eAccountType - Type of account + //----------------------------------------------------------------------------- + void InstancedSet( uint32 unAccountID, uint32 unInstance, EUniverse eUniverse, EAccountType eAccountType ) + { + m_steamid.m_comp.m_unAccountID = unAccountID; + m_steamid.m_comp.m_EUniverse = eUniverse; + m_steamid.m_comp.m_EAccountType = eAccountType; + m_steamid.m_comp.m_unAccountInstance = unInstance; + } + + + //----------------------------------------------------------------------------- + // Purpose: Initializes a steam ID from its 52 bit parts and universe/type + // Input : ulIdentifier - 52 bits of goodness + //----------------------------------------------------------------------------- + void FullSet( uint64 ulIdentifier, EUniverse eUniverse, EAccountType eAccountType ) + { + m_steamid.m_comp.m_unAccountID = ( ulIdentifier & 0xFFFFFFFF ); // account ID is low 32 bits + m_steamid.m_comp.m_unAccountInstance = ( ( ulIdentifier >> 32 ) & 0xFFFFF ); // account instance is next 20 bits + m_steamid.m_comp.m_EUniverse = eUniverse; + m_steamid.m_comp.m_EAccountType = eAccountType; + } + + + //----------------------------------------------------------------------------- + // Purpose: Initializes a steam ID from its 64-bit representation + // Input : ulSteamID - 64-bit representation of a Steam ID + //----------------------------------------------------------------------------- + void SetFromUint64( uint64 ulSteamID ) + { + m_steamid.m_unAll64Bits = ulSteamID; + } + + +#if defined( INCLUDED_STEAM_COMMON_STEAMCOMMON_H ) + //----------------------------------------------------------------------------- + // Purpose: Initializes a steam ID from a Steam2 ID structure + // Input: pTSteamGlobalUserID - Steam2 ID to convert + // eUniverse - universe this ID belongs to + //----------------------------------------------------------------------------- + void SetFromSteam2( TSteamGlobalUserID *pTSteamGlobalUserID, EUniverse eUniverse ) + { + m_steamid.m_comp.m_unAccountID = pTSteamGlobalUserID->m_SteamLocalUserID.Split.Low32bits * 2 + + pTSteamGlobalUserID->m_SteamLocalUserID.Split.High32bits; + m_steamid.m_comp.m_EUniverse = eUniverse; // set the universe + m_steamid.m_comp.m_EAccountType = k_EAccountTypeIndividual; // Steam 2 accounts always map to account type of individual + m_steamid.m_comp.m_unAccountInstance = 1; // individual accounts always have an account instance ID of 1 + } + + //----------------------------------------------------------------------------- + // Purpose: Fills out a Steam2 ID structure + // Input: pTSteamGlobalUserID - Steam2 ID to write to + //----------------------------------------------------------------------------- + void ConvertToSteam2( TSteamGlobalUserID *pTSteamGlobalUserID ) const + { + // only individual accounts have any meaning in Steam 2, only they can be mapped + // Assert( m_steamid.m_comp.m_EAccountType == k_EAccountTypeIndividual ); + + pTSteamGlobalUserID->m_SteamInstanceID = 0; + pTSteamGlobalUserID->m_SteamLocalUserID.Split.High32bits = m_steamid.m_comp.m_unAccountID % 2; + pTSteamGlobalUserID->m_SteamLocalUserID.Split.Low32bits = m_steamid.m_comp.m_unAccountID / 2; + } +#endif // defined( INCLUDED_STEAM_COMMON_STEAMCOMMON_H ) + + //----------------------------------------------------------------------------- + // Purpose: Converts steam ID to its 64-bit representation + // Output : 64-bit representation of a Steam ID + //----------------------------------------------------------------------------- + uint64 ConvertToUint64() const + { + return m_steamid.m_unAll64Bits; + } + + + //----------------------------------------------------------------------------- + // Purpose: Converts the static parts of a steam ID to a 64-bit representation. + // For multiseat accounts, all instances of that account will have the + // same static account key, so they can be grouped together by the static + // account key. + // Output : 64-bit static account key + //----------------------------------------------------------------------------- + uint64 GetStaticAccountKey() const + { + // note we do NOT include the account instance (which is a dynamic property) in the static account key + return (uint64) ( ( ( (uint64) m_steamid.m_comp.m_EUniverse ) << 56 ) + ((uint64) m_steamid.m_comp.m_EAccountType << 52 ) + m_steamid.m_comp.m_unAccountID ); + } + + + //----------------------------------------------------------------------------- + // Purpose: create an anonymous game server login to be filled in by the AM + //----------------------------------------------------------------------------- + void CreateBlankAnonLogon( EUniverse eUniverse ) + { + m_steamid.m_comp.m_unAccountID = 0; + m_steamid.m_comp.m_EAccountType = k_EAccountTypeAnonGameServer; + m_steamid.m_comp.m_EUniverse = eUniverse; + m_steamid.m_comp.m_unAccountInstance = 0; + } + + + //----------------------------------------------------------------------------- + // Purpose: create an anonymous game server login to be filled in by the AM + //----------------------------------------------------------------------------- + void CreateBlankAnonUserLogon( EUniverse eUniverse ) + { + m_steamid.m_comp.m_unAccountID = 0; + m_steamid.m_comp.m_EAccountType = k_EAccountTypeAnonUser; + m_steamid.m_comp.m_EUniverse = eUniverse; + m_steamid.m_comp.m_unAccountInstance = 0; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this an anonymous game server login that will be filled in? + //----------------------------------------------------------------------------- + bool BBlankAnonAccount() const + { + return m_steamid.m_comp.m_unAccountID == 0 && BAnonAccount() && m_steamid.m_comp.m_unAccountInstance == 0; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this a game server account id? + //----------------------------------------------------------------------------- + bool BGameServerAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeGameServer || m_steamid.m_comp.m_EAccountType == k_EAccountTypeAnonGameServer; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this a content server account id? + //----------------------------------------------------------------------------- + bool BContentServerAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeContentServer; + } + + + //----------------------------------------------------------------------------- + // Purpose: Is this a clan account id? + //----------------------------------------------------------------------------- + bool BClanAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeClan; + } + + + //----------------------------------------------------------------------------- + // Purpose: Is this a chat account id? + //----------------------------------------------------------------------------- + bool BChatAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeChat; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this a chat account id? + //----------------------------------------------------------------------------- + bool IsLobby() const + { + return ( m_steamid.m_comp.m_EAccountType == k_EAccountTypeChat ) + && ( m_steamid.m_comp.m_unAccountInstance & k_EChatInstanceFlagLobby ); + } + + + //----------------------------------------------------------------------------- + // Purpose: Is this an individual user account id? + //----------------------------------------------------------------------------- + bool BIndividualAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeIndividual; + } + + + //----------------------------------------------------------------------------- + // Purpose: Is this an anonymous account? + //----------------------------------------------------------------------------- + bool BAnonAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeAnonUser || m_steamid.m_comp.m_EAccountType == k_EAccountTypeAnonGameServer; + } + + //----------------------------------------------------------------------------- + // Purpose: Is this an anonymous user account? ( used to create an account or reset a password ) + //----------------------------------------------------------------------------- + bool BAnonUserAccount() const + { + return m_steamid.m_comp.m_EAccountType == k_EAccountTypeAnonUser; + } + + + // simple accessors + void SetAccountID( uint32 unAccountID ) { m_steamid.m_comp.m_unAccountID = unAccountID; } + uint32 GetAccountID() const { return m_steamid.m_comp.m_unAccountID; } + uint32 GetUnAccountInstance() const { return m_steamid.m_comp.m_unAccountInstance; } + EAccountType GetEAccountType() const { return ( EAccountType ) m_steamid.m_comp.m_EAccountType; } + EUniverse GetEUniverse() const { return m_steamid.m_comp.m_EUniverse; } + void SetEUniverse( EUniverse eUniverse ) { m_steamid.m_comp.m_EUniverse = eUniverse; } + bool IsValid() const; + + // this set of functions is hidden, will be moved out of class + explicit CSteamID( const char *pchSteamID, EUniverse eDefaultUniverse = k_EUniverseInvalid ); + const char * Render() const; // renders this steam ID to string + static const char * Render( uint64 ulSteamID ); // static method to render a uint64 representation of a steam ID to a string + + void SetFromString( const char *pchSteamID, EUniverse eDefaultUniverse ); + bool SetFromSteam2String( const char *pchSteam2ID, EUniverse eUniverse ); + + inline bool operator==( const CSteamID &val ) const { return m_steamid.m_unAll64Bits == val.m_steamid.m_unAll64Bits; } + inline bool operator!=( const CSteamID &val ) const { return !operator==( val ); } + inline bool operator<( const CSteamID &val ) const { return m_steamid.m_unAll64Bits < val.m_steamid.m_unAll64Bits; } + inline bool operator>( const CSteamID &val ) const { return m_steamid.m_unAll64Bits > val.m_steamid.m_unAll64Bits; } + + // DEBUG function + bool BValidExternalSteamID() const; + +private: + // These are defined here to prevent accidental implicit conversion of a u32AccountID to a CSteamID. + // If you get a compiler error about an ambiguous constructor/function then it may be because you're + // passing a 32-bit int to a function that takes a CSteamID. You should explicitly create the SteamID + // using the correct Universe and account Type/Instance values. + CSteamID( uint32 ); + CSteamID( int32 ); + + // 64 bits total + union SteamID_t + { + struct SteamIDComponent_t + { + uint32 m_unAccountID : 32; // unique account identifier + unsigned int m_unAccountInstance : 20; // dynamic instance ID (used for multiseat type accounts only) + unsigned int m_EAccountType : 4; // type of account - can't show as EAccountType, due to signed / unsigned difference + EUniverse m_EUniverse : 8; // universe this account belongs to + } m_comp; + + uint64 m_unAll64Bits; + } m_steamid; +}; + +inline bool CSteamID::IsValid() const +{ + if ( m_steamid.m_comp.m_EAccountType <= k_EAccountTypeInvalid || m_steamid.m_comp.m_EAccountType >= k_EAccountTypeMax ) + return false; + + if ( m_steamid.m_comp.m_EUniverse <= k_EUniverseInvalid || m_steamid.m_comp.m_EUniverse >= k_EUniverseMax ) + return false; + + if ( m_steamid.m_comp.m_EAccountType == k_EAccountTypeIndividual ) + { + if ( m_steamid.m_comp.m_unAccountID == 0 || m_steamid.m_comp.m_unAccountInstance != 1 ) + return false; + } + + if ( m_steamid.m_comp.m_EAccountType == k_EAccountTypeClan ) + { + if ( m_steamid.m_comp.m_unAccountID == 0 || m_steamid.m_comp.m_unAccountInstance != 0 ) + return false; + } + return true; +} + + +// generic invalid CSteamID +const CSteamID k_steamIDNil; + +// This steamID comes from a user game connection to an out of date GS that hasnt implemented the protocol +// to provide its steamID +const CSteamID k_steamIDOutofDateGS( 0, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); +// This steamID comes from a user game connection to an sv_lan GS +const CSteamID k_steamIDLanModeGS( 0, 0, k_EUniversePublic, k_EAccountTypeInvalid ); +// This steamID can come from a user game connection to a GS that has just booted but hasnt yet even initialized +// its steam3 component and started logging on. +const CSteamID k_steamIDNotInitYetGS( 1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); +// This steamID can come from a user game connection to a GS that isn't using the steam authentication system but still +// wants to support the "Join Game" option in the friends list +const CSteamID k_steamIDNonSteamGS( 2, 0, k_EUniverseInvalid, k_EAccountTypeInvalid ); + + +#ifdef STEAM +// Returns the matching chat steamID, with the default instance of 0 +// If the steamID passed in is already of type k_EAccountTypeChat it will be returned with the same instance +CSteamID ChatIDFromSteamID( const CSteamID &steamID ); +// Returns the matching clan steamID, with the default instance of 0 +// If the steamID passed in is already of type k_EAccountTypeClan it will be returned with the same instance +CSteamID ClanIDFromSteamID( const CSteamID &steamID ); +// Asserts steamID type before conversion +CSteamID ChatIDFromClanID( const CSteamID &steamIDClan ); +// Asserts steamID type before conversion +CSteamID ClanIDFromChatID( const CSteamID &steamIDChat ); + +#endif // _STEAM + + +//----------------------------------------------------------------------------- +// Purpose: encapsulates an appID/modID pair +//----------------------------------------------------------------------------- +class CGameID +{ +public: + + CGameID() + { + m_gameID.m_nType = k_EGameIDTypeApp; + m_gameID.m_nAppID = k_uAppIdInvalid; + m_gameID.m_nModID = 0; + } + + explicit CGameID( uint64 ulGameID ) + { + m_ulGameID = ulGameID; + } + + explicit CGameID( int32 nAppID ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = nAppID; + } + + explicit CGameID( uint32 nAppID ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = nAppID; + } + + CGameID( uint32 nAppID, uint32 nModID ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = nAppID; + m_gameID.m_nModID = nModID; + m_gameID.m_nType = k_EGameIDTypeGameMod; + } + + // Hidden functions used only by Steam + explicit CGameID( const char *pchGameID ); + const char *Render() const; // render this Game ID to string + static const char *Render( uint64 ulGameID ); // static method to render a uint64 representation of a Game ID to a string + + // must include checksum_crc.h first to get this functionality +#if defined( CHECKSUM_CRC_H ) + CGameID( uint32 nAppID, const char *pchModPath ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = nAppID; + m_gameID.m_nType = k_EGameIDTypeGameMod; + + char rgchModDir[MAX_PATH]; + Q_FileBase( pchModPath, rgchModDir, sizeof( rgchModDir ) ); + CRC32_t crc32; + CRC32_Init( &crc32 ); + CRC32_ProcessBuffer( &crc32, rgchModDir, Q_strlen( rgchModDir ) ); + CRC32_Final( &crc32 ); + + // set the high-bit on the mod-id + // reduces crc32 to 31bits, but lets us use the modID as a guaranteed unique + // replacement for appID's + m_gameID.m_nModID = crc32 | (0x80000000); + } + + CGameID( const char *pchExePath, const char *pchAppName ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = k_uAppIdInvalid; + m_gameID.m_nType = k_EGameIDTypeShortcut; + + CRC32_t crc32; + CRC32_Init( &crc32 ); + CRC32_ProcessBuffer( &crc32, pchExePath, Q_strlen( pchExePath ) ); + CRC32_ProcessBuffer( &crc32, pchAppName, Q_strlen( pchAppName ) ); + CRC32_Final( &crc32 ); + + // set the high-bit on the mod-id + // reduces crc32 to 31bits, but lets us use the modID as a guaranteed unique + // replacement for appID's + m_gameID.m_nModID = crc32 | (0x80000000); + } + +#if defined( VSTFILEID_H ) + + CGameID( VstFileID vstFileID ) + { + m_ulGameID = 0; + m_gameID.m_nAppID = k_uAppIdInvalid; + m_gameID.m_nType = k_EGameIDTypeP2P; + + CRC32_t crc32; + CRC32_Init( &crc32 ); + const char *pchFileId = vstFileID.Render(); + CRC32_ProcessBuffer( &crc32, pchFileId, Q_strlen( pchFileId ) ); + CRC32_Final( &crc32 ); + + // set the high-bit on the mod-id + // reduces crc32 to 31bits, but lets us use the modID as a guaranteed unique + // replacement for appID's + m_gameID.m_nModID = crc32 | (0x80000000); + } + +#endif /* VSTFILEID_H */ + +#endif /* CHECKSUM_CRC_H */ + + + uint64 ToUint64() const + { + return m_ulGameID; + } + + uint64 *GetUint64Ptr() + { + return &m_ulGameID; + } + + bool IsMod() const + { + return ( m_gameID.m_nType == k_EGameIDTypeGameMod ); + } + + bool IsShortcut() const + { + return ( m_gameID.m_nType == k_EGameIDTypeShortcut ); + } + + bool IsP2PFile() const + { + return ( m_gameID.m_nType == k_EGameIDTypeP2P ); + } + + bool IsSteamApp() const + { + return ( m_gameID.m_nType == k_EGameIDTypeApp ); + } + + uint32 ModID() const + { + return m_gameID.m_nModID; + } + + uint32 AppID() const + { + return m_gameID.m_nAppID; + } + + bool operator == ( const CGameID &rhs ) const + { + return m_ulGameID == rhs.m_ulGameID; + } + + bool operator != ( const CGameID &rhs ) const + { + return !(*this == rhs); + } + + bool operator < ( const CGameID &rhs ) const + { + return ( m_ulGameID < rhs.m_ulGameID ); + } + + bool IsValid() const + { + // each type has it's own invalid fixed point: + switch( m_gameID.m_nType ) + { + case k_EGameIDTypeApp: + return m_gameID.m_nAppID != k_uAppIdInvalid; + break; + case k_EGameIDTypeGameMod: + return m_gameID.m_nAppID != k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000; + break; + case k_EGameIDTypeShortcut: + return (m_gameID.m_nModID & 0x80000000) != 0; + break; + case k_EGameIDTypeP2P: + return m_gameID.m_nAppID == k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000; + break; + default: +#if defined(Assert) + Assert(false); +#endif + return false; + } + + } + + void Reset() + { + m_ulGameID = 0; + } + + + +private: + + enum EGameIDType + { + k_EGameIDTypeApp = 0, + k_EGameIDTypeGameMod = 1, + k_EGameIDTypeShortcut = 2, + k_EGameIDTypeP2P = 3, + }; + + struct GameID_t + { + unsigned int m_nAppID : 24; + unsigned int m_nType : 8; + unsigned int m_nModID : 32; + }; + + union + { + uint64 m_ulGameID; + GameID_t m_gameID; + }; +}; + +#pragma pack( pop ) + +const int k_cchGameExtraInfoMax = 64; + + +//----------------------------------------------------------------------------- +// Constants used for query ports. +//----------------------------------------------------------------------------- + +#define QUERY_PORT_NOT_INITIALIZED 0xFFFF // We haven't asked the GS for this query port's actual value yet. +#define QUERY_PORT_ERROR 0xFFFE // We were unable to get the query port for this server. + + +//----------------------------------------------------------------------------- +// Purpose: Passed as argument to SteamAPI_UseBreakpadCrashHandler to enable optional callback +// just before minidump file is captured after a crash has occurred. (Allows app to append additional comment data to the dump, etc.) +//----------------------------------------------------------------------------- +typedef void (*PFNPreMinidumpCallback)(void *context); + +//----------------------------------------------------------------------------- +// Purpose: Used by ICrashHandler interfaces to reference particular installed crash handlers +//----------------------------------------------------------------------------- +typedef void *BREAKPAD_HANDLE; +#define BREAKPAD_INVALID_HANDLE (BREAKPAD_HANDLE)0 + +#endif // STEAMCLIENTPUBLIC_H diff --git a/public/steam/steamtypes.h b/public/steam/steamtypes.h new file mode 100644 index 0000000..49c6aea --- /dev/null +++ b/public/steam/steamtypes.h @@ -0,0 +1,123 @@ +//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +//============================================================================= + +#ifndef STEAMTYPES_H +#define STEAMTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +// Steam-specific types. Defined here so this header file can be included in other code bases. +#ifndef WCHARTYPES_H +typedef unsigned char uint8; +#endif + +#if defined( __GNUC__ ) && !defined(POSIX) + #if __GNUC__ < 4 + #error "Steamworks requires GCC 4.X (4.2 or 4.4 have been tested)" + #endif + #define POSIX 1 +#endif + +#if defined(__x86_64__) || defined(_WIN64) +#define X64BITS +#endif + +typedef unsigned char uint8; +typedef signed char int8; + +#if defined( _WIN32 ) + +typedef __int16 int16; +typedef unsigned __int16 uint16; +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; + +#ifdef X64BITS +typedef __int64 intp; // intp is an integer that can accomodate a pointer +typedef unsigned __int64 uintp; // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *) +#else +typedef __int32 intp; +typedef unsigned __int32 uintp; +#endif + +#else // _WIN32 + +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#ifdef X64BITS +typedef long long intp; +typedef unsigned long long uintp; +#else +typedef int intp; +typedef unsigned int uintp; +#endif + +#endif // else _WIN32 + +const int k_cubSaltSize = 8; +typedef uint8 Salt_t[ k_cubSaltSize ]; + +//----------------------------------------------------------------------------- +// GID (GlobalID) stuff +// This is a globally unique identifier. It's guaranteed to be unique across all +// racks and servers for as long as a given universe persists. +//----------------------------------------------------------------------------- +// NOTE: for GID parsing/rendering and other utils, see gid.h +typedef uint64 GID_t; + +const GID_t k_GIDNil = 0xffffffffffffffffull; + +// For convenience, we define a number of types that are just new names for GIDs +typedef GID_t JobID_t; // Each Job has a unique ID +typedef GID_t TxnID_t; // Each financial transaction has a unique ID + +const GID_t k_TxnIDNil = k_GIDNil; +const GID_t k_TxnIDUnknown = 0; + + +// this is baked into client messages and interfaces as an int, +// make sure we never break this. +typedef uint32 PackageId_t; +const PackageId_t k_uPackageIdFreeSub = 0x0; +const PackageId_t k_uPackageIdInvalid = 0xFFFFFFFF; + + +// this is baked into client messages and interfaces as an int, +// make sure we never break this. +typedef uint32 AppId_t; +const AppId_t k_uAppIdInvalid = 0x0; + + +// this is baked into client messages and interfaces as an int, +// make sure we never break this. AppIds and DepotIDs also presently +// share the same namespace, but since we'd like to change that in the future +// I've defined it seperately here. +typedef uint32 DepotId_t; +const DepotId_t k_uDepotIdInvalid = 0x0; + +// RTime32 +// We use this 32 bit time representing real world time. +// It offers 1 second resolution beginning on January 1, 1970 (Unix time) +typedef uint32 RTime32; + +typedef uint32 CellID_t; +const CellID_t k_uCellIDInvalid = 0xFFFFFFFF; + +// handle to a Steam API call +typedef uint64 SteamAPICall_t; +const SteamAPICall_t k_uAPICallInvalid = 0x0; + + + + +#endif // STEAMTYPES_H diff --git a/public/string_t.h b/public/string_t.h new file mode 100644 index 0000000..0914199 --- /dev/null +++ b/public/string_t.h @@ -0,0 +1,113 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Defines the more complete set of operations on the string_t defined +// These should be used instead of direct manipulation to allow more +// flexibility in future ports or optimization. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STRING_T_H +#define STRING_T_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifndef NO_STRING_T + +#ifdef WEAK_STRING_T + +typedef int string_t; + +//----------------------------------------------------------------------------- +// Purpose: The correct way to specify the NULL string as a constant. +//----------------------------------------------------------------------------- + +#define NULL_STRING 0 + +//----------------------------------------------------------------------------- +// Purpose: Given a string_t, make a C string. By convention the result string +// pointer should be considered transient and should not be stored. +//----------------------------------------------------------------------------- + +#define STRING( offset ) ( ( offset ) ? reinterpret_cast( offset ) : "" ) + +//----------------------------------------------------------------------------- +// Purpose: Given a C string, obtain a string_t +//----------------------------------------------------------------------------- + +#define MAKE_STRING( str ) ( ( *str != 0 ) ? reinterpret_cast( str ) : 0 ) + +//----------------------------------------------------------------------------- + +#define IDENT_STRINGS( s1, s2 ) ( *((void **)&(s1)) == *((void **)&(s2)) ) + +//----------------------------------------------------------------------------- + +#else // Strong string_t + +//----------------------------------------------------------------------------- + +struct string_t +{ +public: + bool operator!() const { return ( pszValue == NULL ); } + bool operator==( const string_t &rhs ) const { return ( pszValue == rhs.pszValue ); } + bool operator!=( const string_t &rhs ) const { return ( pszValue != rhs.pszValue ); } + bool operator<( const string_t &rhs ) const { return ((void *)pszValue < (void *)rhs.pszValue ); } + + const char *ToCStr() const { return ( pszValue ) ? pszValue : ""; } + +protected: + const char *pszValue; +}; + +//----------------------------------------------------------------------------- + +struct castable_string_t : public string_t // string_t is used in unions, hence, no constructor allowed +{ + castable_string_t() { pszValue = NULL; } + castable_string_t( const char *pszFrom ) { pszValue = (pszFrom && *pszFrom) ? pszFrom : 0; } +}; + +//----------------------------------------------------------------------------- +// Purpose: The correct way to specify the NULL string as a constant. +//----------------------------------------------------------------------------- + +#define NULL_STRING castable_string_t() + +//----------------------------------------------------------------------------- +// Purpose: Given a string_t, make a C string. By convention the result string +// pointer should be considered transient and should not be stored. +//----------------------------------------------------------------------------- + +#define STRING( string_t_obj ) (string_t_obj).ToCStr() + +//----------------------------------------------------------------------------- +// Purpose: Given a C string, obtain a string_t +//----------------------------------------------------------------------------- + +#define MAKE_STRING( c_str ) castable_string_t( c_str ) + +//----------------------------------------------------------------------------- + +#define IDENT_STRINGS( s1, s2 ) ( *((void **)&(s1)) == *((void **)&(s2)) ) + +//----------------------------------------------------------------------------- + +#endif + +#else // NO_STRING_T + +typedef const char *string_t; +#define NULL_STRING 0 +#define STRING( c_str ) ( c_str ) +#define MAKE_STRING( c_str ) ( c_str ) +#define IDENT_STRINGS( s1, s2 ) ( *((void **)&(s1)) == *((void **)&(s2)) ) + +#endif // NO_STRING_T + +//============================================================================= + +#endif // STRING_T_H diff --git a/public/stringregistry.cpp b/public/stringregistry.cpp new file mode 100644 index 0000000..b1f6564 --- /dev/null +++ b/public/stringregistry.cpp @@ -0,0 +1,145 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A registry of strings and associated ints +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + + +#include +#include +#include "stringregistry.h" +#include "utldict.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL) + +//----------------------------------------------------------------------------- +// Purpose: This class wraps the containers that do the actual work +//----------------------------------------------------------------------------- +struct StringTable_t : public CUtlDict +{ +}; + + + +//----------------------------------------------------------------------------- +// Purpose: Add null terminated string to the string registry +// Input : +// Output : +//----------------------------------------------------------------------------- +unsigned short CStringRegistry::AddString(const char *stringText, int stringID) +{ + return m_pStringList->Insert( stringText, stringID ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given string text get the string ID +// Input : Text of string to find +// Output : Return string id or -1 if no such string exists +//----------------------------------------------------------------------------- +int CStringRegistry::GetStringID( const char *stringText ) +{ + unsigned short index = m_pStringList->Find( stringText ); + if ( m_pStringList->IsValidIndex( index ) ) + { + return (*m_pStringList)[index]; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a string ID return the string text +// Input : ID of string to find +// Output : Return string text of NULL of no such ID exists +//----------------------------------------------------------------------------- +char const *CStringRegistry::GetStringText( int stringID ) +{ + for( unsigned short index = m_pStringList->First() ; index != m_pStringList->InvalidIndex(); index = m_pStringList->Next( index ) ) + { + if ( (*m_pStringList)[index] == stringID ) + { + return m_pStringList->GetElementName( index ); + } + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Given a key return the string text +//----------------------------------------------------------------------------- +char const *CStringRegistry::GetStringForKey( unsigned short key ) +{ + if ( !m_pStringList->IsValidIndex( key ) ) + return NULL; + + return m_pStringList->GetElementName( key ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given a key return the string text +//----------------------------------------------------------------------------- +int CStringRegistry::GetIDForKey( unsigned short key ) +{ + if ( !m_pStringList->IsValidIndex( key ) ) + return 0; + + return (*m_pStringList)[key]; +} + +//----------------------------------------------------------------------------- +// Purpose: Clear all strings from the string registry +//----------------------------------------------------------------------------- +void CStringRegistry::ClearStrings(void) +{ + m_pStringList->RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor - delete the list of strings and maps +// Input : +// Output : +//----------------------------------------------------------------------------- +CStringRegistry::~CStringRegistry(void) +{ + delete m_pStringList; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : +// Output : +//----------------------------------------------------------------------------- +CStringRegistry::CStringRegistry(void) +{ + m_pStringList = new StringTable_t; +} + + +unsigned short CStringRegistry::First() const +{ + return m_pStringList->First(); +} + +unsigned short CStringRegistry::Next( unsigned short key ) const +{ + return m_pStringList->Next( key ); +} + +unsigned short CStringRegistry::InvalidIndex() const +{ + return m_pStringList->InvalidIndex(); +} + +#endif // _STATIC_LINKED && CLIENT_DLL diff --git a/public/stringregistry.h b/public/stringregistry.h new file mode 100644 index 0000000..2609aaa --- /dev/null +++ b/public/stringregistry.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A registry of strings and associated ints +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STRINGREGISTRY_H +#define STRINGREGISTRY_H +#pragma once + +struct StringTable_t; + +//----------------------------------------------------------------------------- +// Purpose: Just a convenience/legacy wrapper for CUtlDict<> . +//----------------------------------------------------------------------------- +class CStringRegistry +{ +private: + StringTable_t *m_pStringList; + +public: + // returns a key for a given string + unsigned short AddString(const char *stringText, int stringID); + + // This is optimized. It will do 2 O(logN) searches + // Only one of the searches needs to compare strings, the other compares symbols (ints) + // returns -1 if the string is not present in the registry. + int GetStringID(const char *stringText); + + // This is unoptimized. It will linearly search (but only compares ints, not strings) + const char *GetStringText(int stringID); + + // This is O(1). It will not search. key MUST be a value that was returned by AddString + const char *GetStringForKey(unsigned short key); + // This is O(1). It will not search. key MUST be a value that was returned by AddString + int GetIDForKey(unsigned short key); + + void ClearStrings(void); + + + // Iterate all the keys. + unsigned short First() const; + unsigned short Next( unsigned short key ) const; + unsigned short InvalidIndex() const; + + ~CStringRegistry(void); // Need to free allocated memory + CStringRegistry(void); +}; + +#endif // STRINGREGISTRY_H diff --git a/public/studio.cpp b/public/studio.cpp new file mode 100644 index 0000000..0089e24 --- /dev/null +++ b/public/studio.cpp @@ -0,0 +1,2160 @@ +//========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "studio.h" +#include "datacache/idatacache.h" +#include "datacache/imdlcache.h" +#include "convar.h" +#include "tier1/utlmap.h" +#include "tier0/vprof.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +mstudioanimdesc_t &studiohdr_t::pAnimdesc( int i ) const +{ + if (numincludemodels == 0) + { + return *pLocalAnimdesc( i ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_anim[i].group ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + Assert( pStudioHdr ); + + return *pStudioHdr->pLocalAnimdesc( pVModel->m_anim[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +byte *mstudioanimdesc_t::pAnimBlock( int block, int index ) const +{ + if (block == -1) + { + return (byte *)NULL; + } + if (block == 0) + { + return (((byte *)this) + index); + } + + byte *pAnimBlock = pStudiohdr()->GetAnimBlock( block ); + if ( pAnimBlock ) + { + return pAnimBlock + index; + } + + return (byte *)NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +static ConVar mod_load_showstall( "mod_load_showstall", "0", 0, "1 - show hitches , 2 - show stalls" ); +byte *mstudioanimdesc_t::pAnim( int *piFrame ) const +{ + float flStall; + return pAnim( piFrame, flStall ); +} + +byte *mstudioanimdesc_t::pAnim( int *piFrame, float &flStall ) const +{ + byte *panim = NULL; + + int block = animblock; + int index = animindex; + int section = 0; + + if (sectionframes != 0) + { + if (numframes > sectionframes && *piFrame == numframes - 1) + { + // last frame on long anims is stored separately + *piFrame = 0; + section = (numframes / sectionframes) + 1; + } + else + { + section = *piFrame / sectionframes; + *piFrame -= section * sectionframes; + } + + block = pSection( section )->animblock; + index = pSection( section )->animindex; + } + + if (block == -1) + { + // model needs to be recompiled + return NULL; + } + + panim = pAnimBlock( block, index ); + + // force a preload on the next block + if ( sectionframes != 0 ) + { + int count = ( numframes / sectionframes) + 2; + for ( int i = section + 1; i < count; i++ ) + { + if ( pSection( i )->animblock != block ) + { + pAnimBlock( pSection( i )->animblock, pSection( i )->animindex ); + break; + } + } + } + + if (panim == NULL) + { + if (section > 0 && mod_load_showstall.GetInt() > 0) + { + Msg("[%8.3f] hitch on %s:%s:%d:%d\n", Plat_FloatTime(), pStudiohdr()->pszName(), pszName(), section, block ); + } + // back up until a previously loaded block is found + while (--section >= 0) + { + block = pSection( section )->animblock; + index = pSection( section )->animindex; + panim = pAnimBlock( block, index ); + if (panim) + { + // set it to the last frame in the last valid section + *piFrame = sectionframes - 1; + break; + } + } + } + + // try to guess a valid stall time interval (tuned for the X360) + flStall = 0.0f; + if (panim == NULL && section <= 0) + { + zeroframestalltime = Plat_FloatTime(); + flStall = 1.0f; + } + else if (panim != NULL && zeroframestalltime != 0.0f) + { + float dt = Plat_FloatTime() - zeroframestalltime; + if (dt >= 0.0) + { + flStall = SimpleSpline( clamp( (0.200f - dt) * 5.0, 0.0f, 1.0f ) ); + } + + if (flStall == 0.0f) + { + // disable stalltime + zeroframestalltime = 0.0f; + } + else if (mod_load_showstall.GetInt() > 1) + { + Msg("[%8.3f] stall blend %.2f on %s:%s:%d:%d\n", Plat_FloatTime(), flStall, pStudiohdr()->pszName(), pszName(), section, block ); + } + } + + if (panim == NULL && mod_load_showstall.GetInt() > 1) + { + Msg("[%8.3f] stall on %s:%s:%d:%d\n", Plat_FloatTime(), pStudiohdr()->pszName(), pszName(), section, block ); + } + + return panim; +} + +mstudioikrule_t *mstudioanimdesc_t::pIKRule( int i ) const +{ + if (numikrules) + { + if (ikruleindex) + { + return (mstudioikrule_t *)(((byte *)this) + ikruleindex) + i; + } + else + { + if (animblock == 0) + { + AssertOnce(0); // Should never happen + return (mstudioikrule_t *)(((byte *)this) + animblockikruleindex) + i; + } + else + { + byte *pAnimBlock = pStudiohdr()->GetAnimBlock( animblock ); + + if ( pAnimBlock ) + { + return (mstudioikrule_t *)(pAnimBlock + animblockikruleindex) + i; + } + } + } + } + + return NULL; +} + + +mstudiolocalhierarchy_t *mstudioanimdesc_t::pHierarchy( int i ) const +{ + if (localhierarchyindex) + { + if (animblock == 0) + { + return (mstudiolocalhierarchy_t *)(((byte *)this) + localhierarchyindex) + i; + } + else + { + byte *pAnimBlock = pStudiohdr()->GetAnimBlock( animblock ); + + if ( pAnimBlock ) + { + return (mstudiolocalhierarchy_t *)(pAnimBlock + localhierarchyindex) + i; + } + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool studiohdr_t::SequencesAvailable() const +{ + if (numincludemodels == 0) + { + return true; + } + + return ( GetVirtualModel() != NULL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetNumSeq( void ) const +{ + if (numincludemodels == 0) + { + return numlocalseq; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + return pVModel->m_seq.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +mstudioseqdesc_t &studiohdr_t::pSeqdesc( int i ) const +{ + if (numincludemodels == 0) + { + return *pLocalSeqdesc( i ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + if ( !pVModel ) + { + return *pLocalSeqdesc( i ); + } + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[i].group ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + Assert( pStudioHdr ); + + return *pStudioHdr->pLocalSeqdesc( pVModel->m_seq[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::iRelativeAnim( int baseseq, int relanim ) const +{ + if (numincludemodels == 0) + { + return relanim; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[baseseq].group ]; + + return pGroup->masterAnim[ relanim ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::iRelativeSeq( int baseseq, int relseq ) const +{ + if (numincludemodels == 0) + { + return relseq; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[baseseq].group ]; + + return pGroup->masterSeq[ relseq ]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetNumPoseParameters( void ) const +{ + if (numincludemodels == 0) + { + return numlocalposeparameters; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + return pVModel->m_pose.Count(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +const mstudioposeparamdesc_t &studiohdr_t::pPoseParameter( int i ) +{ + if (numincludemodels == 0) + { + return *pLocalPoseParameter( i ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + if ( pVModel->m_pose[i].group == 0) + return *pLocalPoseParameter( pVModel->m_pose[i].index ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_pose[i].group ]; + + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + Assert( pStudioHdr ); + + return *pStudioHdr->pLocalPoseParameter( pVModel->m_pose[i].index ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetSharedPoseParameter( int iSequence, int iLocalPose ) const +{ + if (numincludemodels == 0) + { + return iLocalPose; + } + + if (iLocalPose == -1) + return iLocalPose; + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; + + return pGroup->masterPose[iLocalPose]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::EntryNode( int iSequence ) +{ + mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); + + if (numincludemodels == 0 || seqdesc.localentrynode == 0) + { + return seqdesc.localentrynode; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; + + return pGroup->masterNode[seqdesc.localentrynode-1]+1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +int studiohdr_t::ExitNode( int iSequence ) +{ + mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); + + if (numincludemodels == 0 || seqdesc.localexitnode == 0) + { + return seqdesc.localexitnode; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_seq[iSequence].group ]; + + return pGroup->masterNode[seqdesc.localexitnode-1]+1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetNumAttachments( void ) const +{ + if (numincludemodels == 0) + { + return numlocalattachments; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + return pVModel->m_attachment.Count(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +const mstudioattachment_t &studiohdr_t::pAttachment( int i ) const +{ + if (numincludemodels == 0) + { + return *pLocalAttachment( i ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[i].group ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + Assert( pStudioHdr ); + + return *pStudioHdr->pLocalAttachment( pVModel->m_attachment[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetAttachmentBone( int i ) +{ + const mstudioattachment_t &attachment = pAttachment( i ); + + // remap bone + virtualmodel_t *pVModel = GetVirtualModel(); + if (pVModel) + { + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[i].group ]; + int iBone = pGroup->masterBone[attachment.localbone]; + if (iBone == -1) + return 0; + return iBone; + } + return attachment.localbone; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void studiohdr_t::SetAttachmentBone( int iAttachment, int iBone ) +{ + mstudioattachment_t &attachment = (mstudioattachment_t &)pAttachment( iAttachment ); + + // remap bone + virtualmodel_t *pVModel = GetVirtualModel(); + if (pVModel) + { + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_attachment[iAttachment].group ]; + iBone = pGroup->boneMap[iBone]; + } + attachment.localbone = iBone; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +char *studiohdr_t::pszNodeName( int iNode ) +{ + if (numincludemodels == 0) + { + return pszLocalNodeName( iNode ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + if ( pVModel->m_node.Count() <= iNode-1 ) + return "Invalid node"; + + return pVModel->m_group[ pVModel->m_node[iNode-1].group ].GetStudioHdr()->pszLocalNodeName( pVModel->m_node[iNode-1].index ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int studiohdr_t::GetTransition( int iFrom, int iTo ) const +{ + if (numincludemodels == 0) + { + return *pLocalTransition( (iFrom-1)*numlocalnodes + (iTo - 1) ); + } + + return iTo; + /* + FIXME: not connected + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + return pVModel->m_transition.Element( iFrom ).Element( iTo ); + */ +} + + +int studiohdr_t::GetActivityListVersion( void ) +{ + if (numincludemodels == 0) + { + return activitylistversion; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + int version = activitylistversion; + + int i; + for (i = 1; i < pVModel->m_group.Count(); i++) + { + virtualgroup_t *pGroup = &pVModel->m_group[ i ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + + Assert( pStudioHdr ); + + version = MIN( version, pStudioHdr->activitylistversion ); + } + + return version; +} + +void studiohdr_t::SetActivityListVersion( int version ) const +{ + activitylistversion = version; + + if (numincludemodels == 0) + { + return; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + int i; + for (i = 1; i < pVModel->m_group.Count(); i++) + { + virtualgroup_t *pGroup = &pVModel->m_group[ i ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + + Assert( pStudioHdr ); + + pStudioHdr->SetActivityListVersion( version ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +int studiohdr_t::GetNumIKAutoplayLocks( void ) const +{ + if (numincludemodels == 0) + { + return numlocalikautoplaylocks; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + return pVModel->m_iklock.Count(); +} + +const mstudioiklock_t &studiohdr_t::pIKAutoplayLock( int i ) +{ + if (numincludemodels == 0) + { + return *pLocalIKAutoplayLock( i ); + } + + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + virtualgroup_t *pGroup = &pVModel->m_group[ pVModel->m_iklock[i].group ]; + const studiohdr_t *pStudioHdr = pGroup->GetStudioHdr(); + Assert( pStudioHdr ); + + return *pStudioHdr->pLocalIKAutoplayLock( pVModel->m_iklock[i].index ); +} + +int studiohdr_t::CountAutoplaySequences() const +{ + int count = 0; + for (int i = 0; i < GetNumSeq(); i++) + { + mstudioseqdesc_t &seqdesc = pSeqdesc( i ); + if (seqdesc.flags & STUDIO_AUTOPLAY) + { + count++; + } + } + return count; +} + +int studiohdr_t::CopyAutoplaySequences( unsigned short *pOut, int outCount ) const +{ + int outIndex = 0; + for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) + { + mstudioseqdesc_t &seqdesc = pSeqdesc( i ); + if (seqdesc.flags & STUDIO_AUTOPLAY) + { + pOut[outIndex] = i; + outIndex++; + } + } + return outIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: maps local sequence bone to global bone +//----------------------------------------------------------------------------- + +int studiohdr_t::RemapSeqBone( int iSequence, int iLocalBone ) const +{ + // remap bone + virtualmodel_t *pVModel = GetVirtualModel(); + if (pVModel) + { + const virtualgroup_t *pSeqGroup = pVModel->pSeqGroup( iSequence ); + return pSeqGroup->masterBone[iLocalBone]; + } + return iLocalBone; +} + +int studiohdr_t::RemapAnimBone( int iAnim, int iLocalBone ) const +{ + // remap bone + virtualmodel_t *pVModel = GetVirtualModel(); + if (pVModel) + { + const virtualgroup_t *pAnimGroup = pVModel->pAnimGroup( iAnim ); + return pAnimGroup->masterBone[iLocalBone]; + } + return iLocalBone; +} + + + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +CStudioHdr::CStudioHdr( void ) +{ + // set pointer to bogus value + m_nFrameUnlockCounter = 0; + m_pFrameUnlockCounter = &m_nFrameUnlockCounter; + Init( NULL ); +} + +CStudioHdr::CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) +{ + // preset pointer to bogus value (it may be overwritten with legitimate data later) + m_nFrameUnlockCounter = 0; + m_pFrameUnlockCounter = &m_nFrameUnlockCounter; + Init( pStudioHdr, mdlcache ); +} + + +// extern IDataCache *g_pDataCache; + +void CStudioHdr::Init( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache ) +{ + m_pStudioHdr = pStudioHdr; + + m_pVModel = NULL; + m_pStudioHdrCache.RemoveAll(); + + if (m_pStudioHdr == NULL) + { + return; + } + + if ( mdlcache ) + { + m_pFrameUnlockCounter = mdlcache->GetFrameUnlockCounterPtr( MDLCACHE_STUDIOHDR ); + m_nFrameUnlockCounter = *m_pFrameUnlockCounter - 1; + } + + if (m_pStudioHdr->numincludemodels != 0) + { + ResetVModel( m_pStudioHdr->GetVirtualModel() ); + } + + m_boneFlags.EnsureCount( numbones() ); + m_boneParent.EnsureCount( numbones() ); + for (int i = 0; i < numbones(); i++) + { + m_boneFlags[i] = pBone( i )->flags; + m_boneParent[i] = pBone( i )->parent; + } + + m_pActivityToSequence = NULL; +} + +void CStudioHdr::Term() +{ + CActivityToSequenceMapping::ReleaseMapping( m_pActivityToSequence ); + m_pActivityToSequence = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +bool CStudioHdr::SequencesAvailable() const +{ + if (m_pStudioHdr->numincludemodels == 0) + { + return true; + } + + if (m_pVModel == NULL) + { + // repoll m_pVModel + return (ResetVModel( m_pStudioHdr->GetVirtualModel() ) != NULL); + } + else + return true; +} + + +const virtualmodel_t * CStudioHdr::ResetVModel( const virtualmodel_t *pVModel ) const +{ + if (pVModel != NULL) + { + m_pVModel = (virtualmodel_t *)pVModel; +#if !defined( POSIX ) + Assert( !pVModel->m_Lock.GetOwnerId() ); +#endif + m_pStudioHdrCache.SetCount( m_pVModel->m_group.Count() ); + + int i; + for (i = 0; i < m_pStudioHdrCache.Count(); i++) + { + m_pStudioHdrCache[ i ] = NULL; + } + + return const_cast(pVModel); + } + else + { + m_pVModel = NULL; + return NULL; + } +} + +const studiohdr_t *CStudioHdr::GroupStudioHdr( int i ) +{ + if ( !this ) + { + ExecuteNTimes( 5, Warning( "Call to NULL CStudioHdr::GroupStudioHdr()\n" ) ); + } + + if ( m_nFrameUnlockCounter != *m_pFrameUnlockCounter ) + { + m_FrameUnlockCounterMutex.Lock(); + if ( *m_pFrameUnlockCounter != m_nFrameUnlockCounter ) // i.e., this thread got the mutex + { + memset( m_pStudioHdrCache.Base(), 0, m_pStudioHdrCache.Count() * sizeof(studiohdr_t *) ); + m_nFrameUnlockCounter = *m_pFrameUnlockCounter; + } + m_FrameUnlockCounterMutex.Unlock(); + } + + if ( !m_pStudioHdrCache.IsValidIndex( i ) ) + { + const char *pszName = ( m_pStudioHdr ) ? m_pStudioHdr->pszName() : "<>"; + ExecuteNTimes( 5, Warning( "Invalid index passed to CStudioHdr(%s)::GroupStudioHdr(): %d [%d]\n", pszName, i, m_pStudioHdrCache.Count() ) ); + DebuggerBreakIfDebugging(); + return m_pStudioHdr; // return something known to probably exist, certainly things will be messed up, but hopefully not crash before the warning is noticed + } + + const studiohdr_t *pStudioHdr = m_pStudioHdrCache[ i ]; + + if (pStudioHdr == NULL) + { +#if !defined( POSIX ) + Assert( !m_pVModel->m_Lock.GetOwnerId() ); +#endif + virtualgroup_t *pGroup = &m_pVModel->m_group[ i ]; + pStudioHdr = pGroup->GetStudioHdr(); + m_pStudioHdrCache[ i ] = pStudioHdr; + } + + Assert( pStudioHdr ); + return pStudioHdr; +} + + +const studiohdr_t *CStudioHdr::pSeqStudioHdr( int sequence ) +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr; + } + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_seq[sequence].group ); + + return pStudioHdr; +} + + +const studiohdr_t *CStudioHdr::pAnimStudioHdr( int animation ) +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr; + } + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_anim[animation].group ); + + return pStudioHdr; +} + + + +mstudioanimdesc_t &CStudioHdr::pAnimdesc( int i ) +{ + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalAnimdesc( i ); + } + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_anim[i].group ); + + return *pStudioHdr->pLocalAnimdesc( m_pVModel->m_anim[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetNumSeq( void ) const +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->numlocalseq; + } + + return m_pVModel->m_seq.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +mstudioseqdesc_t &CStudioHdr::pSeqdesc( int i ) +{ + Assert( i >= 0 && i < GetNumSeq() ); + if ( i < 0 || i >= GetNumSeq() ) + { + // Avoid reading random memory. + i = 0; + } + + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalSeqdesc( i ); + } + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_seq[i].group ); + + return *pStudioHdr->pLocalSeqdesc( m_pVModel->m_seq[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::iRelativeAnim( int baseseq, int relanim ) const +{ + if (m_pVModel == NULL) + { + return relanim; + } + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[baseseq].group ]; + + return pGroup->masterAnim[ relanim ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::iRelativeSeq( int baseseq, int relseq ) const +{ + if (m_pVModel == NULL) + { + return relseq; + } + + Assert( m_pVModel ); + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[baseseq].group ]; + + return pGroup->masterSeq[ relseq ]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetNumPoseParameters( void ) const +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->numlocalposeparameters; + } + + Assert( m_pVModel ); + + return m_pVModel->m_pose.Count(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +const mstudioposeparamdesc_t &CStudioHdr::pPoseParameter( int i ) +{ + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalPoseParameter( i ); + } + + if ( m_pVModel->m_pose[i].group == 0) + return *m_pStudioHdr->pLocalPoseParameter( m_pVModel->m_pose[i].index ); + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_pose[i].group ); + + return *pStudioHdr->pLocalPoseParameter( m_pVModel->m_pose[i].index ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetSharedPoseParameter( int iSequence, int iLocalPose ) const +{ + if (m_pVModel == NULL) + { + return iLocalPose; + } + + if (iLocalPose == -1) + return iLocalPose; + + Assert( m_pVModel ); + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; + + return pGroup->masterPose[iLocalPose]; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::EntryNode( int iSequence ) +{ + mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); + + if (m_pVModel == NULL || seqdesc.localentrynode == 0) + { + return seqdesc.localentrynode; + } + + Assert( m_pVModel ); + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; + + return pGroup->masterNode[seqdesc.localentrynode-1]+1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +int CStudioHdr::ExitNode( int iSequence ) +{ + mstudioseqdesc_t &seqdesc = pSeqdesc( iSequence ); + + if (m_pVModel == NULL || seqdesc.localexitnode == 0) + { + return seqdesc.localexitnode; + } + + Assert( m_pVModel ); + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_seq[iSequence].group ]; + + return pGroup->masterNode[seqdesc.localexitnode-1]+1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetNumAttachments( void ) const +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->numlocalattachments; + } + + Assert( m_pVModel ); + + return m_pVModel->m_attachment.Count(); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +const mstudioattachment_t &CStudioHdr::pAttachment( int i ) +{ + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalAttachment( i ); + } + + Assert( m_pVModel ); + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_attachment[i].group ); + + return *pStudioHdr->pLocalAttachment( m_pVModel->m_attachment[i].index ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetAttachmentBone( int i ) +{ + if (m_pVModel == 0) + { + return m_pStudioHdr->pLocalAttachment( i )->localbone; + } + + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_attachment[i].group ]; + const mstudioattachment_t &attachment = pAttachment( i ); + int iBone = pGroup->masterBone[attachment.localbone]; + if (iBone == -1) + return 0; + return iBone; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CStudioHdr::SetAttachmentBone( int iAttachment, int iBone ) +{ + mstudioattachment_t &attachment = (mstudioattachment_t &)m_pStudioHdr->pAttachment( iAttachment ); + + // remap bone + if (m_pVModel) + { + virtualgroup_t *pGroup = &m_pVModel->m_group[ m_pVModel->m_attachment[iAttachment].group ]; + iBone = pGroup->boneMap[iBone]; + } + attachment.localbone = iBone; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +char *CStudioHdr::pszNodeName( int iNode ) +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->pszLocalNodeName( iNode ); + } + + if ( m_pVModel->m_node.Count() <= iNode-1 ) + return "Invalid node"; + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_node[iNode-1].group ); + + return pStudioHdr->pszLocalNodeName( m_pVModel->m_node[iNode-1].index ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetTransition( int iFrom, int iTo ) const +{ + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalTransition( (iFrom-1)*m_pStudioHdr->numlocalnodes + (iTo - 1) ); + } + + return iTo; + /* + FIXME: not connected + virtualmodel_t *pVModel = (virtualmodel_t *)GetVirtualModel(); + Assert( pVModel ); + + return pVModel->m_transition.Element( iFrom ).Element( iTo ); + */ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetActivityListVersion( void ) +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->activitylistversion; + } + + int version = m_pStudioHdr->activitylistversion; + + int i; + for (i = 1; i < m_pVModel->m_group.Count(); i++) + { + const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); + Assert( pStudioHdr ); + version = MIN( version, pStudioHdr->activitylistversion ); + } + + return version; +} + +void CStudioHdr::SetActivityListVersion( int version ) +{ + m_pStudioHdr->activitylistversion = version; + + if (m_pVModel == NULL) + { + return; + } + + int i; + for (i = 1; i < m_pVModel->m_group.Count(); i++) + { + const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); + Assert( pStudioHdr ); + pStudioHdr->SetActivityListVersion( version ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +int CStudioHdr::GetEventListVersion( void ) +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->eventsindexed; + } + + int version = m_pStudioHdr->eventsindexed; + + int i; + for (i = 1; i < m_pVModel->m_group.Count(); i++) + { + const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); + Assert( pStudioHdr ); + version = MIN( version, pStudioHdr->eventsindexed ); + } + + return version; +} + +void CStudioHdr::SetEventListVersion( int version ) +{ + m_pStudioHdr->eventsindexed = version; + + if (m_pVModel == NULL) + { + return; + } + + int i; + for (i = 1; i < m_pVModel->m_group.Count(); i++) + { + const studiohdr_t *pStudioHdr = GroupStudioHdr( i ); + Assert( pStudioHdr ); + pStudioHdr->eventsindexed = version; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +int CStudioHdr::GetNumIKAutoplayLocks( void ) const +{ + if (m_pVModel == NULL) + { + return m_pStudioHdr->numlocalikautoplaylocks; + } + + return m_pVModel->m_iklock.Count(); +} + +const mstudioiklock_t &CStudioHdr::pIKAutoplayLock( int i ) +{ + if (m_pVModel == NULL) + { + return *m_pStudioHdr->pLocalIKAutoplayLock( i ); + } + + const studiohdr_t *pStudioHdr = GroupStudioHdr( m_pVModel->m_iklock[i].group ); + Assert( pStudioHdr ); + return *pStudioHdr->pLocalIKAutoplayLock( m_pVModel->m_iklock[i].index ); +} + +#if 0 +int CStudioHdr::CountAutoplaySequences() const +{ + int count = 0; + for (int i = 0; i < GetNumSeq(); i++) + { + mstudioseqdesc_t &seqdesc = pSeqdesc( i ); + if (seqdesc.flags & STUDIO_AUTOPLAY) + { + count++; + } + } + return count; +} + +int CStudioHdr::CopyAutoplaySequences( unsigned short *pOut, int outCount ) const +{ + int outIndex = 0; + for (int i = 0; i < GetNumSeq() && outIndex < outCount; i++) + { + mstudioseqdesc_t &seqdesc = pSeqdesc( i ); + if (seqdesc.flags & STUDIO_AUTOPLAY) + { + pOut[outIndex] = i; + outIndex++; + } + } + return outIndex; +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: maps local sequence bone to global bone +//----------------------------------------------------------------------------- + +int CStudioHdr::RemapSeqBone( int iSequence, int iLocalBone ) const +{ + // remap bone + if (m_pVModel) + { + const virtualgroup_t *pSeqGroup = m_pVModel->pSeqGroup( iSequence ); + return pSeqGroup->masterBone[iLocalBone]; + } + return iLocalBone; +} + +int CStudioHdr::RemapAnimBone( int iAnim, int iLocalBone ) const +{ + // remap bone + if (m_pVModel) + { + const virtualgroup_t *pAnimGroup = m_pVModel->pAnimGroup( iAnim ); + return pAnimGroup->masterBone[iLocalBone]; + } + return iLocalBone; +} + +//----------------------------------------------------------------------------- +// Purpose: run the interpreted FAC's expressions, converting flex_controller +// values into FAC weights +//----------------------------------------------------------------------------- +void CStudioHdr::RunFlexRulesOld( const float *src, float *dest ) +{ + int i, j; + + // FIXME: this shouldn't be needed, flex without rules should be stripped in studiomdl + for (i = 0; i < numflexdesc(); i++) + { + dest[i] = 0; + } + + for (i = 0; i < numflexrules(); i++) + { + float stack[32]; + int k = 0; + mstudioflexrule_t *prule = pFlexRule( i ); + + mstudioflexop_t *pops = prule->iFlexOp( 0 ); + + // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), i + 1, 0, "%2d:%d\n", i, prule->flex ); + + for (j = 0; j < prule->numops; j++) + { + switch (pops->op) + { + case STUDIO_ADD: stack[k-2] = stack[k-2] + stack[k-1]; k--; break; + case STUDIO_SUB: stack[k-2] = stack[k-2] - stack[k-1]; k--; break; + case STUDIO_MUL: stack[k-2] = stack[k-2] * stack[k-1]; k--; break; + case STUDIO_DIV: + if (stack[k-1] > 0.0001) + { + stack[k-2] = stack[k-2] / stack[k-1]; + } + else + { + stack[k-2] = 0; + } + k--; + break; + case STUDIO_NEG: stack[k-1] = -stack[k-1]; break; + case STUDIO_MAX: stack[k-2] = MAX( stack[k-2], stack[k-1] ); k--; break; + case STUDIO_MIN: stack[k-2] = MIN( stack[k-2], stack[k-1] ); k--; break; + case STUDIO_CONST: stack[k] = pops->d.value; k++; break; + case STUDIO_FETCH1: + { + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index)->localToGlobal; + stack[k] = src[m]; + k++; + break; + } + case STUDIO_FETCH2: + { + stack[k] = dest[pops->d.index]; k++; break; + } + case STUDIO_COMBO: + { + int m = pops->d.index; + int km = k - m; + for ( int i = km + 1; i < k; ++i ) + { + stack[ km ] *= stack[ i ]; + } + k = k - m + 1; + } + break; + case STUDIO_DOMINATE: + { + int m = pops->d.index; + int km = k - m; + float dv = stack[ km ]; + for ( int i = km + 1; i < k; ++i ) + { + dv *= stack[ i ]; + } + stack[ km - 1 ] *= 1.0f - dv; + k -= m; + } + break; + case STUDIO_2WAY_0: + { + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + stack[ k ] = RemapValClamped( src[m], -1.0f, 0.0f, 1.0f, 0.0f ); + k++; + } + break; + case STUDIO_2WAY_1: + { + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + stack[ k ] = RemapValClamped( src[m], 0.0f, 1.0f, 0.0f, 1.0f ); + k++; + } + break; + case STUDIO_NWAY: + { + LocalFlexController_t valueControllerIndex = static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ); + int m = pFlexcontroller( valueControllerIndex )->localToGlobal; + float flValue = src[ m ]; + int v = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + + const Vector4D filterRamp( stack[ k - 5 ], stack[ k - 4 ], stack[ k - 3 ], stack[ k - 2 ] ); + + // Apply multicontrol remapping + if ( flValue <= filterRamp.x || flValue >= filterRamp.w ) + { + flValue = 0.0f; + } + else if ( flValue < filterRamp.y ) + { + flValue = RemapValClamped( flValue, filterRamp.x, filterRamp.y, 0.0f, 1.0f ); + } + else if ( flValue > filterRamp.z ) + { + flValue = RemapValClamped( flValue, filterRamp.z, filterRamp.w, 1.0f, 0.0f ); + } + else + { + flValue = 1.0f; + } + + stack[ k - 5 ] = flValue * src[ v ]; + + k -= 4; + } + break; + case STUDIO_DME_LOWER_EYELID: + { + const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); + const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); + + const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ) ); + const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); + + int nBlinkIndex = static_cast< int >( stack[ k - 2 ] ); + float flBlink = 0.0f; + if ( nBlinkIndex >= 0 ) + { + const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 2 ] ) ); + flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); + } + + int nEyeUpDownIndex = static_cast< int >( stack[ k - 3 ] ); + float flEyeUpDown = 0.0f; + if ( nEyeUpDownIndex >= 0 ) + { + const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 3 ] ) ); + flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); + } + + if ( flEyeUpDown > 0.0 ) + { + stack [ k - 3 ] = ( 1.0f - flEyeUpDown ) * ( 1.0f - flCloseLidV ) * flCloseLid; + } + else + { + stack [ k - 3 ] = ( 1.0f - flCloseLidV ) * flCloseLid; + } + k -= 2; + } + break; + case STUDIO_DME_UPPER_EYELID: + { + const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); + const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); + + const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 1 ] ) ); + const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); + + int nBlinkIndex = static_cast< int >( stack[ k - 2 ] ); + float flBlink = 0.0f; + if ( nBlinkIndex >= 0 ) + { + const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 2 ] ) ); + flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); + } + + int nEyeUpDownIndex = static_cast< int >( stack[ k - 3 ] ); + float flEyeUpDown = 0.0f; + if ( nEyeUpDownIndex >= 0 ) + { + const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( (int)stack[ k - 3 ] ) ); + flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); + } + + if ( flEyeUpDown < 0.0f ) + { + stack [ k - 3 ] = ( 1.0f + flEyeUpDown ) * flCloseLidV * flCloseLid; + } + else + { + stack [ k - 3 ] = flCloseLidV * flCloseLid; + } + k -= 2; + } + break; + } + + pops++; + } + + dest[prule->flex] = stack[0]; + } +} + +void CStudioHdr::RunFlexRulesNew( const float *src, float *dest ) +{ + // FIXME: this shouldn't be needed, flex without rules should be stripped in studiomdl + memset( dest, 0, sizeof( dest[0] ) * numflexdesc() ); + + for (int i = 0; i < numflexrules(); i++) + { + float stack[32]; + float *pSP = stack + ARRAYSIZE( stack ); + mstudioflexrule_t *prule = pFlexRule( i ); + + mstudioflexop_t *pops = prule->iFlexOp( 0 ); + + int nOps = prule->numops; + float flTOS = 0.; + if ( nOps ) + do + { + switch (pops->op) + { + case STUDIO_ADD: + flTOS += *(pSP++); + break; + + case STUDIO_SUB: + flTOS = *(pSP++) - flTOS; + break; + + case STUDIO_MUL: + flTOS *= *(pSP++); + break; + case STUDIO_DIV: + if (flTOS > 0.0001) + { + flTOS = *(pSP) / flTOS; + } + else + { + flTOS = 0.; + } + pSP++; + break; + + case STUDIO_NEG: + flTOS = -flTOS; + break; + + case STUDIO_MAX: + { + float flNos = *(pSP++); + flTOS = MAX( flTOS, flNos ); + break; + } + + case STUDIO_MIN: + { + float flNos = *(pSP++); + flTOS = MIN( flTOS, flNos); + break; + } + case STUDIO_CONST: + *(--pSP) = flTOS; + flTOS = pops->d.value; + break; + + case STUDIO_FETCH1: + { + *(--pSP ) = flTOS; + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index)->localToGlobal; + flTOS = src[m]; + break; + } + + case STUDIO_FETCH2: + { + *(--pSP) = flTOS; + flTOS = dest[pops->d.index]; + break; + } + case STUDIO_COMBO: + { + // tos = prod( top m elements on stack) + int m = pops->d.index; + while( --m ) + { + flTOS *= *(pSP++); + } + break; + } + break; + + case STUDIO_DOMINATE: + { + // tos *= 1-prod( next top m elements on stack) + int m = pops->d.index; + float dv = *(pSP++); + while( --m ) + { + dv *= *(pSP++); + } + flTOS *= 1.0 - dv; + break; + } + break; + case STUDIO_2WAY_0: + { + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + *(--pSP) = flTOS; + flTOS = RemapValClamped( src[m], -1.0f, 0.0f, 1.0f, 0.0f ); + } + break; + + case STUDIO_2WAY_1: + { + int m = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + *(--pSP) = flTOS; + flTOS = RemapValClamped( src[m], 0.0f, 1.0f, 0.0f, 1.0f ); + } + break; + + case STUDIO_NWAY: + { + LocalFlexController_t valueControllerIndex = static_cast< LocalFlexController_t >( (int) flTOS ); + int m = pFlexcontroller( valueControllerIndex )->localToGlobal; + float flValue = src[ m ]; + int v = pFlexcontroller( (LocalFlexController_t)pops->d.index )->localToGlobal; + + const Vector4D filterRamp( pSP[3], pSP[2], pSP[1], pSP[0] ); + + // Apply multicontrol remapping + if ( flValue <= filterRamp.x || flValue >= filterRamp.w ) + { + flValue = 0.0f; + } + else if ( flValue < filterRamp.y ) + { + flValue = RemapValClamped( flValue, filterRamp.x, filterRamp.y, 0.0f, 1.0f ); + } + else if ( flValue > filterRamp.z ) + { + flValue = RemapValClamped( flValue, filterRamp.z, filterRamp.w, 1.0f, 0.0f ); + } + else + { + flValue = 1.0f; + } + + pSP+= 4; + flTOS = flValue * src[ v ]; + } + break; + + case STUDIO_DME_LOWER_EYELID: + { + const mstudioflexcontroller_t *const pCloseLidV = + pFlexcontroller( (LocalFlexController_t)pops->d.index ); + const float flCloseLidV = + RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); + + const mstudioflexcontroller_t *const pCloseLid = + pFlexcontroller( static_cast< LocalFlexController_t >( (int)flTOS ) ); + const float flCloseLid = + RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); + + int nBlinkIndex = static_cast< int >( pSP[0] ); + float flBlink = 0.0f; + if ( nBlinkIndex >= 0 ) + { + const mstudioflexcontroller_t *const pBlink = + pFlexcontroller( static_cast< LocalFlexController_t >( nBlinkIndex ) ); + flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); + } + + int nEyeUpDownIndex = static_cast< int >( pSP[1] ); + float flEyeUpDown = 0.0f; + if ( nEyeUpDownIndex >= 0 ) + { + const mstudioflexcontroller_t *const pEyeUpDown = + pFlexcontroller( static_cast< LocalFlexController_t >( nEyeUpDownIndex ) ); + flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); + } + + if ( flEyeUpDown > 0.0 ) + { + flTOS = ( 1.0f - flEyeUpDown ) * ( 1.0f - flCloseLidV ) * flCloseLid; + } + else + { + flTOS = ( 1.0f - flCloseLidV ) * flCloseLid; + } + pSP += 2; + } + break; + + case STUDIO_DME_UPPER_EYELID: + { + const mstudioflexcontroller_t *const pCloseLidV = pFlexcontroller( (LocalFlexController_t)pops->d.index ); + const float flCloseLidV = RemapValClamped( src[ pCloseLidV->localToGlobal ], pCloseLidV->min, pCloseLidV->max, 0.0f, 1.0f ); + + const mstudioflexcontroller_t *const pCloseLid = pFlexcontroller( static_cast< LocalFlexController_t >( (int)flTOS ) ); + const float flCloseLid = RemapValClamped( src[ pCloseLid->localToGlobal ], pCloseLid->min, pCloseLid->max, 0.0f, 1.0f ); + + int nBlinkIndex = static_cast< int >( pSP[0] ); + float flBlink = 0.0f; + if ( nBlinkIndex >= 0 ) + { + const mstudioflexcontroller_t *const pBlink = pFlexcontroller( static_cast< LocalFlexController_t >( nBlinkIndex ) ); + flBlink = RemapValClamped( src[ pBlink->localToGlobal ], pBlink->min, pBlink->max, 0.0f, 1.0f ); + } + + int nEyeUpDownIndex = static_cast< int >( pSP[1] ); + float flEyeUpDown = 0.0f; + if ( nEyeUpDownIndex >= 0 ) + { + const mstudioflexcontroller_t *const pEyeUpDown = pFlexcontroller( static_cast< LocalFlexController_t >( nEyeUpDownIndex ) ); + flEyeUpDown = RemapValClamped( src[ pEyeUpDown->localToGlobal ], pEyeUpDown->min, pEyeUpDown->max, -1.0f, 1.0f ); + } + + if ( flEyeUpDown < 0.0f ) + { + flTOS = ( 1.0f + flEyeUpDown ) * flCloseLidV * flCloseLid; + } + else + { + flTOS = flCloseLidV * flCloseLid; + } + pSP += 2; + } + break; + } + + pops++; + } while( --nOps ); + dest[prule->flex] = flTOS; + } +} + +#define USE_OLD_FLEX_RULES_INTERPRETER + +void CStudioHdr::RunFlexRules( const float *src, float *dest ) +{ +#ifndef USE_OLD_FLEX_RULES_INTERPRETER + RunFlexRulesNew( src, dest ); +#else + RunFlexRulesOld( src, dest ); +#endif + +#if defined(_DEBUG) && !defined(USE_OLD_FLEX_RULES_INTERPRETER) + float d1[ MAXSTUDIOFLEXDESC ]; + RunFlexRulesOld( src, d1 ); + + for ( int i =0; i < numflexdesc(); i++) + { + if ( fabs( d1[i] - dest[i] ) > 0.001 ) + { + Warning("bad %d old =%f new=%f\n", i, dest[i], d1[i] ); + } + } +#endif // _DEBUG +} + + + + +//----------------------------------------------------------------------------- +// CODE PERTAINING TO ACTIVITY->SEQUENCE MAPPING SUBCLASS +//----------------------------------------------------------------------------- +#define iabs(i) (( (i) >= 0 ) ? (i) : -(i) ) + +CUtlSymbolTable g_ActivityModifiersTable; + +extern void SetActivityForSequence( CStudioHdr *pstudiohdr, int i ); +void CStudioHdr::CActivityToSequenceMapping::Initialize( const CStudioHdr * __restrict pstudiohdr ) +{ + VPROF( "CStudioHdr::CActivityToSequenceMapping::Initialize" ); + // Algorithm: walk through every sequence in the model, determine to which activity + // it corresponds, and keep a count of sequences per activity. Once the total count + // is available, allocate an array large enough to contain them all, update the + // starting indices for every activity's section in the array, and go back through, + // populating the array with its data. + + m_pStudioHdr = pstudiohdr->m_pStudioHdr; + + AssertMsg1( m_pSequenceTuples == NULL, "Tried to double-initialize sequence mapping for %s", pstudiohdr->pszName() ); + if ( m_pSequenceTuples != NULL ) + return; // don't double initialize. + + SetValidation(pstudiohdr); + + if ( ! pstudiohdr->SequencesAvailable() ) + return; // nothing to do. + + // Some studio headers have no activities at all. In those + // cases we can avoid a lot of this effort. + bool bFoundOne = false; + + // for each sequence in the header... + const int NumSeq = pstudiohdr->GetNumSeq(); + for ( int i = 0 ; i < NumSeq ; ++i ) + { + const mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pstudiohdr)->pSeqdesc( i ); +#if defined(SERVER_DLL) || defined(CLIENT_DLL) || defined(GAME_DLL) + if (!(seqdesc.flags & STUDIO_ACTIVITY)) + { + // AssertMsg2( false, "Sequence %d on studiohdr %s didn't have its activity initialized!", i, pstudiohdr->pszName() ); + SetActivityForSequence( (CStudioHdr *)pstudiohdr, i ); + } +#endif + + // is there an activity associated with this sequence? + if (seqdesc.activity >= 0) + { + bFoundOne = true; + + // look up if we already have an entry. First we need to make a speculative one -- + HashValueType entry(seqdesc.activity, 0, 1, iabs(seqdesc.actweight)); + UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); + if ( m_ActToSeqHash.IsValidHandle(handle) ) + { + // we already have an entry and must update it by incrementing count + HashValueType * __restrict toUpdate = &m_ActToSeqHash.Element(handle); + toUpdate->count += 1; + toUpdate->totalWeight += iabs(seqdesc.actweight); + } + else + { + // we do not have an entry yet; create one. + m_ActToSeqHash.Insert(entry); + } + } + } + + // if we found nothing, don't bother with any other initialization! + if (!bFoundOne) + return; + + // Now, create starting indices for each activity. For an activity n, + // the starting index is of course the sum of counts [0..n-1]. + register int sequenceCount = 0; + int topActivity = 0; // this will store the highest seen activity number (used later to make an ad hoc map on the stack) + for ( UtlHashHandle_t handle = m_ActToSeqHash.GetFirstHandle() ; + m_ActToSeqHash.IsValidHandle(handle) ; + handle = m_ActToSeqHash.GetNextHandle(handle) ) + { + HashValueType &element = m_ActToSeqHash[handle]; + element.startingIdx = sequenceCount; + sequenceCount += element.count; + topActivity = MAX(topActivity, element.activityIdx); + } + + + // Allocate the actual array of sequence information. Note the use of restrict; + // this is an important optimization, but means that you must never refer to this + // array through m_pSequenceTuples in the scope of this function. + SequenceTuple * __restrict tupleList = new SequenceTuple[sequenceCount]; + m_pSequenceTuples = tupleList; // save it off -- NEVER USE m_pSequenceTuples in this function! + m_iSequenceTuplesCount = sequenceCount; + + + + // Now we're going to actually populate that list with the relevant data. + // First, create an array on the stack to store how many sequences we've written + // so far for each activity. (This is basically a very simple way of doing a map.) + // This stack may potentially grow very large; so if you have problems with it, + // go to a utlmap or similar structure. + unsigned int allocsize = (topActivity + 1) * sizeof(int); +#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) // need macro for constant expression + allocsize = ALIGN_VALUE(allocsize,16); + int * __restrict seqsPerAct = static_cast(stackalloc(allocsize)); + memset(seqsPerAct, 0, allocsize); + + // okay, walk through all the sequences again, and write the relevant data into + // our little table. + for ( int i = 0 ; i < NumSeq ; ++i ) + { + const mstudioseqdesc_t &seqdesc = ((CStudioHdr *)pstudiohdr)->pSeqdesc( i ); + if (seqdesc.activity >= 0) + { + const HashValueType &element = m_ActToSeqHash[m_ActToSeqHash.Find(HashValueType(seqdesc.activity, 0, 0, 0))]; + + // If this assert trips, we've written more sequences per activity than we allocated + // (therefore there must have been a miscount in the first for loop above). + int tupleOffset = seqsPerAct[seqdesc.activity]; + Assert( tupleOffset < element.count ); + + if ( seqdesc.numactivitymodifiers > 0 ) + { + // add entries for this model's activity modifiers + (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers = new CUtlSymbol[ seqdesc.numactivitymodifiers ]; + (tupleList + element.startingIdx + tupleOffset)->iNumActivityModifiers = seqdesc.numactivitymodifiers; + + for ( int k = 0; k < seqdesc.numactivitymodifiers; k++ ) + { + (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers[ k ] = g_ActivityModifiersTable.AddString( seqdesc.pActivityModifier( k )->pszName() ); + } + } + else + { + (tupleList + element.startingIdx + tupleOffset)->pActivityModifiers = NULL; + (tupleList + element.startingIdx + tupleOffset)->iNumActivityModifiers = 0; + } + + // You might be tempted to collapse this pointer math into a single pointer -- + // don't! the tuple list is marked __restrict above. + (tupleList + element.startingIdx + tupleOffset)->seqnum = i; // store sequence number + (tupleList + element.startingIdx + tupleOffset)->weight = iabs(seqdesc.actweight); + + seqsPerAct[seqdesc.activity] += 1; + } + } + +#ifdef DBGFLAG_ASSERT + // double check that we wrote exactly the right number of sequences. + unsigned int chkSequenceCount = 0; + for (int j = 0 ; j <= topActivity ; ++j) + { + chkSequenceCount += seqsPerAct[j]; + } + Assert(chkSequenceCount == m_iSequenceTuplesCount); +#endif + +} + +/// Force Initialize() to occur again, even if it has already occured. +void CStudioHdr::CActivityToSequenceMapping::Reinitialize( CStudioHdr *pstudiohdr ) +{ + if (m_pSequenceTuples) + { + delete m_pSequenceTuples; + m_pSequenceTuples = NULL; + } + m_ActToSeqHash.RemoveAll(); + + Initialize(pstudiohdr); +} + +// Look up relevant data for an activity's sequences. This isn't terribly efficient, due to the +// load-hit-store on the output parameters, so the most common case -- SelectWeightedSequence -- +// is specially implemented. +const CStudioHdr::CActivityToSequenceMapping::SequenceTuple *CStudioHdr::CActivityToSequenceMapping::GetSequences( int forActivity, int * __restrict outSequenceCount, int * __restrict outTotalWeight ) +{ + // Construct a dummy entry so we can do a hash lookup (the UtlHash does not divorce keys from values) + + HashValueType entry(forActivity, 0, 0, 0); + UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); + + if (m_ActToSeqHash.IsValidHandle(handle)) + { + const HashValueType &element = m_ActToSeqHash[handle]; + const SequenceTuple *retval = m_pSequenceTuples + element.startingIdx; + *outSequenceCount = element.count; + *outTotalWeight = element.totalWeight; + + return retval; + } + else + { + // invalid handle; return NULL. + // this is actually a legit use case, so no need to assert. + return NULL; + } +} + +int CStudioHdr::CActivityToSequenceMapping::NumSequencesForActivity( int forActivity ) +{ + // If this trips, you've called this function on something that doesn't + // have activities. + //Assert(m_pSequenceTuples != NULL); + if ( m_pSequenceTuples == NULL ) + return 0; + + HashValueType entry(forActivity, 0, 0, 0); + UtlHashHandle_t handle = m_ActToSeqHash.Find(entry); + if (m_ActToSeqHash.IsValidHandle(handle)) + { + return m_ActToSeqHash[handle].count; + } + else + { + return 0; + } +} + +static CStudioHdr::CActivityToSequenceMapping emptyMapping; + +// double-check that the data I point to hasn't changed +bool CStudioHdr::CActivityToSequenceMapping::ValidateAgainst( const CStudioHdr * RESTRICT pstudiohdr ) RESTRICT +{ + return ( this == &emptyMapping || + ( m_pStudioHdr == pstudiohdr->m_pStudioHdr && m_expectedVModel == pstudiohdr->GetVirtualModel() ) ); +} + +void CStudioHdr::CActivityToSequenceMapping::SetValidation( const CStudioHdr *RESTRICT pstudiohdr ) RESTRICT +{ + m_expectedVModel = pstudiohdr->GetVirtualModel(); +} + +struct StudioHdrToActivityMapEntry_t +{ + long checksum; + char name[64]; + int nRefs; + CStudioHdr::CActivityToSequenceMapping *pMap; +}; + +CUtlMap g_StudioHdrToActivityMaps( DefLessFunc( const studiohdr_t * ) ); +CThreadFastMutex g_StudioHdrToActivityMapsLock; + +CStudioHdr::CActivityToSequenceMapping *CStudioHdr::CActivityToSequenceMapping::FindMapping( const CStudioHdr *pHdr ) +{ + VPROF( "CStudioHdr::CActivityToSequenceMapping::FindMapping" ); + + if ( !pHdr->SequencesAvailable() || pHdr->GetNumSeq() <= 1 ) + { + return &emptyMapping; + } + + Assert( !pHdr->m_pActivityToSequence ); + + AUTO_LOCK( g_StudioHdrToActivityMapsLock ); + const studiohdr_t *pRealHdr = pHdr->m_pStudioHdr; + int i = g_StudioHdrToActivityMaps.Find( pRealHdr ); + if ( i != g_StudioHdrToActivityMaps.InvalidIndex() ) + { + if ( !IsX360() && ( g_StudioHdrToActivityMaps[i].checksum != pRealHdr->checksum || Q_strcmp( g_StudioHdrToActivityMaps[i].name, pRealHdr->name ) != 0 ) ) + { + AssertFatal( g_StudioHdrToActivityMaps[i].nRefs == 0 ); + delete g_StudioHdrToActivityMaps[i].pMap; + g_StudioHdrToActivityMaps.RemoveAt( i ); + } + else + { + Assert( g_StudioHdrToActivityMaps[i].checksum == pRealHdr->checksum && Q_strcmp( g_StudioHdrToActivityMaps[i].name, pRealHdr->name ) == 0 ); + g_StudioHdrToActivityMaps[i].nRefs++; + return g_StudioHdrToActivityMaps[i].pMap; + } + } + + i = g_StudioHdrToActivityMaps.Insert( pRealHdr ); + + g_StudioHdrToActivityMaps[i].checksum = pRealHdr->checksum; + Q_strncpy( g_StudioHdrToActivityMaps[i].name, pRealHdr->name, 64 ); + g_StudioHdrToActivityMaps[i].nRefs = 1; + g_StudioHdrToActivityMaps[i].pMap = new CStudioHdr::CActivityToSequenceMapping; + g_StudioHdrToActivityMaps[i].pMap->Initialize( pHdr ); + + return g_StudioHdrToActivityMaps[i].pMap; +} + +void CStudioHdr::CActivityToSequenceMapping::ReleaseMapping( CActivityToSequenceMapping *pMap ) +{ + if ( pMap && pMap != &emptyMapping) + { + VPROF( "CStudioHdr::CActivityToSequenceMapping::ReleaseMapping" ); + AUTO_LOCK( g_StudioHdrToActivityMapsLock ); + int i = g_StudioHdrToActivityMaps.Find( pMap->m_pStudioHdr ); + if ( i != g_StudioHdrToActivityMaps.InvalidIndex() ) + { + Assert( g_StudioHdrToActivityMaps[i].nRefs > 0 ); + g_StudioHdrToActivityMaps[i].nRefs--; + } + else + { + Assert( 0 ); + } + } +} + +void CStudioHdr::CActivityToSequenceMapping::ResetMappings() +{ + for ( int i = g_StudioHdrToActivityMaps.FirstInorder(); i != g_StudioHdrToActivityMaps.InvalidIndex(); i = g_StudioHdrToActivityMaps.NextInorder( i ) ) + { + if ( g_StudioHdrToActivityMaps[i].nRefs == 0 ) + { + delete g_StudioHdrToActivityMaps[i].pMap; + } + else + { + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + Msg( "************* DO NOT IGNORE ME *******************************\n" ); + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + Warning( "Studio activity sequence mapping leak! (%s, %d)\n", g_StudioHdrToActivityMaps[i].name, g_StudioHdrToActivityMaps[i].nRefs ); + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + Msg( "****************************************************************\n" ); + } + } + g_StudioHdrToActivityMaps.RemoveAll(); +} \ No newline at end of file diff --git a/public/studio.h b/public/studio.h new file mode 100644 index 0000000..06f7a41 --- /dev/null +++ b/public/studio.h @@ -0,0 +1,3363 @@ +//===== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef STUDIO_H +#define STUDIO_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "mathlib/compressed_vector.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "mathlib/mathlib.h" +#include "utlvector.h" +#include "utlhash.h" +#include "datamap.h" +#include "generichash.h" +#include "localflexcontroller.h" +#include "utlsymbol.h" + + +#define STUDIO_ENABLE_PERF_COUNTERS + +#define STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW 0 +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- + +class IMaterial; +class IMesh; +class IMorph; +struct virtualmodel_t; +struct vertexFileHeader_t; +struct thinModelVertices_t; + +namespace OptimizedModel +{ + struct StripHeader_t; +} + + +/* +============================================================================== + +STUDIO MODELS + +Studio models are position independent, so the cache manager can move them. +============================================================================== +*/ + +#define STUDIO_VERSION 49 + +struct studiohdr_t; + +#ifdef _X360 +#define MAXSTUDIOTRIANGLES 65536 // +#define MAXSTUDIOVERTS 32768 // These numbers save memory in CCachedRenderData, but restrict usable model sizes on 360 +#define MAXSTUDIOFLEXVERTS 4096 // +#else +#define MAXSTUDIOTRIANGLES 65536 // TODO: tune this +#define MAXSTUDIOVERTS 65536 // TODO: tune this +#define MAXSTUDIOFLEXVERTS 10000 // max number of verts that can be flexed per mesh. TODO: tune this +#endif +#define MAXSTUDIOSKINS 32 // total textures +#define MAXSTUDIOBONES 128 // total bones actually used +#define MAXSTUDIOFLEXDESC 1024 // maximum number of low level flexes (actual morph targets) +#define MAXSTUDIOFLEXCTRL 96 // maximum number of flexcontrollers (input sliders) +#define MAXSTUDIOPOSEPARAM 24 +#define MAXSTUDIOBONECTRLS 4 +#define MAXSTUDIOANIMBLOCKS 256 + +#define MAXSTUDIOBONEBITS 7 // NOTE: MUST MATCH MAXSTUDIOBONES + +// NOTE!!! : Changing this number also changes the vtx file format!!!!! +#define MAX_NUM_BONES_PER_VERT 3 + +//Adrian - Remove this when we completely phase out the old event system. +#define NEW_EVENT_STYLE ( 1 << 10 ) + +struct mstudiodata_t +{ + int count; + int offset; +}; + +#define STUDIO_PROC_AXISINTERP 1 +#define STUDIO_PROC_QUATINTERP 2 +#define STUDIO_PROC_AIMATBONE 3 +#define STUDIO_PROC_AIMATATTACH 4 +#define STUDIO_PROC_JIGGLE 5 +#define STUDIO_PROC_TWIST_MASTER 6 +#define STUDIO_PROC_TWIST_SLAVE 7 // Multiple twist bones are computed at once for the same parent/child combo so TWIST_NULL do nothing + +struct mstudioaxisinterpbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + int control;// local transformation of this bone used to calc 3 point blend + int axis; // axis to check + Vector pos[6]; // X+, X-, Y+, Y-, Z+, Z- + Quaternion quat[6];// X+, X-, Y+, Y-, Z+, Z- + + mstudioaxisinterpbone_t(){} +private: + // No copy constructors allowed + mstudioaxisinterpbone_t(const mstudioaxisinterpbone_t& vOther); +}; + + +struct mstudioquatinterpinfo_t +{ + DECLARE_BYTESWAP_DATADESC(); + float inv_tolerance; // 1 / radian angle of trigger influence + Quaternion trigger; // angle to match + Vector pos; // new position + Quaternion quat; // new angle + + mstudioquatinterpinfo_t(){} +private: + // No copy constructors allowed + mstudioquatinterpinfo_t(const mstudioquatinterpinfo_t& vOther); +}; + +struct mstudioquatinterpbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + int control;// local transformation to check + int numtriggers; + int triggerindex; + inline mstudioquatinterpinfo_t *pTrigger( int i ) const { return (mstudioquatinterpinfo_t *)(((byte *)this) + triggerindex) + i; }; + + mstudioquatinterpbone_t(){} +private: + // No copy constructors allowed + mstudioquatinterpbone_t(const mstudioquatinterpbone_t& vOther); +}; + + +#define JIGGLE_IS_FLEXIBLE 0x01 +#define JIGGLE_IS_RIGID 0x02 +#define JIGGLE_HAS_YAW_CONSTRAINT 0x04 +#define JIGGLE_HAS_PITCH_CONSTRAINT 0x08 +#define JIGGLE_HAS_ANGLE_CONSTRAINT 0x10 +#define JIGGLE_HAS_LENGTH_CONSTRAINT 0x20 +#define JIGGLE_HAS_BASE_SPRING 0x40 + +struct mstudiojigglebone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int flags; + + // general params + float length; // how from from bone base, along bone, is tip + float tipMass; + + // flexible params + float yawStiffness; + float yawDamping; + float pitchStiffness; + float pitchDamping; + float alongStiffness; + float alongDamping; + + // angle constraint + float angleLimit; // maximum deflection of tip in radians + + // yaw constraint + float minYaw; // in radians + float maxYaw; // in radians + float yawFriction; + float yawBounce; + + // pitch constraint + float minPitch; // in radians + float maxPitch; // in radians + float pitchFriction; + float pitchBounce; + + // base spring + float baseMass; + float baseStiffness; + float baseDamping; + float baseMinLeft; + float baseMaxLeft; + float baseLeftFriction; + float baseMinUp; + float baseMaxUp; + float baseUpFriction; + float baseMinForward; + float baseMaxForward; + float baseForwardFriction; + +private: + // No copy constructors allowed + //mstudiojigglebone_t(const mstudiojigglebone_t& vOther); +}; + +struct mstudioaimatbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int parent; + int aim; // Might be bone or attach + Vector aimvector; + Vector upvector; + Vector basepos; + + mstudioaimatbone_t() {} +private: + // No copy constructors allowed + mstudioaimatbone_t(const mstudioaimatbone_t& vOther); +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +struct mstudiotwistbonetarget_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int m_nBone; + float m_flWeight; + Vector m_vBaseTranslate; + Quaternion m_qBaseRotation; + + mstudiotwistbonetarget_t() {} +private: + // No copy constructors allowed + mstudiotwistbonetarget_t( const mstudiotwistbonetarget_t &vOther ); +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +struct mstudiotwistbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + bool m_bInverse; // False: Apply child rotation to twist targets True: Apply parent rotation to twist targets + Vector m_vUpVector; // In parent space, projected into plane defined by vector between parent & child + int m_nParentBone; + Quaternion m_qBaseInv; // The base rotation of the parent, used if m_bInverse is true + int m_nChildBone; + + int m_nTargetCount; + int m_nTargetIndex; + inline mstudiotwistbonetarget_t *pTarget( int i ) const { return ( mstudiotwistbonetarget_t * )( ( ( byte * )this) + m_nTargetIndex ) + i; } + + mstudiotwistbone_t() {} +private: + // No copy constructors allowed + mstudiotwistbone_t( const mstudiotwistbone_t &vOther ); +}; + + +// bones +struct mstudiobone_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int parent; // parent bone + int bonecontroller[6]; // bone controller index, -1 == none + + // default values + Vector pos; + Quaternion quat; + RadianEuler rot; + // compression scale + Vector posscale; + Vector rotscale; + + matrix3x4_t poseToBone; + Quaternion qAlignment; + int flags; + int proctype; + int procindex; // procedural rule + mutable int physicsbone; // index into physically simulated bone + inline void *pProcedure( ) const { if (procindex == 0) return NULL; else return (void *)(((byte *)this) + procindex); }; + int surfacepropidx; // index into string tablefor property name + inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropidx; } + inline int GetSurfaceProp( void ) const { return surfacepropLookup; } + + int contents; // See BSPFlags.h for the contents flags + int surfacepropLookup; // this index must be cached by the loader, not saved in the file + int unused[7]; // remove as appropriate + + mstudiobone_t(){} +private: + // No copy constructors allowed + mstudiobone_t(const mstudiobone_t& vOther); +}; + +struct mstudiolinearbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int numbones; + + int flagsindex; + inline int flags( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + flagsindex) + i); }; + inline int *pflags( int i ) { Assert( i >= 0 && i < numbones); return ((int *)(((byte *)this) + flagsindex) + i); }; + + int parentindex; + inline int parent( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + parentindex) + i); }; + + int posindex; + inline const Vector &pos( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + posindex) + i); }; + + int quatindex; + inline const Quaternion &quat( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)(((byte *)this) + quatindex) + i); }; + + int rotindex; + inline const RadianEuler &rot( int i ) const { Assert( i >= 0 && i < numbones); return *((RadianEuler *)(((byte *)this) + rotindex) + i); }; + + int posetoboneindex; + inline const matrix3x4_t &poseToBone( int i ) const { Assert( i >= 0 && i < numbones); return *((matrix3x4_t *)(((byte *)this) + posetoboneindex) + i); }; + + int posscaleindex; + inline const Vector &posscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + posscaleindex) + i); }; + + int rotscaleindex; + inline const Vector &rotscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + rotscaleindex) + i); }; + + int qalignmentindex; + inline const Quaternion &qalignment( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)(((byte *)this) + qalignmentindex) + i); }; + + int unused[6]; + + mstudiolinearbone_t(){} +private: + // No copy constructors allowed + mstudiolinearbone_t(const mstudiolinearbone_t& vOther); +}; + + +//----------------------------------------------------------------------------- +// The component of the bone used by mstudioboneflexdriver_t +//----------------------------------------------------------------------------- +enum StudioBoneFlexComponent_t +{ + STUDIO_BONE_FLEX_INVALID = -1, // Invalid + STUDIO_BONE_FLEX_TX = 0, // Translate X + STUDIO_BONE_FLEX_TY = 1, // Translate Y + STUDIO_BONE_FLEX_TZ = 2 // Translate Z +}; + + +//----------------------------------------------------------------------------- +// Component is one of Translate X, Y or Z [0,2] (StudioBoneFlexComponent_t) +//----------------------------------------------------------------------------- +struct mstudioboneflexdrivercontrol_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int m_nBoneComponent; // Bone component that drives flex, StudioBoneFlexComponent_t + int m_nFlexControllerIndex; // Flex controller to drive + float m_flMin; // Min value of bone component mapped to 0 on flex controller + float m_flMax; // Max value of bone component mapped to 1 on flex controller + + mstudioboneflexdrivercontrol_t(){} +private: + // No copy constructors allowed + mstudioboneflexdrivercontrol_t( const mstudioboneflexdrivercontrol_t &vOther ); +}; + + +//----------------------------------------------------------------------------- +// Drive flex controllers from bone components +//----------------------------------------------------------------------------- +struct mstudioboneflexdriver_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int m_nBoneIndex; // Bone to drive flex controller + int m_nControlCount; // Number of flex controllers being driven + int m_nControlIndex; // Index into data where controllers are (relative to this) + + inline mstudioboneflexdrivercontrol_t *pBoneFlexDriverControl( int i ) const + { + Assert( i >= 0 && i < m_nControlCount ); + return (mstudioboneflexdrivercontrol_t *)(((byte *)this) + m_nControlIndex) + i; + } + + int unused[3]; + + mstudioboneflexdriver_t(){} +private: + // No copy constructors allowed + mstudioboneflexdriver_t( const mstudioboneflexdriver_t &vOther ); +}; + + +#define BONE_CALCULATE_MASK 0x1F +#define BONE_PHYSICALLY_SIMULATED 0x01 // bone is physically simulated when physics are active +#define BONE_PHYSICS_PROCEDURAL 0x02 // procedural when physics is active +#define BONE_ALWAYS_PROCEDURAL 0x04 // bone is always procedurally animated +#define BONE_SCREEN_ALIGN_SPHERE 0x08 // bone aligns to the screen, not constrained in motion. +#define BONE_SCREEN_ALIGN_CYLINDER 0x10 // bone aligns to the screen, constrained by it's own axis. + +#define BONE_USED_MASK 0x0007FF00 +#define BONE_USED_BY_ANYTHING 0x0007FF00 +#define BONE_USED_BY_HITBOX 0x00000100 // bone (or child) is used by a hit box +#define BONE_USED_BY_ATTACHMENT 0x00000200 // bone (or child) is used by an attachment point +#define BONE_USED_BY_VERTEX_MASK 0x0003FC00 +#define BONE_USED_BY_VERTEX_LOD0 0x00000400 // bone (or child) is used by the toplevel model via skinned vertex +#define BONE_USED_BY_VERTEX_LOD1 0x00000800 +#define BONE_USED_BY_VERTEX_LOD2 0x00001000 +#define BONE_USED_BY_VERTEX_LOD3 0x00002000 +#define BONE_USED_BY_VERTEX_LOD4 0x00004000 +#define BONE_USED_BY_VERTEX_LOD5 0x00008000 +#define BONE_USED_BY_VERTEX_LOD6 0x00010000 +#define BONE_USED_BY_VERTEX_LOD7 0x00020000 +#define BONE_USED_BY_BONE_MERGE 0x00040000 // bone is available for bone merge to occur against it + +#define BONE_USED_BY_VERTEX_AT_LOD(lod) ( BONE_USED_BY_VERTEX_LOD0 << (lod) ) +#define BONE_USED_BY_ANYTHING_AT_LOD(lod) ( ( BONE_USED_BY_ANYTHING & ~BONE_USED_BY_VERTEX_MASK ) | BONE_USED_BY_VERTEX_AT_LOD(lod) ) + +#define MAX_NUM_LODS 8 + +#define BONE_TYPE_MASK 0x00F00000 +#define BONE_FIXED_ALIGNMENT 0x00100000 // bone can't spin 360 degrees, all interpolation is normalized around a fixed orientation + +#define BONE_HAS_SAVEFRAME_POS 0x00200000 // Vector48 +#define BONE_HAS_SAVEFRAME_ROT64 0x00400000 // Quaternion64 +#define BONE_HAS_SAVEFRAME_ROT32 0x00800000 // Quaternion32 + +// bone controllers +struct mstudiobonecontroller_t +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; // -1 == 0 + int type; // X, Y, Z, XR, YR, ZR, M + float start; + float end; + int rest; // byte index value at rest + int inputfield; // 0-3 user set controller, 4 mouth + int unused[8]; +}; + +// intersection boxes +struct mstudiobbox_t +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + int group; // intersection group + Vector bbmin; // bounding box + Vector bbmax; + int szhitboxnameindex; // offset to the name of the hitbox. + int unused[8]; + + const char* pszHitboxName() const + { + if( szhitboxnameindex == 0 ) + return ""; + + return ((const char*)this) + szhitboxnameindex; + } + + mstudiobbox_t() {} + +private: + // No copy constructors allowed + mstudiobbox_t(const mstudiobbox_t& vOther); +}; + +// demand loaded sequence groups +struct mstudiomodelgroup_t +{ + DECLARE_BYTESWAP_DATADESC(); + int szlabelindex; // textual name + inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; } + int sznameindex; // file name + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } +}; + +struct mstudiomodelgrouplookup_t +{ + int modelgroup; + int indexwithingroup; +}; + +// events +// NOTE: If you modify this struct you MUST also modify mstudioevent_for_client_server_t in npcevent.h!!! +struct mstudioevent_t +{ + DECLARE_BYTESWAP_DATADESC(); + float cycle; + int event; + int type; + inline const char * pszOptions( void ) const { return options; } + char options[64]; + + int szeventindex; + inline char * const pszEventName( void ) const { return ((char *)this) + szeventindex; } +}; + +#define ATTACHMENT_FLAG_WORLD_ALIGN 0x10000 + +// attachment +struct mstudioattachment_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + unsigned int flags; + int localbone; + matrix3x4_t local; // attachment point + int unused[8]; +}; + +#define IK_SELF 1 +#define IK_WORLD 2 +#define IK_GROUND 3 +#define IK_RELEASE 4 +#define IK_ATTACHMENT 5 +#define IK_UNLATCH 6 + +struct mstudioikerror_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector pos; + Quaternion q; + + mstudioikerror_t() {} + +private: + // No copy constructors allowed + mstudioikerror_t(const mstudioikerror_t& vOther); +}; + +union mstudioanimvalue_t; + +struct mstudiocompressedikerror_t +{ + DECLARE_BYTESWAP_DATADESC(); + float scale[6]; + short offset[6]; + inline mstudioanimvalue_t *pAnimvalue( int i ) const { if (offset[i] > 0) return (mstudioanimvalue_t *)(((byte *)this) + offset[i]); else return NULL; }; + mstudiocompressedikerror_t(){} + +private: + // No copy constructors allowed + mstudiocompressedikerror_t(const mstudiocompressedikerror_t& vOther); +}; + +struct mstudioikrule_t +{ + DECLARE_BYTESWAP_DATADESC(); + int index; + + int type; + int chain; + + int bone; + + int slot; // iktarget slot. Usually same as chain. + float height; + float radius; + float floor; + Vector pos; + Quaternion q; + + int compressedikerrorindex; + inline mstudiocompressedikerror_t *pCompressedError() const { return (mstudiocompressedikerror_t *)(((byte *)this) + compressedikerrorindex); }; + int unused2; + + int iStart; + int ikerrorindex; + inline mstudioikerror_t *pError( int i ) const { return (ikerrorindex) ? (mstudioikerror_t *)(((byte *)this) + ikerrorindex) + (i - iStart) : NULL; }; + + float start; // beginning of influence + float peak; // start of full influence + float tail; // end of full influence + float end; // end of all influence + + float unused3; // + float contact; // frame footstep makes ground concact + float drop; // how far down the foot should drop when reaching for IK + float top; // top of the foot box + + int unused6; + int unused7; + int unused8; + + int szattachmentindex; // name of world attachment + inline char * const pszAttachment( void ) const { return ((char *)this) + szattachmentindex; } + + int unused[7]; + + mstudioikrule_t() {} + +private: + // No copy constructors allowed + mstudioikrule_t(const mstudioikrule_t& vOther); +}; + + +struct mstudioikrulezeroframe_t +{ + short chain; + short slot; + float16 start; // beginning of influence + float16 peak; // start of full influence + float16 tail; // end of full influence + float16 end; // end of all influence +}; + + +struct mstudioiklock_t +{ + DECLARE_BYTESWAP_DATADESC(); + int chain; + float flPosWeight; + float flLocalQWeight; + int flags; + + int unused[4]; +}; + + +struct mstudiolocalhierarchy_t +{ + DECLARE_BYTESWAP_DATADESC(); + int iBone; // bone being adjusted + int iNewParent; // the bones new parent + + float start; // beginning of influence + float peak; // start of full influence + float tail; // end of full influence + float end; // end of all influence + + int iStart; // first frame + + int localanimindex; + inline mstudiocompressedikerror_t *pLocalAnim() const { return (mstudiocompressedikerror_t *)(((byte *)this) + localanimindex); }; + + int unused[4]; +}; + + + +// animation frames +union mstudioanimvalue_t +{ + struct + { + byte valid; + byte total; + } num; + short value; +}; + +struct mstudioanim_valueptr_t +{ + DECLARE_BYTESWAP_DATADESC(); + short offset[3]; + inline mstudioanimvalue_t *pAnimvalue( int i ) const { if (offset[i] > 0) return (mstudioanimvalue_t *)(((byte *)this) + offset[i]); else return NULL; }; +}; + +#define STUDIO_ANIM_RAWPOS 0x01 // Vector48 +#define STUDIO_ANIM_RAWROT 0x02 // Quaternion48 +#define STUDIO_ANIM_ANIMPOS 0x04 // mstudioanim_valueptr_t +#define STUDIO_ANIM_ANIMROT 0x08 // mstudioanim_valueptr_t +#define STUDIO_ANIM_DELTA 0x10 +#define STUDIO_ANIM_RAWROT2 0x20 // Quaternion64 + +// per bone per animation DOF and weight pointers, RLE encoded +struct mstudio_rle_anim_t +{ + DECLARE_BYTESWAP_DATADESC(); + byte bone; + byte flags; // weighing options + + // valid for animating data only + inline byte *pData( void ) const { return (((byte *)this) + sizeof( struct mstudio_rle_anim_t )); }; + inline mstudioanim_valueptr_t *pRotV( void ) const { return (mstudioanim_valueptr_t *)(pData()); }; + inline mstudioanim_valueptr_t *pPosV( void ) const { return (mstudioanim_valueptr_t *)(pData()) + ((flags & STUDIO_ANIM_ANIMROT) != 0); }; + + // valid if animation unvaring over timeline + inline Quaternion48 *pQuat48( void ) const { return (Quaternion48 *)(pData()); }; + inline Quaternion64 *pQuat64( void ) const { return (Quaternion64 *)(pData()); }; + inline Vector48 *pPos( void ) const { return (Vector48 *)(pData() + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( *pQuat48() ) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof( *pQuat64() ) ); }; + + // points to next bone in the list + short nextoffset; + inline mstudio_rle_anim_t *pNext( void ) const { if (nextoffset != 0) return (mstudio_rle_anim_t *)(((byte *)this) + nextoffset); else return NULL; }; +}; + + +#define STUDIO_FRAME_RAWPOS 0x01 // Vector48 in constants +#define STUDIO_FRAME_RAWROT 0x02 // Quaternion48 in constants +#define STUDIO_FRAME_ANIMPOS 0x04 // Vector48 in framedata +#define STUDIO_FRAME_ANIMROT 0x08 // Quaternion48 in framedata +#define STUDIO_FRAME_FULLANIMPOS 0x10 // Vector in framedata + + +struct mstudio_frame_anim_t +{ + DECLARE_BYTESWAP_DATADESC(); + + inline byte *pBoneFlags( void ) const { return (((byte *)this) + sizeof( struct mstudio_frame_anim_t )); }; + + int constantsoffset; + inline byte *pConstantData( void ) const { return (((byte *)this) + constantsoffset); }; + + int frameoffset; + int framelength; + inline byte *pFrameData( int iFrame ) const { return (((byte *)this) + frameoffset + iFrame * framelength); }; + + int unused[3]; +}; + + + +struct mstudiomovement_t +{ + DECLARE_BYTESWAP_DATADESC(); + int endframe; + int motionflags; + float v0; // velocity at start of block + float v1; // velocity at end of block + float angle; // YAW rotation at end of this blocks movement + Vector vector; // movement vector relative to this blocks initial angle + Vector position; // relative to start of animation??? + + mstudiomovement_t(){} +private: + // No copy constructors allowed + mstudiomovement_t(const mstudiomovement_t& vOther); +}; + +// used for piecewise loading of animation data +struct mstudioanimblock_t +{ + DECLARE_BYTESWAP_DATADESC(); + int datastart; + int dataend; +}; + +struct mstudioanimsections_t +{ + DECLARE_BYTESWAP_DATADESC(); + int animblock; + int animindex; +}; + +struct mstudioanimdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + int baseptr; + inline studiohdr_t *pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + baseptr); } + + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + + float fps; // frames per second + int flags; // looping/non-looping flags + + int numframes; + + // piecewise movement + int nummovements; + int movementindex; + inline mstudiomovement_t * const pMovement( int i ) const { return (mstudiomovement_t *)(((byte *)this) + movementindex) + i; }; + + int ikrulezeroframeindex; + mstudioikrulezeroframe_t *pIKRuleZeroFrame( int i ) const { if (ikrulezeroframeindex) return (mstudioikrulezeroframe_t *)(((byte *)this) + ikrulezeroframeindex) + i; else return NULL; }; + + int unused1[5]; // remove as appropriate (and zero if loading older versions) + + int animblock; + int animindex; // non-zero when anim data isn't in sections + byte *pAnimBlock( int block, int index ) const; // returns pointer to a specific anim block (local or external) + byte *pAnim( int *piFrame, float &flStall ) const; // returns pointer to data and new frame index + byte *pAnim( int *piFrame ) const; // returns pointer to data and new frame index + + int numikrules; + int ikruleindex; // non-zero when IK rule is stored in the mdl + int animblockikruleindex; // non-zero when IK data is stored in animblock file + mstudioikrule_t *pIKRule( int i ) const; + + int numlocalhierarchy; + int localhierarchyindex; + mstudiolocalhierarchy_t *pHierarchy( int i ) const; + + int sectionindex; + int sectionframes; // number of frames used in each fast lookup section, zero if not used + inline mstudioanimsections_t * const pSection( int i ) const { return (mstudioanimsections_t *)(((byte *)this) + sectionindex) + i; } + + short zeroframespan; // frames per span + short zeroframecount; // number of spans + int zeroframeindex; + byte *pZeroFrameData( ) const { if (zeroframeindex) return (((byte *)this) + zeroframeindex); else return NULL; }; + mutable float zeroframestalltime; // saved during read stalls + + mstudioanimdesc_t(){} +private: + // No copy constructors allowed + mstudioanimdesc_t(const mstudioanimdesc_t& vOther); +}; + +struct mstudioikrule_t; + +struct mstudioautolayer_t +{ + DECLARE_BYTESWAP_DATADESC(); +//private: + short iSequence; + short iPose; +//public: + int flags; + float start; // beginning of influence + float peak; // start of full influence + float tail; // end of full influence + float end; // end of all influence +}; + +struct mstudioactivitymodifier_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int sznameindex; + inline char *pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; } +}; + +// sequence descriptions +struct mstudioseqdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + int baseptr; + inline studiohdr_t *pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + baseptr); } + + int szlabelindex; + inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; } + + int szactivitynameindex; + inline char * const pszActivityName( void ) const { return ((char *)this) + szactivitynameindex; } + + int flags; // looping/non-looping flags + + int activity; // initialized at loadtime to game DLL values + int actweight; + + int numevents; + int eventindex; + inline mstudioevent_t *pEvent( int i ) const { Assert( i >= 0 && i < numevents); return (mstudioevent_t *)(((byte *)this) + eventindex) + i; }; + + Vector bbmin; // per sequence bounding box + Vector bbmax; + + int numblends; + + // Index into array of shorts which is groupsize[0] x groupsize[1] in length + int animindexindex; + + inline int anim( int x, int y ) const + { + if ( x >= groupsize[0] ) + { + x = groupsize[0] - 1; + } + + if ( y >= groupsize[1] ) + { + y = groupsize[ 1 ] - 1; + } + + int offset = y * groupsize[0] + x; + short *blends = (short *)(((byte *)this) + animindexindex); + int value = (int)blends[ offset ]; + return value; + } + + int movementindex; // [blend] float array for blended movement + int groupsize[2]; + int paramindex[2]; // X, Y, Z, XR, YR, ZR + float paramstart[2]; // local (0..1) starting value + float paramend[2]; // local (0..1) ending value + int paramparent; + + float fadeintime; // ideal cross fate in time (0.2 default) + float fadeouttime; // ideal cross fade out time (0.2 default) + + int localentrynode; // transition node at entry + int localexitnode; // transition node at exit + int nodeflags; // transition rules + + float entryphase; // used to match entry gait + float exitphase; // used to match exit gait + + float lastframe; // frame that should generation EndOfSequence + + int nextseq; // auto advancing sequences + int pose; // index of delta animation between end and nextseq + + int numikrules; + + int numautolayers; // + int autolayerindex; + inline mstudioautolayer_t *pAutolayer( int i ) const { Assert( i >= 0 && i < numautolayers); return (mstudioautolayer_t *)(((byte *)this) + autolayerindex) + i; }; + + int weightlistindex; + inline float *pBoneweight( int i ) const { return ((float *)(((byte *)this) + weightlistindex) + i); }; + inline float weight( int i ) const { return *(pBoneweight( i)); }; + + // FIXME: make this 2D instead of 2x1D arrays + int posekeyindex; + float *pPoseKey( int iParam, int iAnim ) const { return (float *)(((byte *)this) + posekeyindex) + iParam * groupsize[0] + iAnim; } + float poseKey( int iParam, int iAnim ) const { return *(pPoseKey( iParam, iAnim )); } + + int numiklocks; + int iklockindex; + inline mstudioiklock_t *pIKLock( int i ) const { Assert( i >= 0 && i < numiklocks); return (mstudioiklock_t *)(((byte *)this) + iklockindex) + i; }; + + // Key values + int keyvalueindex; + int keyvaluesize; + inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; } + + int cycleposeindex; // index of pose parameter to use as cycle index + + int activitymodifierindex; + int numactivitymodifiers; + inline mstudioactivitymodifier_t *pActivityModifier( int i ) const { Assert( i >= 0 && i < numactivitymodifiers); return activitymodifierindex != 0 ? (mstudioactivitymodifier_t *)(((byte *)this) + activitymodifierindex) + i : NULL; }; + + int unused[5]; // remove/add as appropriate (grow back to 8 ints on version change!) + + mstudioseqdesc_t(){} +private: + // No copy constructors allowed + mstudioseqdesc_t(const mstudioseqdesc_t& vOther); +}; + + +struct mstudioposeparamdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int flags; // ???? + float start; // starting value + float end; // ending value + float loop; // looping range, 0 for no looping, 360 for rotations, etc. +}; + +struct mstudioflexdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + int szFACSindex; + inline char * const pszFACS( void ) const { return ((char *)this) + szFACSindex; } +}; + + + +struct mstudioflexcontroller_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sztypeindex; + inline char * const pszType( void ) const { return ((char *)this) + sztypeindex; } + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + mutable int localToGlobal; // remapped at load time to master list + float min; + float max; +}; + + +enum FlexControllerRemapType_t +{ + FLEXCONTROLLER_REMAP_PASSTHRU = 0, + FLEXCONTROLLER_REMAP_2WAY, // Control 0 -> ramps from 1-0 from 0->0.5. Control 1 -> ramps from 0-1 from 0.5->1 + FLEXCONTROLLER_REMAP_NWAY, // StepSize = 1 / (control count-1) Control n -> ramps from 0-1-0 from (n-1)*StepSize to n*StepSize to (n+1)*StepSize. A second control is needed to specify amount to use + FLEXCONTROLLER_REMAP_EYELID +}; + + +class CStudioHdr; +struct mstudioflexcontrollerui_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + + // These are used like a union to save space + // Here are the possible configurations for a UI controller + // + // SIMPLE NON-STEREO: 0: control 1: unused 2: unused + // STEREO: 0: left 1: right 2: unused + // NWAY NON-STEREO: 0: control 1: unused 2: value + // NWAY STEREO: 0: left 1: right 2: value + + int szindex0; + int szindex1; + int szindex2; + + inline const mstudioflexcontroller_t *pController( void ) const + { + return !stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL; + } + inline char * const pszControllerName( void ) const { return !stereo ? pController()->pszName() : NULL; } + inline int controllerIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pLeftController( void ) const + { + return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL; + } + inline char * const pszLeftName( void ) const { return stereo ? pLeftController()->pszName() : NULL; } + inline int leftIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pRightController( void ) const + { + return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex1 ): NULL; + } + inline char * const pszRightName( void ) const { return stereo ? pRightController()->pszName() : NULL; } + inline int rightIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pNWayValueController( void ) const + { + return remaptype == FLEXCONTROLLER_REMAP_NWAY ? (mstudioflexcontroller_t *)( (char *)this + szindex2 ) : NULL; + } + inline char * const pszNWayValueName( void ) const { return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController()->pszName() : NULL; } + inline int nWayValueIndex( const CStudioHdr &cStudioHdr ) const; + + // Number of controllers this ui description contains, 1, 2 or 3 + inline int Count() const { return ( stereo ? 2 : 1 ) + ( remaptype == FLEXCONTROLLER_REMAP_NWAY ? 1 : 0 ); } + inline const mstudioflexcontroller_t *pController( int index ) const; + + unsigned char remaptype; // See the FlexControllerRemapType_t enum + bool stereo; // Is this a stereo control? + byte unused[2]; +}; + + +// this is the memory image of vertex anims (16-bit fixed point) +struct mstudiovertanim_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short index; + byte speed; // 255/max_length_in_flex + byte side; // 255/left_right + +protected: + union + { + short delta[3]; + float16 flDelta[3]; + }; + + union + { + short ndelta[3]; + float16 flNDelta[3]; + }; + +public: + inline void ConvertToFixed( float flVertAnimFixedPointScale ) + { + delta[0] = flDelta[0].GetFloat() / flVertAnimFixedPointScale; + delta[1] = flDelta[1].GetFloat() / flVertAnimFixedPointScale; + delta[2] = flDelta[2].GetFloat() / flVertAnimFixedPointScale; + ndelta[0] = flNDelta[0].GetFloat() / flVertAnimFixedPointScale; + ndelta[1] = flNDelta[1].GetFloat() / flVertAnimFixedPointScale; + ndelta[2] = flNDelta[2].GetFloat() / flVertAnimFixedPointScale; + } + + inline Vector GetDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale ); + } + inline Vector GetNDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale ); + } + inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, 0.0f ); + } + inline void GetNDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale, 0.0f ); + } + inline Vector GetDeltaFloat() + { + return Vector (flDelta[0].GetFloat(), flDelta[1].GetFloat(), flDelta[2].GetFloat()); + } + inline Vector GetNDeltaFloat() + { + return Vector (flNDelta[0].GetFloat(), flNDelta[1].GetFloat(), flNDelta[2].GetFloat()); + } + inline void SetDeltaFixed( const Vector& vInput, float flVertAnimFixedPointScale ) + { + delta[0] = vInput.x / flVertAnimFixedPointScale; + delta[1] = vInput.y / flVertAnimFixedPointScale; + delta[2] = vInput.z / flVertAnimFixedPointScale; + } + inline void SetNDeltaFixed( const Vector& vInputNormal, float flVertAnimFixedPointScale ) + { + ndelta[0] = vInputNormal.x / flVertAnimFixedPointScale; + ndelta[1] = vInputNormal.y / flVertAnimFixedPointScale; + ndelta[2] = vInputNormal.z / flVertAnimFixedPointScale; + } + + // Ick...can also force fp16 data into this structure for writing to file in legacy format... + inline void SetDeltaFloat( const Vector& vInput ) + { + flDelta[0].SetFloat( vInput.x ); + flDelta[1].SetFloat( vInput.y ); + flDelta[2].SetFloat( vInput.z ); + } + inline void SetNDeltaFloat( const Vector& vInputNormal ) + { + flNDelta[0].SetFloat( vInputNormal.x ); + flNDelta[1].SetFloat( vInputNormal.y ); + flNDelta[2].SetFloat( vInputNormal.z ); + } + + class CSortByIndex + { + public: + bool operator()(const mstudiovertanim_t &left, const mstudiovertanim_t & right)const + { + return left.index < right.index; + } + }; + friend class CSortByIndex; + + mstudiovertanim_t(){} +//private: +// No copy constructors allowed, but it's needed for std::sort() +// mstudiovertanim_t(const mstudiovertanim_t& vOther); +}; + + +// this is the memory image of vertex anims (16-bit fixed point) +struct mstudiovertanim_wrinkle_t : public mstudiovertanim_t +{ + DECLARE_BYTESWAP_DATADESC(); + + short wrinkledelta; + + inline void SetWrinkleFixed( float flWrinkle, float flVertAnimFixedPointScale ) + { + int nWrinkleDeltaInt = flWrinkle / flVertAnimFixedPointScale; + wrinkledelta = clamp( nWrinkleDeltaInt, -32767, 32767 ); + } + + inline Vector4D GetDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector4D( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale ); + } + + inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale ); + } + + inline float GetWrinkleDeltaFixed( float flVertAnimFixedPointScale ) + { + return wrinkledelta * flVertAnimFixedPointScale; + } +}; + + +enum StudioVertAnimType_t +{ + STUDIO_VERT_ANIM_NORMAL = 0, + STUDIO_VERT_ANIM_WRINKLE, +}; + + +struct mstudioflex_t +{ + DECLARE_BYTESWAP_DATADESC(); + int flexdesc; // input value + + float target0; // zero + float target1; // one + float target2; // one + float target3; // zero + + int numverts; + int vertindex; + + inline mstudiovertanim_t *pVertanim( int i ) const { Assert( vertanimtype == STUDIO_VERT_ANIM_NORMAL ); return (mstudiovertanim_t *)(((byte *)this) + vertindex) + i; }; + inline mstudiovertanim_wrinkle_t *pVertanimWrinkle( int i ) const { Assert( vertanimtype == STUDIO_VERT_ANIM_WRINKLE ); return (mstudiovertanim_wrinkle_t *)(((byte *)this) + vertindex) + i; }; + + inline byte *pBaseVertanim( ) const { return ((byte *)this) + vertindex; }; + inline int VertAnimSizeBytes() const { return ( vertanimtype == STUDIO_VERT_ANIM_NORMAL ) ? sizeof(mstudiovertanim_t) : sizeof(mstudiovertanim_wrinkle_t); } + + int flexpair; // second flex desc + unsigned char vertanimtype; // See StudioVertAnimType_t + unsigned char unusedchar[3]; + int unused[6]; + +}; + + +struct mstudioflexop_t +{ + DECLARE_BYTESWAP_DATADESC(); + int op; + union + { + int index; + float value; + } d; +}; + +struct mstudioflexrule_t +{ + DECLARE_BYTESWAP_DATADESC(); + int flex; + int numops; + int opindex; + inline mstudioflexop_t *iFlexOp( int i ) const { return (mstudioflexop_t *)(((byte *)this) + opindex) + i; }; +}; + +// 16 bytes +struct mstudioboneweight_t +{ + DECLARE_BYTESWAP_DATADESC(); + float weight[MAX_NUM_BONES_PER_VERT]; + char bone[MAX_NUM_BONES_PER_VERT]; + byte numbones; + +// byte material; +// short firstref; +// short lastref; +}; + +// NOTE: This is exactly 48 bytes +struct mstudiovertex_t +{ + DECLARE_BYTESWAP_DATADESC(); + mstudioboneweight_t m_BoneWeights; + Vector m_vecPosition; + Vector m_vecNormal; + Vector2D m_vecTexCoord; + + mstudiovertex_t() {} + +private: + // No copy constructors allowed + mstudiovertex_t(const mstudiovertex_t& vOther); +}; + +// skin info +struct mstudiotexture_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int flags; + int used; + int unused1; + mutable IMaterial *material; // fixme: this needs to go away . .isn't used by the engine, but is used by studiomdl + mutable void *clientmaterial; // gary, replace with client material pointer if used + + int unused[10]; +}; + +// eyeball +struct mstudioeyeball_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int bone; + Vector org; + float zoffset; + float radius; + Vector up; + Vector forward; + int texture; + + int unused1; + float iris_scale; + int unused2; + + int upperflexdesc[3]; // index of raiser, neutral, and lowerer flexdesc that is set by flex controllers + int lowerflexdesc[3]; + float uppertarget[3]; // angle (radians) of raised, neutral, and lowered lid positions + float lowertarget[3]; + + int upperlidflexdesc; // index of flex desc that actual lid flexes look to + int lowerlidflexdesc; + int unused[4]; // These were used before, so not guaranteed to be 0 + bool m_bNonFACS; // Never used before version 44 + char unused3[3]; + int unused4[7]; + + mstudioeyeball_t(){} +private: + // No copy constructors allowed + mstudioeyeball_t(const mstudioeyeball_t& vOther); +}; + + +// ikinfo +struct mstudioiklink_t +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + Vector kneeDir; // ideal bending direction (per link, if applicable) + Vector unused0; // unused + + mstudioiklink_t(){} +private: + // No copy constructors allowed + mstudioiklink_t(const mstudioiklink_t& vOther); +}; + +struct mstudioikchain_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int linktype; + int numlinks; + int linkindex; + inline mstudioiklink_t *pLink( int i ) const { return (mstudioiklink_t *)(((byte *)this) + linkindex) + i; }; + // FIXME: add unused entries +}; + +struct mstudioiface_t +{ + mstudioiface_t() + { + a = b = c = d = 0xFFFF; + } + + unsigned short a, b, c, d; // Indices to vertices (If d is 0xFFFF, this is a triangle, else it's a quad) +}; + +struct mstudiomodel_t; + +struct mstudio_modelvertexdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector *Position( int i ) const; + Vector *Normal( int i ) const; + Vector4D *TangentS( int i ) const; + Vector2D *Texcoord( int i ) const; + mstudioboneweight_t *BoneWeights( int i ) const; + mstudiovertex_t *Vertex( int i ) const; + bool HasTangentData( void ) const; + int GetGlobalVertexIndex( int i ) const; + int GetGlobalTangentIndex( int i ) const; + + // base of external vertex data stores + const void *pVertexData; + const void *pTangentData; +}; + +struct mstudio_meshvertexdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + Vector *Position( int i ) const; + Vector *Normal( int i ) const; + Vector4D *TangentS( int i ) const; + Vector2D *Texcoord( int i ) const; + mstudioboneweight_t *BoneWeights( int i ) const; + mstudiovertex_t *Vertex( int i ) const; + bool HasTangentData( void ) const; + int GetModelVertexIndex( int i ) const; + int GetGlobalVertexIndex( int i ) const; + + // indirection to this mesh's model's vertex data + const mstudio_modelvertexdata_t *modelvertexdata; + + // used for fixup calcs when culling top level lods + // expected number of mesh verts at desired lod + int numLODVertexes[MAX_NUM_LODS]; +}; + +struct mstudiomesh_t +{ + DECLARE_BYTESWAP_DATADESC(); + int material; + + int modelindex; + mstudiomodel_t *pModel() const; + + int numvertices; // number of unique vertices/normals/texcoords + int vertexoffset; // vertex mstudiovertex_t + + // Access thin/fat mesh vertex data (only one will return a non-NULL result) + const mstudio_meshvertexdata_t *GetVertexData( void *pModelData = NULL ); + const thinModelVertices_t *GetThinVertexData( void *pModelData = NULL ); + + int numflexes; // vertex animation + int flexindex; + inline mstudioflex_t *pFlex( int i ) const { return (mstudioflex_t *)(((byte *)this) + flexindex) + i; }; + + // special codes for material operations + int materialtype; + int materialparam; + + // a unique ordinal for this mesh + int meshid; + + Vector center; + + mstudio_meshvertexdata_t vertexdata; + + int unused[8]; // remove as appropriate + + mstudiomesh_t(){} +private: + // No copy constructors allowed + mstudiomesh_t(const mstudiomesh_t& vOther); +}; + +// studio models +struct mstudiomodel_t +{ + DECLARE_BYTESWAP_DATADESC(); + inline const char * pszName( void ) const { return name; } + char name[64]; + + int type; + + float boundingradius; + + int nummeshes; + int meshindex; + inline mstudiomesh_t *pMesh( int i ) const { return (mstudiomesh_t *)(((byte *)this) + meshindex) + i; }; + + // cache purposes + int numvertices; // number of unique vertices/normals/texcoords + int vertexindex; // vertex Vector + int tangentsindex; // tangents Vector + + // These functions are defined in application-specific code: + const vertexFileHeader_t *CacheVertexData( void *pModelData ); + + // Access thin/fat mesh vertex data (only one will return a non-NULL result) + const mstudio_modelvertexdata_t *GetVertexData( void *pModelData = NULL ); + const thinModelVertices_t *GetThinVertexData( void *pModelData = NULL ); + + int numattachments; + int attachmentindex; + + int numeyeballs; + int eyeballindex; + inline mstudioeyeball_t *pEyeball( int i ) { return (mstudioeyeball_t *)(((byte *)this) + eyeballindex) + i; }; + + mstudio_modelvertexdata_t vertexdata; + + int unused[8]; // remove as appropriate +}; + +inline bool mstudio_modelvertexdata_t::HasTangentData( void ) const +{ + return (pTangentData != NULL); +} + +inline int mstudio_modelvertexdata_t::GetGlobalVertexIndex( int i ) const +{ + mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata)); + Assert( ( modelptr->vertexindex % sizeof( mstudiovertex_t ) ) == 0 ); + return ( i + ( modelptr->vertexindex / sizeof( mstudiovertex_t ) ) ); +} + +inline int mstudio_modelvertexdata_t::GetGlobalTangentIndex( int i ) const +{ + mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata)); + Assert( ( modelptr->tangentsindex % sizeof( Vector4D ) ) == 0 ); + return ( i + ( modelptr->tangentsindex / sizeof( Vector4D ) ) ); +} + +inline mstudiovertex_t *mstudio_modelvertexdata_t::Vertex( int i ) const +{ + return (mstudiovertex_t *)pVertexData + GetGlobalVertexIndex( i ); +} + +inline Vector *mstudio_modelvertexdata_t::Position( int i ) const +{ + return &Vertex(i)->m_vecPosition; +} + +inline Vector *mstudio_modelvertexdata_t::Normal( int i ) const +{ + return &Vertex(i)->m_vecNormal; +} + +inline Vector4D *mstudio_modelvertexdata_t::TangentS( int i ) const +{ + // NOTE: The tangents vector is 16-bytes in a separate array + // because it only exists on the high end, and if I leave it out + // of the mstudiovertex_t, the vertex is 64-bytes (good for low end) + return (Vector4D *)pTangentData + GetGlobalTangentIndex( i ); +} + +inline Vector2D *mstudio_modelvertexdata_t::Texcoord( int i ) const +{ + return &Vertex(i)->m_vecTexCoord; +} + +inline mstudioboneweight_t *mstudio_modelvertexdata_t::BoneWeights( int i ) const +{ + return &Vertex(i)->m_BoneWeights; +} + +inline mstudiomodel_t *mstudiomesh_t::pModel() const +{ + return (mstudiomodel_t *)(((byte *)this) + modelindex); +} + +inline bool mstudio_meshvertexdata_t::HasTangentData( void ) const +{ + return modelvertexdata->HasTangentData(); +} + +inline const mstudio_meshvertexdata_t *mstudiomesh_t::GetVertexData( void *pModelData ) +{ + // get this mesh's model's vertex data (allow for mstudiomodel_t::GetVertexData + // returning NULL if the data has been converted to 'thin' vertices) + this->pModel()->GetVertexData( pModelData ); + vertexdata.modelvertexdata = &( this->pModel()->vertexdata ); + + if ( !vertexdata.modelvertexdata->pVertexData ) + return NULL; + + return &vertexdata; +} + +inline const thinModelVertices_t * mstudiomesh_t::GetThinVertexData( void *pModelData ) +{ + // get this mesh's model's thin vertex data + return this->pModel()->GetThinVertexData( pModelData ); +} + +inline int mstudio_meshvertexdata_t::GetModelVertexIndex( int i ) const +{ + mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); + return meshptr->vertexoffset + i; +} + +inline int mstudio_meshvertexdata_t::GetGlobalVertexIndex( int i ) const +{ + return modelvertexdata->GetGlobalVertexIndex( GetModelVertexIndex( i ) ); +} + +inline Vector *mstudio_meshvertexdata_t::Position( int i ) const +{ + return modelvertexdata->Position( GetModelVertexIndex( i ) ); +}; + +inline Vector *mstudio_meshvertexdata_t::Normal( int i ) const +{ + return modelvertexdata->Normal( GetModelVertexIndex( i ) ); +}; + +inline Vector4D *mstudio_meshvertexdata_t::TangentS( int i ) const +{ + return modelvertexdata->TangentS( GetModelVertexIndex( i ) ); +} + +inline Vector2D *mstudio_meshvertexdata_t::Texcoord( int i ) const +{ + return modelvertexdata->Texcoord( GetModelVertexIndex( i ) ); +}; + +inline mstudioboneweight_t *mstudio_meshvertexdata_t::BoneWeights( int i ) const +{ + return modelvertexdata->BoneWeights( GetModelVertexIndex( i ) ); +}; + +inline mstudiovertex_t *mstudio_meshvertexdata_t::Vertex( int i ) const +{ + return modelvertexdata->Vertex( GetModelVertexIndex( i ) ); +} + +// a group of studio model data +enum studiomeshgroupflags_t +{ + MESHGROUP_IS_HWSKINNED = 0x2, + MESHGROUP_IS_DELTA_FLEXED = 0x4 +}; + + +// ---------------------------------------------------------- +// Runtime stuff +// ---------------------------------------------------------- + +struct studiomeshgroup_t +{ + IMesh *m_pMesh; + int m_NumStrips; + int m_Flags; // see studiomeshgroupflags_t + OptimizedModel::StripHeader_t *m_pStripData; + unsigned short *m_pGroupIndexToMeshIndex; + int m_NumVertices; + int *m_pUniqueFaces; // for performance measurements + unsigned short *m_pIndices; + unsigned short *m_pTopologyIndices; + bool m_MeshNeedsRestore; + short m_ColorMeshID; + IMorph *m_pMorph; + + inline unsigned short MeshIndex( int i ) const { return m_pGroupIndexToMeshIndex[m_pIndices[i]]; } +}; + + +// studio model data +struct studiomeshdata_t +{ + int m_NumGroup; + studiomeshgroup_t* m_pMeshGroup; +}; + +struct studioloddata_t +{ + // not needed - this is really the same as studiohwdata_t.m_NumStudioMeshes + //int m_NumMeshes; + studiomeshdata_t *m_pMeshData; // there are studiohwdata_t.m_NumStudioMeshes of these. + float m_SwitchPoint; + // one of these for each lod since we can switch to simpler materials on lower lods. + int numMaterials; + IMaterial **ppMaterials; /* will have studiohdr_t.numtextures elements allocated */ + // hack - this needs to go away. + int *pMaterialFlags; /* will have studiohdr_t.numtextures elements allocated */ + + // For decals on hardware morphing, we must actually do hardware skinning + // For this to work, we have to hope that the total # of bones used by + // hw flexed verts is < than the max possible for the dx level we're running under + int *m_pHWMorphDecalBoneRemap; + int m_nDecalBoneCount; +}; + +struct studiohwdata_t +{ + int m_RootLOD; // calced and clamped, nonzero for lod culling + int m_NumLODs; + studioloddata_t *m_pLODs; + int m_NumStudioMeshes; + + inline float LODMetric( float unitSphereSize ) const { return ( unitSphereSize != 0.0f ) ? (100.0f / unitSphereSize) : 0.0f; } + inline int GetLODForMetric( float lodMetric ) const + { + if ( !m_NumLODs ) + return 0; + + // shadow lod is specified on the last lod with a negative switch + // never consider shadow lod as viable candidate + int numLODs = (m_pLODs[m_NumLODs-1].m_SwitchPoint < 0.0f) ? m_NumLODs-1 : m_NumLODs; + + for ( int i = m_RootLOD; i < numLODs-1; i++ ) + { + if ( m_pLODs[i+1].m_SwitchPoint > lodMetric ) + return i; + } + + return numLODs-1; + } +}; + +// ---------------------------------------------------------- +// ---------------------------------------------------------- + +// body part index +struct mstudiobodyparts_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int nummodels; + int base; + int modelindex; // index into models array + inline mstudiomodel_t *pModel( int i ) const { return (mstudiomodel_t *)(((byte *)this) + modelindex) + i; }; +}; + + +struct mstudiomouth_t +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + Vector forward; + int flexdesc; + + mstudiomouth_t(){} +private: + // No copy constructors allowed + mstudiomouth_t(const mstudiomouth_t& vOther); +}; + +struct mstudiohitboxset_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + int numhitboxes; + int hitboxindex; + inline mstudiobbox_t *pHitbox( int i ) const { return (mstudiobbox_t *)(((byte *)this) + hitboxindex) + i; }; +}; + + +//----------------------------------------------------------------------------- +// Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations +// NOTE: The operation you should apply is: pretransform * bone transform * posttransform +//----------------------------------------------------------------------------- +struct mstudiosrcbonetransform_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int sznameindex; + inline const char *pszName( void ) const { return ((char *)this) + sznameindex; } + matrix3x4_t pretransform; + matrix3x4_t posttransform; +}; + + +// ---------------------------------------------------------- +// Purpose: Load time results on model compositing +// ---------------------------------------------------------- + +class virtualgroup_t +{ +public: + virtualgroup_t( void ) { cache = NULL; }; + // tool dependant. In engine this is a model_t, in tool it's a direct pointer + void *cache; + // converts cache entry into a usable studiohdr_t * + const studiohdr_t *GetStudioHdr( void ) const; + + CUtlVector< int > boneMap; // maps global bone to local bone + CUtlVector< int > masterBone; // maps local bone to global bone + CUtlVector< int > masterSeq; // maps local sequence to master sequence + CUtlVector< int > masterAnim; // maps local animation to master animation + CUtlVector< int > masterAttachment; // maps local attachment to global + CUtlVector< int > masterPose; // maps local pose parameter to global + CUtlVector< int > masterNode; // maps local transition nodes to global +}; + +struct virtualsequence_t +{ +#ifdef _XBOX + short flags; + short activity; + short group; + short index; +#else + int flags; + int activity; + int group; + int index; +#endif +}; + +struct virtualgeneric_t +{ +#ifdef _XBOX + short group; + short index; +#else + int group; + int index; +#endif +}; + + +struct virtualmodel_t +{ + void AppendSequences( int group, const studiohdr_t *pStudioHdr ); + void AppendAnimations( int group, const studiohdr_t *pStudioHdr ); + void AppendAttachments( int ground, const studiohdr_t *pStudioHdr ); + void AppendPoseParameters( int group, const studiohdr_t *pStudioHdr ); + void AppendBonemap( int group, const studiohdr_t *pStudioHdr ); + void AppendNodes( int group, const studiohdr_t *pStudioHdr ); + void AppendTransitions( int group, const studiohdr_t *pStudioHdr ); + void AppendIKLocks( int group, const studiohdr_t *pStudioHdr ); + void AppendModels( int group, const studiohdr_t *pStudioHdr ); + void UpdateAutoplaySequences( const studiohdr_t *pStudioHdr ); + + virtualgroup_t *pAnimGroup( int animation ) { return &m_group[ m_anim[ animation ].group ]; }; // Note: user must manage mutex for this + virtualgroup_t *pSeqGroup( int sequence ) { return &m_group[ m_seq[ sequence ].group ]; }; // Note: user must manage mutex for this + + CThreadFastMutex m_Lock; + + CUtlVector< virtualsequence_t > m_seq; + CUtlVector< virtualgeneric_t > m_anim; + CUtlVector< virtualgeneric_t > m_attachment; + CUtlVector< virtualgeneric_t > m_pose; + CUtlVector< virtualgroup_t > m_group; + CUtlVector< virtualgeneric_t > m_node; + CUtlVector< virtualgeneric_t > m_iklock; + CUtlVector< unsigned short > m_autoplaySequences; +}; + +// 'thin' vertex data, used to do model decals (see Studio_CreateThinVertexes()) +struct thinModelVertices_t +{ + void Init( int numBoneInfluences, Vector *positions, unsigned short *normals, float *boneWeights, char *boneIndices ) + { + Assert( positions != NULL ); + Assert( normals != NULL ); + Assert( ( numBoneInfluences >= 0 ) && ( numBoneInfluences <= 3 ) ); + Assert( numBoneInfluences > 0 ? !!boneIndices : !boneIndices ); + Assert( numBoneInfluences > 1 ? !!boneWeights : !boneWeights ); + m_numBoneInfluences = numBoneInfluences; + m_vecPositions = positions; + m_vecNormals = normals; + m_boneWeights = boneWeights; + m_boneIndices = boneIndices; + } + + void SetPosition( int vertIndex, const Vector & position ) + { + Assert( m_vecPositions ); + m_vecPositions[ vertIndex ] = position; + } + + void SetNormal( int vertIndex, const Vector & normal ) + { + Assert( m_vecNormals ); + unsigned int packedNormal; + PackNormal_UBYTE4( normal.x, normal.y, normal.z, &packedNormal ); + m_vecNormals[ vertIndex ] = (unsigned short)( 0x0000FFFF & packedNormal ); + } + + void SetBoneWeights( int vertIndex, const mstudioboneweight_t & boneWeights ) + { + Assert( ( m_numBoneInfluences >= 1 ) && ( m_numBoneInfluences <= 3 ) ); + Assert( ( boneWeights.numbones >= 1 ) && ( boneWeights.numbones <= m_numBoneInfluences ) ); + int numStoredWeights = MAX( 0, ( m_numBoneInfluences - 1 ) ); + float *pBaseWeight = m_boneWeights + vertIndex*numStoredWeights; + char *pBaseIndex = m_boneIndices + vertIndex*m_numBoneInfluences; + for ( int i = 0; i < m_numBoneInfluences; i++ ) + { + pBaseIndex[i] = boneWeights.bone[i]; + } + for ( int i = 0; i < numStoredWeights; i++ ) + { + pBaseWeight[i] = boneWeights.weight[i]; + } + } + + void GetMeshPosition( mstudiomesh_t *pMesh, int meshIndex, Vector *pPosition ) const + { + Assert( pMesh ); + GetPosition( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pPosition ); + } + + void GetMeshNormal( mstudiomesh_t *pMesh, int meshIndex, Vector *pNormal ) const + { + Assert( pMesh ); + GetNormal( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pNormal ); + } + + void GetMeshBoneWeights( mstudiomesh_t *pMesh, int meshIndex, mstudioboneweight_t *pBoneWeights ) const + { + Assert( pMesh ); + GetBoneWeights( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pBoneWeights ); + } + + void GetModelPosition( mstudiomodel_t *pModel, int modelIndex, Vector *pPosition ) const + { + Assert( pModel ); + GetPosition( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pPosition ); + } + + void GetModelNormal( mstudiomodel_t *pModel, int modelIndex, Vector *pNormal ) const + { + Assert( pModel ); + GetNormal( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pNormal ); + } + + void GetModelBoneWeights( mstudiomodel_t *pModel, int modelIndex, mstudioboneweight_t *pBoneWeights ) const + { + Assert( pModel ); + GetBoneWeights( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pBoneWeights ); + } + +private: + void GetPosition( int vertIndex, Vector *pPosition ) const + { + Assert( pPosition ); + Assert( m_vecPositions ); + *pPosition = m_vecPositions[ vertIndex ]; + } + + void GetNormal( int vertIndex, Vector *pNormal ) const + { + Assert( pNormal ); + Assert( m_vecNormals ); + unsigned int packedNormal = 0x0000FFFF & m_vecNormals[ vertIndex ]; + UnpackNormal_UBYTE4( &packedNormal, pNormal->Base() ); + } + + void GetBoneWeights( int vertIndex, mstudioboneweight_t * RESTRICT pBoneWeights ) const + { + Assert( pBoneWeights ); + Assert( ( m_numBoneInfluences <= 1 ) || ( m_boneWeights != NULL ) ); + Assert( ( m_numBoneInfluences <= 0 ) || ( m_boneIndices != NULL ) ); + int numStoredWeights = MAX( 0, ( m_numBoneInfluences - 1 ) ); + float * RESTRICT pBaseWeight = m_boneWeights + vertIndex*numStoredWeights; + char * RESTRICT pBaseIndex = m_boneIndices + vertIndex*m_numBoneInfluences; + float sum = 0.0f; + // TODO: unroll this loop? It's only three. We could use a switch + // and code it explicitly for the various possible m_numBoneInfluences + // which would improve scheduling but bloat code. + for (int i = 0;i < MAX_NUM_BONES_PER_VERT;i++) + { + float weight; + if ( i < ( m_numBoneInfluences - 1 ) ) + { + weight = pBaseWeight[i]; + sum += weight; + } + else + { + weight = 1.0f - sum; + sum = 1.0f; + } + + pBoneWeights->weight[i] = weight; + pBoneWeights->bone[i] = ( i < m_numBoneInfluences ) ? pBaseIndex[i] : 0; + + /* + if ( i < ( m_numBoneInfluences - 1 ) ) + pBoneWeights->weight[i] = pBaseWeight[i]; + else + pBoneWeights->weight[i] = 1.0f - sum; + sum += pBoneWeights->weight[i]; + + pBoneWeights->bone[i] = ( i < m_numBoneInfluences ) ? pBaseIndex[i] : 0; + */ + } + + // Treat 'zero weights' as '100% binding to bone zero': + pBoneWeights->numbones = m_numBoneInfluences ? m_numBoneInfluences : 1; + } + + int m_numBoneInfluences;// Number of bone influences per vertex, N + float *m_boneWeights; // This array stores (N-1) weights per vertex (unless N is zero) + char *m_boneIndices; // This array stores N indices per vertex + Vector *m_vecPositions; + unsigned short *m_vecNormals; // Normals are compressed into 16 bits apiece (see PackNormal_UBYTE4() ) +}; + + +// ---------------------------------------------------------- +// Studio Model Stream Data File +// ---------------------------------------------------------- + +// little-endian "IDSS" +#define MODEL_STREAM_FILE_ID (('S'<<24)+('S'<<16)+('D'<<8)+'I') +#define MODEL_STREAM_FILE_VERSION 1 + +struct vertexStreamFileHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; // MODEL_STREAM_FILE_ID + int version; // MODEL_STREAM_FILE_VERSION + long checksum; // same as studiohdr_t, ensures sync + long flags; // flags + int numVerts; // number of vertices + int uv2StreamStart; // offset from base to uv2 stream + int uv2ElementSize; // size of each uv2 element + int pad; // pad + +public: + + // Accessor to uv2 stream + const void *GetStreamUv2() const + { + if ( ( id == MODEL_STREAM_FILE_ID ) && ( uv2StreamStart != 0 ) ) + return ( void * ) ( uv2StreamStart + (byte *)this ); + else + return NULL; + } +}; + + + +// ---------------------------------------------------------- +// Studio Model Vertex Data File +// Position independent flat data for cache manager +// ---------------------------------------------------------- + +// little-endian "IDSV" +#define MODEL_VERTEX_FILE_ID (('V'<<24)+('S'<<16)+('D'<<8)+'I') +#define MODEL_VERTEX_FILE_VERSION 4 +// this id (IDCV) is used once the vertex data has been compressed (see CMDLCache::CreateThinVertexes) +#define MODEL_VERTEX_FILE_THIN_ID (('V'<<24)+('C'<<16)+('D'<<8)+'I') +// this id (IDDV) is used once the vertex data has been discarded (see CMDLCache::CreateNullVertexes) +#define MODEL_VERTEX_FILE_NULL_ID (('V'<<24)+('D'<<16)+('D'<<8)+'I') + +struct vertexFileHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; // MODEL_VERTEX_FILE_ID + int version; // MODEL_VERTEX_FILE_VERSION + long checksum; // same as studiohdr_t, ensures sync + int numLODs; // num of valid lods + int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod + int numFixups; // num of vertexFileFixup_t + int fixupTableStart; // offset from base to fixup table + int vertexDataStart; // offset from base to vertex block + int tangentDataStart; // offset from base to tangent block + +public: + + // Accessor to fat vertex data + const mstudiovertex_t *GetVertexData() const + { + if ( ( id == MODEL_VERTEX_FILE_ID ) && ( vertexDataStart != 0 ) ) + return ( mstudiovertex_t * ) ( vertexDataStart + (byte *)this ); + else + return NULL; + } + // Accessor to (fat) tangent vertex data (tangents aren't stored in compressed data) + const Vector4D *GetTangentData() const + { + if ( ( id == MODEL_VERTEX_FILE_ID ) && ( tangentDataStart != 0 ) ) + return ( Vector4D * ) ( tangentDataStart + (byte *)this ); + else + return NULL; + } + // Accessor to thin vertex data + const thinModelVertices_t *GetThinVertexData() const + { + if ( ( id == MODEL_VERTEX_FILE_THIN_ID ) && ( vertexDataStart != 0 ) ) + return ( thinModelVertices_t * ) ( vertexDataStart + (byte *)this ); + else + return NULL; + } +}; + +// model vertex data accessor (defined here so vertexFileHeader_t can be used) +inline const mstudio_modelvertexdata_t * mstudiomodel_t::GetVertexData( void *pModelData ) +{ + const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData ); + if ( !pVertexHdr ) + return NULL; + + vertexdata.pVertexData = pVertexHdr->GetVertexData(); + vertexdata.pTangentData = pVertexHdr->GetTangentData(); + + if ( !vertexdata.pVertexData ) + return NULL; + + return &vertexdata; +} + +// model thin vertex data accessor (defined here so vertexFileHeader_t can be used) +inline const thinModelVertices_t * mstudiomodel_t::GetThinVertexData( void *pModelData ) +{ + const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData ); + if ( !pVertexHdr ) + return NULL; + + return pVertexHdr->GetThinVertexData(); +} + +// apply sequentially to lod sorted vertex and tangent pools to re-establish mesh order +struct vertexFileFixup_t +{ + DECLARE_BYTESWAP_DATADESC(); + int lod; // used to skip culled root lod + int sourceVertexID; // absolute index from start of vertex/tangent blocks + int numVertexes; +}; + +// This flag is set if no hitbox information was specified +#define STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX ( 1 << 0 ) + +// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild +// models when we change materials. +#define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP ( 1 << 1 ) + +// Use this when there are translucent parts to the model but we're not going to sort it +#define STUDIOHDR_FLAGS_FORCE_OPAQUE ( 1 << 2 ) + +// Use this when we want to render the opaque parts during the opaque pass +// and the translucent parts during the translucent pass +#define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS ( 1 << 3 ) + +// This is set any time the .qc files has $staticprop in it +// Means there's no bones and no transforms +#define STUDIOHDR_FLAGS_STATIC_PROP ( 1 << 4 ) + +// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild +// models when we change materials. +#define STUDIOHDR_FLAGS_USES_FB_TEXTURE ( 1 << 5 ) + +// This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present +// for the .mdl (the shadow lod is the last entry in the lod list if present) +#define STUDIOHDR_FLAGS_HASSHADOWLOD ( 1 << 6 ) + +// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild +// models when we change materials. +#define STUDIOHDR_FLAGS_USES_BUMPMAPPING ( 1 << 7 ) + +// NOTE: This flag is set when we should use the actual materials on the shadow LOD +// instead of overriding them with the default one (necessary for translucent shadows) +#define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS ( 1 << 8 ) + +// NOTE: This flag is set when we should use the actual materials on the shadow LOD +// instead of overriding them with the default one (necessary for translucent shadows) +#define STUDIOHDR_FLAGS_OBSOLETE ( 1 << 9 ) + +#define STUDIOHDR_FLAGS_UNUSED ( 1 << 10 ) + +// NOTE: This flag is set at mdl build time +#define STUDIOHDR_FLAGS_NO_FORCED_FADE ( 1 << 11 ) + +// NOTE: The npc will lengthen the viseme check to always include two phonemes +#define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE ( 1 << 12 ) + +// This flag is set when the .qc has $constantdirectionallight in it +// If set, we use constantdirectionallightdot to calculate light intensity +// rather than the normal directional dot product +// only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set +#define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT ( 1 << 13 ) + +// Flag to mark delta flexes as already converted from disk format to memory format +#define STUDIOHDR_FLAGS_FLEXES_CONVERTED ( 1 << 14 ) + +// Indicates the studiomdl was built in preview mode +#define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE ( 1 << 15 ) + +// Ambient boost (runtime flag) +#define STUDIOHDR_FLAGS_AMBIENT_BOOST ( 1 << 16 ) + +// Don't cast shadows from this model (useful on first-person models) +#define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS ( 1 << 17 ) + +// alpha textures should cast shadows in vrad on this model (ONLY prop_static!) +#define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS ( 1 << 18 ) + +// Model has a quad-only Catmull-Clark SubD cage +#define STUDIOHDR_FLAGS_SUBDIVISION_SURFACE ( 1 << 19 ) + +// flagged on load to indicate no animation events on this model +#define STUDIOHDR_FLAGS_NO_ANIM_EVENTS ( 1 << 20 ) + +// If flag is set then studiohdr_t.flVertAnimFixedPointScale contains the +// scale value for fixed point vert anim data, if not set then the +// scale value is the default of 1.0 / 4096.0. Regardless use +// studiohdr_t::VertAnimFixedPointScale() to always retrieve the scale value +#define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE ( 1 << 21 ) + +// NOTE! Next time we up the .mdl file format, remove studiohdr2_t +// and insert all fields in this structure into studiohdr_t. +struct studiohdr2_t +{ + // NOTE: For forward compat, make sure any methods in this struct + // are also available in studiohdr_t so no leaf code ever directly references + // a studiohdr2_t structure + DECLARE_BYTESWAP_DATADESC(); + int numsrcbonetransform; + int srcbonetransformindex; + + int illumpositionattachmentindex; + inline int IllumPositionAttachmentIndex() const { return illumpositionattachmentindex; } + + float flMaxEyeDeflection; + inline float MaxEyeDeflection() const { return flMaxEyeDeflection != 0.0f ? flMaxEyeDeflection : 0.866f; } // default to cos(30) if not set + + int linearboneindex; + inline mstudiolinearbone_t *pLinearBones() const { return (linearboneindex) ? (mstudiolinearbone_t *)(((byte *)this) + linearboneindex) : NULL; } + + int sznameindex; + inline char *pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; } + + int m_nBoneFlexDriverCount; + int m_nBoneFlexDriverIndex; + inline mstudioboneflexdriver_t *pBoneFlexDriver( int i ) const { Assert( i >= 0 && i < m_nBoneFlexDriverCount ); return (mstudioboneflexdriver_t *)(((byte *)this) + m_nBoneFlexDriverIndex) + i; } + + int reserved[56]; +}; + +struct studiohdr_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; + int version; + + long checksum; // this has to be the same in the phy and vtx files to load! + + inline const char * pszName( void ) const { if (studiohdr2index && pStudioHdr2()->pszName()) return pStudioHdr2()->pszName(); else return name; } + char name[64]; + + int length; + + Vector eyeposition; // ideal eye position + + Vector illumposition; // illumination center + + Vector hull_min; // ideal movement hull size + Vector hull_max; + + Vector view_bbmin; // clipping bounding box + Vector view_bbmax; + + int flags; + + int numbones; // bones + int boneindex; + inline mstudiobone_t *pBone( int i ) const { Assert( i >= 0 && i < numbones); return (mstudiobone_t *)(((byte *)this) + boneindex) + i; }; + int RemapSeqBone( int iSequence, int iLocalBone ) const; // maps local sequence bone to global bone + int RemapAnimBone( int iAnim, int iLocalBone ) const; // maps local animations bone to global bone + + int numbonecontrollers; // bone controllers + int bonecontrollerindex; + inline mstudiobonecontroller_t *pBonecontroller( int i ) const { Assert( i >= 0 && i < numbonecontrollers); return (mstudiobonecontroller_t *)(((byte *)this) + bonecontrollerindex) + i; }; + + int numhitboxsets; + int hitboxsetindex; + + // Look up hitbox set by index + mstudiohitboxset_t *pHitboxSet( int i ) const + { + Assert( i >= 0 && i < numhitboxsets); + return (mstudiohitboxset_t *)(((byte *)this) + hitboxsetindex ) + i; + }; + + // Calls through to hitbox to determine size of specified set + inline mstudiobbox_t *pHitbox( int i, int set ) const + { + mstudiohitboxset_t const *s = pHitboxSet( set ); + if ( !s ) + return NULL; + + return s->pHitbox( i ); + }; + + // Calls through to set to get hitbox count for set + inline int iHitboxCount( int set ) const + { + mstudiohitboxset_t const *s = pHitboxSet( set ); + if ( !s ) + return 0; + + return s->numhitboxes; + }; + + // file local animations? and sequences +//private: + int numlocalanim; // animations/poses + int localanimindex; // animation descriptions + inline mstudioanimdesc_t *pLocalAnimdesc( int i ) const { if (i < 0 || i >= numlocalanim) i = 0; return (mstudioanimdesc_t *)(((byte *)this) + localanimindex) + i; }; + + int numlocalseq; // sequences + int localseqindex; + inline mstudioseqdesc_t *pLocalSeqdesc( int i ) const { if (i < 0 || i >= numlocalseq) i = 0; return (mstudioseqdesc_t *)(((byte *)this) + localseqindex) + i; }; + +//public: + bool SequencesAvailable() const; + int GetNumSeq() const; + mstudioanimdesc_t &pAnimdesc( int i ) const; + mstudioseqdesc_t &pSeqdesc( int i ) const; + int iRelativeAnim( int baseseq, int relanim ) const; // maps seq local anim reference to global anim index + int iRelativeSeq( int baseseq, int relseq ) const; // maps seq local seq reference to global seq index + +//private: + mutable int activitylistversion; // initialization flag - have the sequences been indexed? + mutable int eventsindexed; +//public: + int GetSequenceActivity( int iSequence ); + void SetSequenceActivity( int iSequence, int iActivity ); + int GetActivityListVersion( void ); + void SetActivityListVersion( int version ) const; + int GetEventListVersion( void ); + void SetEventListVersion( int version ); + + // raw textures + int numtextures; + int textureindex; + inline mstudiotexture_t *pTexture( int i ) const { Assert( i >= 0 && i < numtextures ); return (mstudiotexture_t *)(((byte *)this) + textureindex) + i; }; + + + // raw textures search paths + int numcdtextures; + int cdtextureindex; + inline char *pCdtexture( int i ) const { return (((char *)this) + *((int *)(((byte *)this) + cdtextureindex) + i)); }; + + // replaceable textures tables + int numskinref; + int numskinfamilies; + int skinindex; + inline short *pSkinref( int i ) const { return (short *)(((byte *)this) + skinindex) + i; }; + + int numbodyparts; + int bodypartindex; + inline mstudiobodyparts_t *pBodypart( int i ) const { return (mstudiobodyparts_t *)(((byte *)this) + bodypartindex) + i; }; + + // queryable attachable points +//private: + int numlocalattachments; + int localattachmentindex; + inline mstudioattachment_t *pLocalAttachment( int i ) const { Assert( i >= 0 && i < numlocalattachments); return (mstudioattachment_t *)(((byte *)this) + localattachmentindex) + i; }; +//public: + int GetNumAttachments( void ) const; + const mstudioattachment_t &pAttachment( int i ) const; + int GetAttachmentBone( int i ); + // used on my tools in hlmv, not persistant + void SetAttachmentBone( int iAttachment, int iBone ); + + // animation node to animation node transition graph +//private: + int numlocalnodes; + int localnodeindex; + int localnodenameindex; + inline char *pszLocalNodeName( int iNode ) const { Assert( iNode >= 0 && iNode < numlocalnodes); return (((char *)this) + *((int *)(((byte *)this) + localnodenameindex) + iNode)); } + inline byte *pLocalTransition( int i ) const { Assert( i >= 0 && i < (numlocalnodes * numlocalnodes)); return (byte *)(((byte *)this) + localnodeindex) + i; }; + +//public: + int EntryNode( int iSequence ); + int ExitNode( int iSequence ); + char *pszNodeName( int iNode ); + int GetTransition( int iFrom, int iTo ) const; + + int numflexdesc; + int flexdescindex; + inline mstudioflexdesc_t *pFlexdesc( int i ) const { Assert( i >= 0 && i < numflexdesc); return (mstudioflexdesc_t *)(((byte *)this) + flexdescindex) + i; }; + + int numflexcontrollers; + int flexcontrollerindex; + inline mstudioflexcontroller_t *pFlexcontroller( LocalFlexController_t i ) const { Assert( i >= 0 && i < numflexcontrollers); return (mstudioflexcontroller_t *)(((byte *)this) + flexcontrollerindex) + i; }; + + int numflexrules; + int flexruleindex; + inline mstudioflexrule_t *pFlexRule( int i ) const { Assert( i >= 0 && i < numflexrules); return (mstudioflexrule_t *)(((byte *)this) + flexruleindex) + i; }; + + int numikchains; + int ikchainindex; + inline mstudioikchain_t *pIKChain( int i ) const { Assert( i >= 0 && i < numikchains); return (mstudioikchain_t *)(((byte *)this) + ikchainindex) + i; }; + + int nummouths; + int mouthindex; + inline mstudiomouth_t *pMouth( int i ) const { Assert( i >= 0 && i < nummouths); return (mstudiomouth_t *)(((byte *)this) + mouthindex) + i; }; + +//private: + int numlocalposeparameters; + int localposeparamindex; + inline mstudioposeparamdesc_t *pLocalPoseParameter( int i ) const { Assert( i >= 0 && i < numlocalposeparameters); return (mstudioposeparamdesc_t *)(((byte *)this) + localposeparamindex) + i; }; +//public: + int GetNumPoseParameters( void ) const; + const mstudioposeparamdesc_t &pPoseParameter( int i ); + int GetSharedPoseParameter( int iSequence, int iLocalPose ) const; + + int surfacepropindex; + inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropindex; } + inline int GetSurfaceProp() const { return surfacepropLookup; } + + // Key values + int keyvalueindex; + int keyvaluesize; + inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; } + + int numlocalikautoplaylocks; + int localikautoplaylockindex; + inline mstudioiklock_t *pLocalIKAutoplayLock( int i ) const { Assert( i >= 0 && i < numlocalikautoplaylocks); return (mstudioiklock_t *)(((byte *)this) + localikautoplaylockindex) + i; }; + + int GetNumIKAutoplayLocks( void ) const; + const mstudioiklock_t &pIKAutoplayLock( int i ); + int CountAutoplaySequences() const; + int CopyAutoplaySequences( unsigned short *pOut, int outCount ) const; + int GetAutoplayList( unsigned short **pOut ) const; + + // The collision model mass that jay wanted + float mass; + int contents; + + // external animations, models, etc. + int numincludemodels; + int includemodelindex; + inline mstudiomodelgroup_t *pModelGroup( int i ) const { Assert( i >= 0 && i < numincludemodels); return (mstudiomodelgroup_t *)(((byte *)this) + includemodelindex) + i; }; + // implementation specific call to get a named model + const studiohdr_t *FindModel( void **cache, char const *modelname ) const; + + // implementation specific back pointer to virtual data + mutable void *virtualModel; + virtualmodel_t *GetVirtualModel( void ) const; + + // for demand loaded animation blocks + int szanimblocknameindex; + inline char * const pszAnimBlockName( void ) const { return ((char *)this) + szanimblocknameindex; } + int numanimblocks; + int animblockindex; + inline mstudioanimblock_t *pAnimBlock( int i ) const { Assert( i > 0 && i < numanimblocks); return (mstudioanimblock_t *)(((byte *)this) + animblockindex) + i; }; + mutable void *animblockModel; + byte * GetAnimBlock( int i ) const; + + int bonetablebynameindex; + inline const byte *GetBoneTableSortedByName() const { return (byte *)this + bonetablebynameindex; } + + // used by tools only that don't cache, but persist mdl's peer data + // engine uses virtualModel to back link to cache pointers + void *pVertexBase; + void *pIndexBase; + + // if STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT is set, + // this value is used to calculate directional components of lighting + // on static props + byte constdirectionallightdot; + + // set during load of mdl data to track *desired* lod configuration (not actual) + // the *actual* clamped root lod is found in studiohwdata + // this is stored here as a global store to ensure the staged loading matches the rendering + byte rootLOD; + + // set in the mdl data to specify that lod configuration should only allow first numAllowRootLODs + // to be set as root LOD: + // numAllowedRootLODs = 0 means no restriction, any lod can be set as root lod. + // numAllowedRootLODs = N means that lod0 - lod(N-1) can be set as root lod, but not lodN or lower. + byte numAllowedRootLODs; + + byte unused[1]; + + int unused4; // zero out if version < 47 + + int numflexcontrollerui; + int flexcontrolleruiindex; + mstudioflexcontrollerui_t *pFlexControllerUI( int i ) const { Assert( i >= 0 && i < numflexcontrollerui); return (mstudioflexcontrollerui_t *)(((byte *)this) + flexcontrolleruiindex) + i; } + + float flVertAnimFixedPointScale; + inline float VertAnimFixedPointScale() const { return ( flags & STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE ) ? flVertAnimFixedPointScale : 1.0f / 4096.0f; } + + mutable int surfacepropLookup; // this index must be cached by the loader, not saved in the file + + // FIXME: Remove when we up the model version. Move all fields of studiohdr2_t into studiohdr_t. + int studiohdr2index; + studiohdr2_t* pStudioHdr2() const { return (studiohdr2_t *)( ( (byte *)this ) + studiohdr2index ); } + + // Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations + int NumSrcBoneTransforms() const { return studiohdr2index ? pStudioHdr2()->numsrcbonetransform : 0; } + const mstudiosrcbonetransform_t* SrcBoneTransform( int i ) const { Assert( i >= 0 && i < NumSrcBoneTransforms()); return (mstudiosrcbonetransform_t *)(((byte *)this) + pStudioHdr2()->srcbonetransformindex) + i; } + + inline int IllumPositionAttachmentIndex() const { return studiohdr2index ? pStudioHdr2()->IllumPositionAttachmentIndex() : 0; } + + inline float MaxEyeDeflection() const { return studiohdr2index ? pStudioHdr2()->MaxEyeDeflection() : 0.866f; } // default to cos(30) if not set + + inline mstudiolinearbone_t *pLinearBones() const { return studiohdr2index ? pStudioHdr2()->pLinearBones() : NULL; } + + inline int BoneFlexDriverCount() const { return studiohdr2index ? pStudioHdr2()->m_nBoneFlexDriverCount : 0; } + inline const mstudioboneflexdriver_t* BoneFlexDriver( int i ) const { Assert( i >= 0 && i < BoneFlexDriverCount() ); return studiohdr2index ? pStudioHdr2()->pBoneFlexDriver( i ) : NULL; } + + // NOTE: No room to add stuff? Up the .mdl file format version + // [and move all fields in studiohdr2_t into studiohdr_t and kill studiohdr2_t], + // or add your stuff to studiohdr2_t. See NumSrcBoneTransforms/SrcBoneTransform for the pattern to use. + int unused2[1]; + + studiohdr_t() {} + +private: + // No copy constructors allowed + studiohdr_t(const studiohdr_t& vOther); + + friend struct virtualmodel_t; +}; + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +class IDataCache; +class IMDLCache; + +class CStudioHdr +{ +public: + CStudioHdr( void ); + CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache = NULL ); + ~CStudioHdr() { Term(); } + + void Init( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache = NULL ); + void Term(); + +public: + inline bool IsVirtual( void ) { return (m_pVModel != NULL); }; + inline bool IsValid( void ) { return (m_pStudioHdr != NULL); }; + inline bool IsReadyForAccess( void ) const { return (m_pStudioHdr != NULL); }; + inline virtualmodel_t *GetVirtualModel( void ) const { return m_pVModel; }; + inline const studiohdr_t *GetRenderHdr( void ) const { return m_pStudioHdr; }; + const studiohdr_t *pSeqStudioHdr( int sequence ); + const studiohdr_t *pAnimStudioHdr( int animation ); + +private: + mutable const studiohdr_t *m_pStudioHdr; + mutable virtualmodel_t *m_pVModel; + + const virtualmodel_t * ResetVModel( const virtualmodel_t *pVModel ) const; + const studiohdr_t *GroupStudioHdr( int group ); + mutable CUtlVector< const studiohdr_t * > m_pStudioHdrCache; + + mutable int m_nFrameUnlockCounter; + int * m_pFrameUnlockCounter; + CThreadFastMutex m_FrameUnlockCounterMutex; + +public: + inline int numbones( void ) const { return m_pStudioHdr->numbones; }; + inline mstudiobone_t *pBone( int i ) const { return m_pStudioHdr->pBone( i ); }; + int RemapAnimBone( int iAnim, int iLocalBone ) const; // maps local animations bone to global bone + int RemapSeqBone( int iSequence, int iLocalBone ) const; // maps local sequence bone to global bone + + bool SequencesAvailable() const; + int GetNumSeq( void ) const; + mstudioanimdesc_t &pAnimdesc( int i ); + mstudioseqdesc_t &pSeqdesc( int iSequence ); + int iRelativeAnim( int baseseq, int relanim ) const; // maps seq local anim reference to global anim index + int iRelativeSeq( int baseseq, int relseq ) const; // maps seq local seq reference to global seq index + + int GetSequenceActivity( int iSequence ); + void SetSequenceActivity( int iSequence, int iActivity ); + int GetActivityListVersion( void ); + void SetActivityListVersion( int version ); + int GetEventListVersion( void ); + void SetEventListVersion( int version ); + + int GetNumAttachments( void ) const; + const mstudioattachment_t &pAttachment( int i ); + int GetAttachmentBone( int i ); + // used on my tools in hlmv, not persistant + void SetAttachmentBone( int iAttachment, int iBone ); + + int EntryNode( int iSequence ); + int ExitNode( int iSequence ); + char *pszNodeName( int iNode ); + // FIXME: where should this one be? + int GetTransition( int iFrom, int iTo ) const; + + int GetNumPoseParameters( void ) const; + const mstudioposeparamdesc_t &pPoseParameter( int i ); + int GetSharedPoseParameter( int iSequence, int iLocalPose ) const; + + int GetNumIKAutoplayLocks( void ) const; + const mstudioiklock_t &pIKAutoplayLock( int i ); + + inline int CountAutoplaySequences() const { return m_pStudioHdr->CountAutoplaySequences(); }; + inline int CopyAutoplaySequences( unsigned short *pOut, int outCount ) const { return m_pStudioHdr->CopyAutoplaySequences( pOut, outCount ); }; + inline int GetAutoplayList( unsigned short **pOut ) const { return m_pStudioHdr->GetAutoplayList( pOut ); }; + + inline int GetNumBoneControllers( void ) const { return m_pStudioHdr->numbonecontrollers; }; + inline mstudiobonecontroller_t *pBonecontroller( int i ) const { return m_pStudioHdr->pBonecontroller( i ); }; + + inline int numikchains() const { return m_pStudioHdr->numikchains; }; + inline int GetNumIKChains( void ) const { return m_pStudioHdr->numikchains; }; + inline mstudioikchain_t *pIKChain( int i ) const { return m_pStudioHdr->pIKChain( i ); }; + + inline int numflexrules() const { return m_pStudioHdr->numflexrules; }; + inline mstudioflexrule_t *pFlexRule( int i ) const { return m_pStudioHdr->pFlexRule( i ); }; + + inline int numflexdesc() const{ return m_pStudioHdr->numflexdesc; }; + inline mstudioflexdesc_t *pFlexdesc( int i ) const { return m_pStudioHdr->pFlexdesc( i ); }; + + inline LocalFlexController_t numflexcontrollers() const{ return (LocalFlexController_t)m_pStudioHdr->numflexcontrollers; }; + inline mstudioflexcontroller_t *pFlexcontroller( LocalFlexController_t i ) const { return m_pStudioHdr->pFlexcontroller( i ); }; + + inline int numflexcontrollerui() const{ return m_pStudioHdr->numflexcontrollerui; }; + inline mstudioflexcontrollerui_t *pFlexcontrollerUI( int i ) const { return m_pStudioHdr->pFlexControllerUI( i ); }; + + inline const char *name() const { return m_pStudioHdr->pszName(); }; // deprecated -- remove after full xbox merge + inline const char *pszName() const { return m_pStudioHdr->pszName(); }; + + inline int numbonecontrollers() const { return m_pStudioHdr->numbonecontrollers; }; + + inline int numhitboxsets() const { return m_pStudioHdr->numhitboxsets; }; + inline mstudiohitboxset_t *pHitboxSet( int i ) const { return m_pStudioHdr->pHitboxSet( i ); }; + + inline mstudiobbox_t *pHitbox( int i, int set ) const { return m_pStudioHdr->pHitbox( i, set ); }; + inline int iHitboxCount( int set ) const { return m_pStudioHdr->iHitboxCount( set ); }; + + inline int numbodyparts() const { return m_pStudioHdr->numbodyparts; }; + inline mstudiobodyparts_t *pBodypart( int i ) const { return m_pStudioHdr->pBodypart( i ); }; + + inline int numskinfamilies() const { return m_pStudioHdr->numskinfamilies; } + + inline Vector eyeposition() const { return m_pStudioHdr->eyeposition; }; + + inline int flags() const { return m_pStudioHdr->flags; }; + + inline char *const pszSurfaceProp( void ) const { return m_pStudioHdr->pszSurfaceProp(); }; + inline int GetSurfaceProp()const { return m_pStudioHdr->surfacepropLookup; } + + inline float mass() const { return m_pStudioHdr->mass; }; + inline int contents() const { return m_pStudioHdr->contents; } + + inline const byte *GetBoneTableSortedByName() const { return m_pStudioHdr->GetBoneTableSortedByName(); }; + + inline Vector illumposition() const { return m_pStudioHdr->illumposition; }; + + inline Vector hull_min() const { return m_pStudioHdr->hull_min; }; // ideal movement hull size + inline Vector hull_max() const { return m_pStudioHdr->hull_max; }; + + inline Vector view_bbmin() const { return m_pStudioHdr->view_bbmin; }; // clipping bounding box + inline Vector view_bbmax() const { return m_pStudioHdr->view_bbmax; }; + + inline int numtextures() const { return m_pStudioHdr->numtextures; }; + + inline int IllumPositionAttachmentIndex() const { return m_pStudioHdr->IllumPositionAttachmentIndex(); } + + inline float MaxEyeDeflection() const { return m_pStudioHdr->MaxEyeDeflection(); } + + inline mstudiolinearbone_t *pLinearBones() const { return m_pStudioHdr->pLinearBones(); } + + inline int BoneFlexDriverCount() const { return m_pStudioHdr->BoneFlexDriverCount(); } + inline const mstudioboneflexdriver_t *BoneFlexDriver( int i ) const { return m_pStudioHdr->BoneFlexDriver( i ); } + +public: + int IsSequenceLooping( int iSequence ); + float GetSequenceCycleRate( int iSequence ); + + void RunFlexRules( const float *src, float *dest ); + void RunFlexRulesOld( const float *src, float *dest ); + void RunFlexRulesNew( const float *src, float *dest ); + + +public: + inline int boneFlags( int iBone ) const { return m_boneFlags[ iBone ]; } + inline void setBoneFlags( int iBone, int flags ) { m_boneFlags[ iBone ] |= flags; } + inline void clearBoneFlags( int iBone, int flags ) { m_boneFlags[ iBone ] &= ~flags; } + inline int boneParent( int iBone ) const { return m_boneParent[ iBone ]; } + +private: + CUtlVector< int > m_boneFlags; + CUtlVector< int > m_boneParent; + +public: + + // This class maps an activity to sequences allowed for that activity, accelerating the resolution + // of SelectWeightedSequence(), especially on PowerPC. Iterating through every sequence + // attached to a model turned out to be a very destructive cache access pattern on 360. + // + // I've encapsulated this behavior inside a nested class for organizational reasons; there is + // no particular programmatic or efficiency benefit to it. It just makes clearer what particular + // code in the otherwise very complicated StudioHdr class has to do with this particular + // optimization, and it lets you collapse the whole definition down to a single line in Visual + // Studio. + class CActivityToSequenceMapping /* final */ + { + public: + // A tuple of a sequence and its corresponding weight. Lists of these correspond to activities. + struct SequenceTuple + { + short seqnum; + short weight; // the absolute value of the weight from the sequence header + CUtlSymbol *pActivityModifiers; // list of activity modifier symbols + int iNumActivityModifiers; + }; + + // The type of the hash's stored data, a composite of both key and value + // (because that's how CUtlHash works): + // key: an int, the activity # + // values: an index into the m_pSequenceTuples array, a count of the + // total sequences present for an activity, and the sum of their + // weights. + // Note this struct is 128-bits wide, exactly coincident to a PowerPC + // cache line and VMX register. Please consider very carefully the + // performance implications before adding any additional fields to this. + // You could probably do away with totalWeight if you really had to. + struct HashValueType + { + // KEY (hashed) + int activityIdx; + + // VALUE (not hashed) + int startingIdx; + int count; + int totalWeight; + + HashValueType(int _actIdx, int _stIdx, int _ct, int _tW) : + activityIdx(_actIdx), startingIdx(_stIdx), count(_ct), totalWeight(_tW) {} + + // default constructor (ought not to be actually used) + HashValueType() : activityIdx(-1), startingIdx(-1), count(-1), totalWeight(-1) + { AssertMsg(false, "Don't use default HashValueType()!"); } + + + class HashFuncs + { + public: + // dummy constructor (gndn) + HashFuncs( int ) {} + + // COMPARE + // compare two entries for uniqueness. We should never have two different + // entries for the same activity, so we only compare the activity index; + // this allows us to use the utlhash as a dict by constructing dummy entries + // as hash lookup keys. + bool operator()( const HashValueType &lhs, const HashValueType &rhs ) const + { + return lhs.activityIdx == rhs.activityIdx; + } + + // HASH + // We only hash on the activity index; everything else is data. + unsigned int operator()( const HashValueType &item ) const + { + return HashInt( item.activityIdx ); + } + }; + }; + + typedef CUtlHash ActivityToValueIdxHash; + + // These must be here because IFM does not compile/link studio.cpp (?!?) + + // ctor + CActivityToSequenceMapping( void ) + : m_pSequenceTuples(NULL), m_iSequenceTuplesCount(0), m_ActToSeqHash(8,0,0), m_expectedVModel(NULL), m_pStudioHdr(NULL) + {}; + + // dtor -- not virtual because this class has no inheritors + ~CActivityToSequenceMapping() + { + if ( m_pSequenceTuples != NULL ) + { + if ( m_pSequenceTuples->pActivityModifiers != NULL ) + { + delete[] m_pSequenceTuples->pActivityModifiers; + } + delete[] m_pSequenceTuples; + } + } + + /// Get the list of sequences for an activity. Returns the pointer to the + /// first sequence tuple. Output parameters are a count of sequences present, + /// and the total weight of all the sequences. (it would be more LHS-friendly + /// to return these on registers, if only C++ offered more than one return + /// value....) + const SequenceTuple *GetSequences( int forActivity, int *outSequenceCount, int *outTotalWeight ); + + /// The number of sequences available for an activity. + int NumSequencesForActivity( int forActivity ); + + static CActivityToSequenceMapping *FindMapping( const CStudioHdr *pstudiohdr ); + static void ReleaseMapping( CActivityToSequenceMapping *pMap ); + static void ResetMappings(); + + private: + + /// Allocate my internal array. (It is freed in the destructor.) Also, + /// build the hash of activities to sequences and populate m_pSequenceTuples. + void Initialize( const CStudioHdr *pstudiohdr ); + + /// Force Initialize() to occur again, even if it has already occured. + void Reinitialize( CStudioHdr *pstudiohdr ); + + /// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. + int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence ); + + // selects the sequence with the most matching modifiers + int SelectWeightedSequenceFromModifiers( CStudioHdr *pstudiohdr, int activity, CUtlSymbol *pActivityModifiers, int iModifierCount ); + + // Actually a big array, into which the hash values index. + SequenceTuple *m_pSequenceTuples; + unsigned int m_iSequenceTuplesCount; // (size of the whole array) + + // we don't store an outer pointer because we can't initialize it at construction time + // (warning c4355) -- there are ways around this but it's easier to just pass in a + // pointer to the CStudioHdr when we need it, since this class isn't supposed to + // export its interface outside the studio header anyway. + // CStudioHdr * const m_pOuter; + + ActivityToValueIdxHash m_ActToSeqHash; + + const studiohdr_t *m_pStudioHdr; + + // we store these so we can know if the contents of the studiohdr have changed + // from underneath our feet (this is an emergency data integrity check) + const void *m_expectedVModel; + + + // double-check that the data I point to hasn't changed + bool ValidateAgainst( const CStudioHdr * RESTRICT pstudiohdr ); + void SetValidation( const CStudioHdr *RESTRICT pstudiohdr ); + + friend class CStudioHdr; + }; + + CActivityToSequenceMapping *m_pActivityToSequence; + + void InitActivityToSequence() + { + if ( !m_pActivityToSequence ) + { + m_pActivityToSequence = CActivityToSequenceMapping::FindMapping( this ); + } + } + + /// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. + /// Returns -1 on failure to find a sequence + inline int SelectWeightedSequence( int activity, int curSequence ) + { + InitActivityToSequence(); + return m_pActivityToSequence->SelectWeightedSequence( this, activity, curSequence ); + } + + inline int SelectWeightedSequenceFromModifiers( int activity, CUtlSymbol *pActivityModifiers, int iModifierCount ) + { + InitActivityToSequence(); + return m_pActivityToSequence->SelectWeightedSequenceFromModifiers( this, activity, pActivityModifiers, iModifierCount ); + } + + /// True iff there is at least one sequence for the given activity. + inline bool HaveSequenceForActivity( int activity ) + { + InitActivityToSequence(); + return (m_pActivityToSequence->NumSequencesForActivity( activity ) > 0); + } + + // Force this CStudioHdr's activity-to-sequence mapping to be reinitialized + inline void ReinitializeSequenceMapping(void) + { + if ( m_pActivityToSequence ) + { + CActivityToSequenceMapping::ReleaseMapping( m_pActivityToSequence ); + m_pActivityToSequence = NULL; + } + m_pActivityToSequence = CActivityToSequenceMapping::FindMapping( this ); + } + +#ifdef STUDIO_ENABLE_PERF_COUNTERS +public: + inline void ClearPerfCounters( void ) + { + m_nPerfAnimatedBones = 0; + m_nPerfUsedBones = 0; + m_nPerfAnimationLayers = 0; + }; + + // timing info + mutable int m_nPerfAnimatedBones; + mutable int m_nPerfUsedBones; + mutable int m_nPerfAnimationLayers; +#endif + + +}; + +/* +class CModelAccess +{ +public: + CModelAccess(CStudioHdr *pSemaphore) + : m_pStudioHdr(pSemaphore) + { + m_pStudioHdr->IncrementAccess(); + } + + ~CModelAccess() + { + m_pStudioHdr->DecrementAccess(); + } + +private: + CStudioHdr *m_pStudioHdr; +}; + +#define ENABLE_MODEL_ACCESS( a ) \ + CModelAccess ModelAccess##__LINE__( a->m_pStudioHdr ) +*/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +struct flexweight_t +{ + DECLARE_BYTESWAP_DATADESC(); + int key; + float weight; + float influence; +}; + +struct flexsetting_t +{ + DECLARE_BYTESWAP_DATADESC(); + int nameindex; + + inline char *pszName( void ) const + { + return (char *)(((byte *)this) + nameindex); + } + + // Leaving this for legacy support + int obsolete1; + + // Number of flex settings + int numsettings; + int index; + + // OBSOLETE: + int obsolete2; + + // Index of start of contiguous array of flexweight_t structures + int settingindex; + + //----------------------------------------------------------------------------- + // Purpose: Retrieves a pointer to the flexweight_t, including resolving + // any markov chain hierarchy. Because of this possibility, we return + // the number of settings in the weights array returned. We'll generally + // call this function with i == 0 + // Input : *base - + // i - + // **weights - + // Output : int + //----------------------------------------------------------------------------- + inline int psetting( byte *base, int i, flexweight_t **weights ) const; +}; + + +struct flexsettinghdr_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; + int version; + + inline const char * pszName( void ) const { return name; } + char name[64]; + int length; + + int numflexsettings; + int flexsettingindex; + inline flexsetting_t *pSetting( int i ) const { return (flexsetting_t *)(((byte *)this) + flexsettingindex) + i; }; + int nameindex; + + // look up flex settings by "index" + int numindexes; + int indexindex; + + inline flexsetting_t *pIndexedSetting( int index ) const + { + if ( index < 0 || index >= numindexes ) + { + return NULL; + } + + int i = *((int *)(((byte *)this) + indexindex) + index); + + if (i == -1) + { + return NULL; + } + + return pSetting( i ); + } + + // index names of "flexcontrollers" + int numkeys; + int keynameindex; + inline char *pLocalName( int i ) const { return (char *)(((byte *)this) + *((int *)(((byte *)this) + keynameindex) + i)); }; + + int keymappingindex; + inline int *pLocalToGlobal( int i ) const { return (int *)(((byte *)this) + keymappingindex) + i; }; + inline int LocalToGlobal( int i ) const { return *pLocalToGlobal( i ); }; +}; + +//----------------------------------------------------------------------------- +// Purpose: Retrieves a pointer to the flexweight_t. +// Input : *base - flexsettinghdr_t * pointer +// i - index of flex setting to retrieve +// **weights - destination for weights array starting at index i. +// Output : int +//----------------------------------------------------------------------------- +inline int flexsetting_t::psetting( byte *base, int i, flexweight_t **weights ) const +{ + // Grab array pointer + *weights = (flexweight_t *)(((byte *)this) + settingindex) + i; + // Return true number of settings + return numsettings; +}; + + +//----------------------------------------------------------------------------- +// For a given flex controller ui struct, these return the index of the +// studiohdr_t flex controller that correspond to the the left and right +// flex controllers if the ui controller is a stereo control. +// nWayValueIndex returns the index of the flex controller that is the value +// flex controller for an NWAY combination +// If these functions are called and the ui controller isn't of the type +// specified then -1 is returned +//----------------------------------------------------------------------------- +inline int mstudioflexcontrollerui_t::controllerIndex( const CStudioHdr &cStudioHdr ) const +{ + return !stereo ? pController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::rightIndex( const CStudioHdr &cStudioHdr ) const +{ + return stereo ? pRightController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::leftIndex( const CStudioHdr &cStudioHdr ) const +{ + return stereo ? pLeftController() - cStudioHdr.pFlexcontroller((LocalFlexController_t) 0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::nWayValueIndex( const CStudioHdr &cStudioHdr ) const +{ + return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline const mstudioflexcontroller_t *mstudioflexcontrollerui_t::pController( int index ) const +{ + if ( index < 0 || index > Count() ) + return NULL; + + if ( remaptype == FLEXCONTROLLER_REMAP_NWAY ) + { + if ( stereo ) + return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index ); + + if ( index == 0 ) + return pController(); + + if ( index == 1 ) + return pNWayValueController(); + + return NULL; + } + + if ( index > 1 ) + return NULL; + + if ( stereo ) + return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index ); + + if ( index > 0 ) + return NULL; + + return pController(); +} + + +#define STUDIO_CONST 1 // get float +#define STUDIO_FETCH1 2 // get Flexcontroller value +#define STUDIO_FETCH2 3 // get flex weight +#define STUDIO_ADD 4 +#define STUDIO_SUB 5 +#define STUDIO_MUL 6 +#define STUDIO_DIV 7 +#define STUDIO_NEG 8 // not implemented +#define STUDIO_EXP 9 // not implemented +#define STUDIO_OPEN 10 // only used in token parsing +#define STUDIO_CLOSE 11 +#define STUDIO_COMMA 12 // only used in token parsing +#define STUDIO_MAX 13 +#define STUDIO_MIN 14 +#define STUDIO_2WAY_0 15 // Fetch a value from a 2 Way slider for the 1st value RemapVal( 0.0, 0.5, 0.0, 1.0 ) +#define STUDIO_2WAY_1 16 // Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 ) +#define STUDIO_NWAY 17 // Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 ) +#define STUDIO_COMBO 18 // Perform a combo operation (essentially multiply the last N values on the stack) +#define STUDIO_DOMINATE 19 // Performs a combination domination operation +#define STUDIO_DME_LOWER_EYELID 20 // +#define STUDIO_DME_UPPER_EYELID 21 // + +// motion flags +#define STUDIO_X 0x00000001 +#define STUDIO_Y 0x00000002 +#define STUDIO_Z 0x00000004 +#define STUDIO_XR 0x00000008 +#define STUDIO_YR 0x00000010 +#define STUDIO_ZR 0x00000020 + +#define STUDIO_LX 0x00000040 +#define STUDIO_LY 0x00000080 +#define STUDIO_LZ 0x00000100 +#define STUDIO_LXR 0x00000200 +#define STUDIO_LYR 0x00000400 +#define STUDIO_LZR 0x00000800 + +#define STUDIO_LINEAR 0x00001000 + +#define STUDIO_TYPES 0x0003FFFF +#define STUDIO_RLOOP 0x00040000 // controller that wraps shortest distance + +// sequence and autolayer flags +#define STUDIO_LOOPING 0x0001 // ending frame should be the same as the starting frame +#define STUDIO_SNAP 0x0002 // do not interpolate between previous animation and this one +#define STUDIO_DELTA 0x0004 // this sequence "adds" to the base sequences, not slerp blends +#define STUDIO_AUTOPLAY 0x0008 // temporary flag that forces the sequence to always play +#define STUDIO_POST 0x0010 // +#define STUDIO_ALLZEROS 0x0020 // this animation/sequence has no real animation data +#define STUDIO_FRAMEANIM 0x0040 // animation is encoded as by frame x bone instead of RLE bone x frame +#define STUDIO_CYCLEPOSE 0x0080 // cycle index is taken from a pose parameter index +#define STUDIO_REALTIME 0x0100 // cycle index is taken from a real-time clock, not the animations cycle index +#define STUDIO_LOCAL 0x0200 // sequence has a local context sequence +#define STUDIO_HIDDEN 0x0400 // don't show in default selection views +#define STUDIO_OVERRIDE 0x0800 // a forward declared sequence (empty) +#define STUDIO_ACTIVITY 0x1000 // Has been updated at runtime to activity index +#define STUDIO_EVENT 0x2000 // Has been updated at runtime to event index on server +#define STUDIO_WORLD 0x4000 // sequence blends in worldspace +#define STUDIO_NOFORCELOOP 0x8000 // do not force the animation loop +#define STUDIO_EVENT_CLIENT 0x10000 // Has been updated at runtime to event index on client + +// autolayer flags +// 0x0001 +// 0x0002 +// 0x0004 +// 0x0008 +#define STUDIO_AL_POST 0x0010 // +// 0x0020 +#define STUDIO_AL_SPLINE 0x0040 // convert layer ramp in/out curve is a spline instead of linear +#define STUDIO_AL_XFADE 0x0080 // pre-bias the ramp curve to compense for a non-1 weight, assuming a second layer is also going to accumulate +// 0x0100 +#define STUDIO_AL_NOBLEND 0x0200 // animation always blends at 1.0 (ignores weight) +// 0x0400 +// 0x0800 +#define STUDIO_AL_LOCAL 0x1000 // layer is a local context sequence +// 0x2000 +#define STUDIO_AL_POSE 0x4000 // layer blends using a pose parameter instead of parent cycle + + +// Insert this code anywhere that you need to allow for conversion from an old STUDIO_VERSION to a new one. +// If we only support the current version, this function should be empty. +inline bool Studio_ConvertStudioHdrToNewVersion( studiohdr_t *pStudioHdr ) +{ + COMPILE_TIME_ASSERT( STUDIO_VERSION == 49 ); // put this to make sure this code is updated upon changing version. + + int version = pStudioHdr->version; + if ( version == STUDIO_VERSION ) + return true; + + bool bResult = true; + if (version < 46) + { + // some of the anim index data is incompatible + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + + // old ANI files that used sections (v45 only) are not compatible + if ( pAnim->sectionframes != 0 ) + { + // zero most everything out + memset( &(pAnim->numframes), 0, (byte *)(pAnim + 1) - (byte *)&(pAnim->numframes) ); + + pAnim->numframes = 1; + pAnim->animblock = -1; // disable animation fetching + bResult = false; + } + } + } + + if (version < 47) + { + // now used to contain zeroframe cache data, make sure it's empty + if (pStudioHdr->unused4 != 0) + { + pStudioHdr->unused4 = 0; + bResult = false; + } + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + pAnim->zeroframeindex = 0; + pAnim->zeroframespan = 0; + } + } + else if (version == 47) + { + // clear out stale version of zeroframe cache data + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + if (pAnim->zeroframeindex != 0) + { + pAnim->zeroframeindex = 0; + pAnim->zeroframespan = 0; + bResult = false; + } + } + } + + if (version < 49) + { + // remove any frameanim flag settings that might be stale + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + if (pAnim->flags & STUDIO_FRAMEANIM) + { + pAnim->flags &= ~STUDIO_FRAMEANIM; + bResult = false; + } + } + } + // for now, just slam the version number since they're compatible + pStudioHdr->version = STUDIO_VERSION; + + return bResult; +} + +// must be run to fixup with specified rootLOD +inline void Studio_SetRootLOD( studiohdr_t *pStudioHdr, int rootLOD ) +{ + // honor studiohdr restriction of root lod in case requested root lod exceeds restriction. + if ( pStudioHdr->numAllowedRootLODs > 0 && + rootLOD >= pStudioHdr->numAllowedRootLODs ) + { + rootLOD = pStudioHdr->numAllowedRootLODs - 1; + } + + // run the lod fixups that culls higher detail lods + // vertexes are external, fixups ensure relative offsets and counts are cognizant of shrinking data + // indexes are built in lodN..lod0 order so higher detail lod data can be truncated at load + // the fixup lookup arrays are filled (or replicated) to ensure all slots valid + int vertexindex = 0; + int tangentsindex = 0; + int bodyPartID; + for ( bodyPartID = 0; bodyPartID < pStudioHdr->numbodyparts; bodyPartID++ ) + { + mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPartID ); + int modelID; + for ( modelID = 0; modelID < pBodyPart->nummodels; modelID++ ) + { + mstudiomodel_t *pModel = pBodyPart->pModel( modelID ); + int totalMeshVertexes = 0; + int meshID; + for ( meshID = 0; meshID < pModel->nummeshes; meshID++ ) + { + mstudiomesh_t *pMesh = pModel->pMesh( meshID ); + + // get the fixup, vertexes are reduced + pMesh->numvertices = pMesh->vertexdata.numLODVertexes[rootLOD]; + pMesh->vertexoffset = totalMeshVertexes; + totalMeshVertexes += pMesh->numvertices; + } + + // stay in sync + pModel->numvertices = totalMeshVertexes; + pModel->vertexindex = vertexindex; + pModel->tangentsindex = tangentsindex; + + vertexindex += totalMeshVertexes*sizeof(mstudiovertex_t); + tangentsindex += totalMeshVertexes*sizeof(Vector4D); + } + } + + // track the set desired configuration + pStudioHdr->rootLOD = rootLOD; +} + +// Determines allocation requirements for vertexes +inline int Studio_VertexDataSize( const vertexFileHeader_t *pVvdHdr, int rootLOD, bool bNeedsTangentS ) +{ + // the quantity of vertexes necessary for root lod and all lower detail lods + // add one extra vertex to each section + // the extra vertex allows prefetch hints to read ahead 1 vertex without faulting + int numVertexes = pVvdHdr->numLODVertexes[rootLOD] + 1; + int dataLength = pVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t); + if (bNeedsTangentS) + { + dataLength += numVertexes*sizeof(Vector4D); + } + + // allocate this much + return dataLength; +} + +// Load the minimum quantity of verts and run fixups +inline int Studio_LoadVertexes( const vertexFileHeader_t *pTempVvdHdr, vertexFileHeader_t *pNewVvdHdr, int rootLOD, bool bNeedsTangentS ) +{ + int i; + int target; + int numVertexes; + vertexFileFixup_t *pFixupTable; + + numVertexes = pTempVvdHdr->numLODVertexes[rootLOD]; + + // copy all data up to start of vertexes + memcpy((void*)pNewVvdHdr, (void*)pTempVvdHdr, pTempVvdHdr->vertexDataStart); + + for ( i = 0; i < rootLOD; i++) + { + pNewVvdHdr->numLODVertexes[i] = pNewVvdHdr->numLODVertexes[rootLOD]; + } + + // fixup data starts + if (bNeedsTangentS) + { + // tangent data follows possibly reduced vertex data + pNewVvdHdr->tangentDataStart = pNewVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t); + } + else + { + // no tangent data will be available, mark for identification + pNewVvdHdr->tangentDataStart = 0; + } + + if (!pNewVvdHdr->numFixups) + { + // fixups not required + // transfer vertex data + memcpy( + (byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart, + (byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart, + numVertexes*sizeof(mstudiovertex_t) ); + + if (bNeedsTangentS) + { + // transfer tangent data to cache memory + memcpy( + (byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart, + (byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart, + numVertexes*sizeof(Vector4D) ); + } + + return numVertexes; + } + + // fixups required + // re-establish mesh ordered vertexes into cache memory, according to table + target = 0; + pFixupTable = (vertexFileFixup_t *)((byte *)pTempVvdHdr + pTempVvdHdr->fixupTableStart); + for (i=0; inumFixups; i++) + { + if (pFixupTable[i].lod < rootLOD) + { + // working bottom up, skip over copying higher detail lods + continue; + } + + // copy vertexes + memcpy( + (mstudiovertex_t *)((byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart) + target, + (mstudiovertex_t *)((byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart) + pFixupTable[i].sourceVertexID, + pFixupTable[i].numVertexes*sizeof(mstudiovertex_t) ); + + if (bNeedsTangentS) + { + // copy tangents + memcpy( + (Vector4D *)((byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart) + target, + (Vector4D *)((byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart) + pFixupTable[i].sourceVertexID, + pFixupTable[i].numVertexes*sizeof(Vector4D) ); + } + + // data is placed consecutively + target += pFixupTable[i].numVertexes; + } + + pNewVvdHdr->numFixups = 0; + + return target; +} + + +#endif // STUDIO_H diff --git a/public/studio_generic_io.cpp b/public/studio_generic_io.cpp new file mode 100644 index 0000000..054c911 --- /dev/null +++ b/public/studio_generic_io.cpp @@ -0,0 +1,156 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include "studio.h" +#include "UtlRBTree.h" + +extern studiohdr_t *FindOrLoadGroupFile( char const *modelname ); + +virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const +{ + if (numincludemodels == 0) + { + return NULL; + } + + virtualmodel_t *pVModel = (virtualmodel_t *)virtualModel; + + if (pVModel == NULL) + { + pVModel = new virtualmodel_t; + + // !!! Set cache handle? Set pointer to local virtual model?? + virtualModel = (void *)pVModel; + + int group = pVModel->m_group.AddToTail( ); + pVModel->m_group[ group ].cache = (void *)this; + pVModel->AppendModels( 0, this ); + } + + return pVModel; +} + + +const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *modelname ) const +{ + studiohdr_t *hdr = (studiohdr_t *)(*cache); + + if (hdr) + { + return hdr; + } + + hdr = FindOrLoadGroupFile( modelname ); + + *cache = (void *)hdr; + + return hdr; +} + +const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const +{ + return (studiohdr_t *)cache; +} + + +byte *studiohdr_t::GetAnimBlock( int i ) const +{ + byte *hdr = (byte *)animblockModel; + + if (!hdr) + { + hdr = (byte *)FindOrLoadGroupFile( pszAnimBlockName() ); + animblockModel = hdr; + } + + return hdr + pAnimBlock( i )->datastart; +} + +//----------------------------------------------------------------------------- +// Purpose: Builds up a dictionary of autoplay indices by studiohdr_t * +// NOTE: This list never gets freed even if the model gets unloaded, but we're in a tool so we can probably live with that +//----------------------------------------------------------------------------- +struct AutoPlayGeneric_t +{ +public: + + AutoPlayGeneric_t() : + hdr( 0 ) + { + } + + // Implement copy constructor + AutoPlayGeneric_t( const AutoPlayGeneric_t& src ) + { + hdr = src.hdr; + autoplaylist.EnsureCount( src.autoplaylist.Count() ); + autoplaylist.CopyArray( src.autoplaylist.Base(), src.autoplaylist.Count() ); + } + + static bool AutoPlayGenericLessFunc( const AutoPlayGeneric_t& lhs, const AutoPlayGeneric_t& rhs ) + { + return lhs.hdr < rhs.hdr; + } + +public: + // Data + const studiohdr_t *hdr; + CUtlVector< unsigned short > autoplaylist; +}; + +// A global array to track this data +static CUtlRBTree< AutoPlayGeneric_t, int > g_AutoPlayGeneric( 0, 0, AutoPlayGeneric_t::AutoPlayGenericLessFunc ); + +int studiohdr_t::GetAutoplayList( unsigned short **pAutoplayList ) const +{ + virtualmodel_t *pVirtualModel = GetVirtualModel(); + if ( pVirtualModel ) + { + if ( pAutoplayList && pVirtualModel->m_autoplaySequences.Count() ) + { + *pAutoplayList = pVirtualModel->m_autoplaySequences.Base(); + } + return pVirtualModel->m_autoplaySequences.Count(); + } + + AutoPlayGeneric_t *pData = NULL; + + // Search for this studiohdr_t ptr in the global list + AutoPlayGeneric_t search; + search.hdr = this; + int index = g_AutoPlayGeneric.Find( search ); + if ( index == g_AutoPlayGeneric.InvalidIndex() ) + { + // Not there, so add it + index = g_AutoPlayGeneric.Insert( search ); + pData = &g_AutoPlayGeneric[ index ]; + // And compute the autoplay info this one time + int autoPlayCount = CountAutoplaySequences(); + pData->autoplaylist.EnsureCount( autoPlayCount ); + CopyAutoplaySequences( pData->autoplaylist.Base(), autoPlayCount ); + } + else + { + // Refer to existing data + pData = &g_AutoPlayGeneric[ index ]; + } + + // Oops!!! + if ( !pData ) + { + return 0; + } + + // Give back data if it's being requested + if ( pAutoplayList ) + { + *pAutoplayList = pData->autoplaylist.Base(); + } + return pData->autoplaylist.Count(); +} diff --git a/public/studio_virtualmodel.cpp b/public/studio_virtualmodel.cpp new file mode 100644 index 0000000..309c130 --- /dev/null +++ b/public/studio_virtualmodel.cpp @@ -0,0 +1,567 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include +#include "studio.h" +#include "tier1/utlmap.h" +#include "tier1/utldict.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" +#include "tier0/icommandline.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern IFileSystem * g_pFileSystem; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +// a string table to speed up searching for sequences in the current virtual model +struct modellookup_t +{ + CUtlDict seqTable; + CUtlDict animTable; +}; + +static CUtlVector g_ModelLookup; +static int g_ModelLookupIndex = -1; + +inline bool HasLookupTable() +{ + return g_ModelLookupIndex >= 0 ? true : false; +} + +inline CUtlDict *GetSeqTable() +{ + return &g_ModelLookup[g_ModelLookupIndex].seqTable; +} + +inline CUtlDict *GetAnimTable() +{ + return &g_ModelLookup[g_ModelLookupIndex].animTable; +} + +class CModelLookupContext +{ +public: + CModelLookupContext(int group, const studiohdr_t *pStudioHdr); + ~CModelLookupContext(); + +private: + int m_lookupIndex; +}; + +CModelLookupContext::CModelLookupContext(int group, const studiohdr_t *pStudioHdr) +{ + m_lookupIndex = -1; + if ( group == 0 && pStudioHdr->numincludemodels ) + { + m_lookupIndex = g_ModelLookup.AddToTail(); + g_ModelLookupIndex = g_ModelLookup.Count()-1; + } +} + +CModelLookupContext::~CModelLookupContext() +{ + if ( m_lookupIndex >= 0 ) + { + Assert(m_lookupIndex == (g_ModelLookup.Count()-1)); + g_ModelLookup.FastRemove(m_lookupIndex); + g_ModelLookupIndex = g_ModelLookup.Count()-1; + } +} + +void virtualmodel_t::AppendModels( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + + // build a search table if necesary + CModelLookupContext ctx(group, pStudioHdr); + + AppendSequences( group, pStudioHdr ); + AppendAnimations( group, pStudioHdr ); + AppendBonemap( group, pStudioHdr ); + AppendAttachments( group, pStudioHdr ); + AppendPoseParameters( group, pStudioHdr ); + AppendNodes( group, pStudioHdr ); + AppendIKLocks( group, pStudioHdr ); + + struct HandleAndHeader_t + { + void *handle; + const studiohdr_t *pHdr; + }; + HandleAndHeader_t list[64]; + + // determine quantity of valid include models in one pass only + // temporarily cache results off, otherwise FindModel() causes ref counting problems + int j; + int nValidIncludes = 0; + for (j = 0; j < pStudioHdr->numincludemodels; j++) + { + // find model (increases ref count) + void *tmp = NULL; + const studiohdr_t *pTmpHdr = pStudioHdr->FindModel( &tmp, pStudioHdr->pModelGroup( j )->pszName() ); + if ( pTmpHdr ) + { + if ( nValidIncludes >= ARRAYSIZE( list ) ) + { + // would cause stack overflow + Assert( 0 ); + break; + } + + list[nValidIncludes].handle = tmp; + list[nValidIncludes].pHdr = pTmpHdr; + nValidIncludes++; + } + } + + if ( nValidIncludes ) + { + m_group.EnsureCapacity( m_group.Count() + nValidIncludes ); + for (j = 0; j < nValidIncludes; j++) + { + MEM_ALLOC_CREDIT(); + int group = m_group.AddToTail(); + m_group[group].cache = list[j].handle; + AppendModels( group, list[j].pHdr ); + } + } + + UpdateAutoplaySequences( pStudioHdr ); +} + +void virtualmodel_t::AppendSequences( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_seq.Count(); + + int j, k; + + MEM_ALLOC_CREDIT(); + + CUtlVector< virtualsequence_t > seq; + + seq = m_seq; + + m_group[ group ].masterSeq.SetCount( pStudioHdr->numlocalseq ); + + for (j = 0; j < pStudioHdr->numlocalseq; j++) + { + const mstudioseqdesc_t *seqdesc = pStudioHdr->pLocalSeqdesc( j ); + char *s1 = seqdesc->pszLabel(); + + if ( HasLookupTable() ) + { + k = numCheck; + short index = GetSeqTable()->Find( s1 ); + if ( index != GetSeqTable()->InvalidIndex() ) + { + k = GetSeqTable()->Element(index); + } + } + else + { + for (k = 0; k < numCheck; k++) + { + const studiohdr_t *hdr = m_group[ seq[k].group ].GetStudioHdr(); + char *s2 = hdr->pLocalSeqdesc( seq[k].index )->pszLabel(); + if ( !stricmp( s1, s2 ) ) + { + break; + } + } + } + // no duplication + if (k == numCheck) + { + virtualsequence_t tmp; + tmp.group = group; + tmp.index = j; + tmp.flags = seqdesc->flags; + tmp.activity = seqdesc->activity; + k = seq.AddToTail( tmp ); + } + else if (m_group[ seq[k].group ].GetStudioHdr()->pLocalSeqdesc( seq[k].index )->flags & STUDIO_OVERRIDE) + { + // the one in memory is a forward declared sequence, override it + virtualsequence_t tmp; + tmp.group = group; + tmp.index = j; + tmp.flags = seqdesc->flags; + tmp.activity = seqdesc->activity; + seq[k] = tmp; + } + m_group[ group ].masterSeq[ j ] = k; + } + + if ( HasLookupTable() ) + { + for ( j = numCheck; j < seq.Count(); j++ ) + { + const studiohdr_t *hdr = m_group[ seq[j].group ].GetStudioHdr(); + const char *s1 = hdr->pLocalSeqdesc( seq[j].index )->pszLabel(); + GetSeqTable()->Insert( s1, j ); + } + } + + m_seq = seq; +} + + +void virtualmodel_t::UpdateAutoplaySequences( const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int autoplayCount = pStudioHdr->CountAutoplaySequences(); + m_autoplaySequences.SetCount( autoplayCount ); + pStudioHdr->CopyAutoplaySequences( m_autoplaySequences.Base(), autoplayCount ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void virtualmodel_t::AppendAnimations( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_anim.Count(); + + CUtlVector< virtualgeneric_t > anim; + anim = m_anim; + + MEM_ALLOC_CREDIT(); + + int j, k; + + m_group[ group ].masterAnim.SetCount( pStudioHdr->numlocalanim ); + + for (j = 0; j < pStudioHdr->numlocalanim; j++) + { + char *s1 = pStudioHdr->pLocalAnimdesc( j )->pszName(); + if ( HasLookupTable() ) + { + k = numCheck; + short index = GetAnimTable()->Find( s1 ); + if ( index != GetAnimTable()->InvalidIndex() ) + { + k = GetAnimTable()->Element(index); + } + } + else + { + for (k = 0; k < numCheck; k++) + { + char *s2 = m_group[ anim[k].group ].GetStudioHdr()->pLocalAnimdesc( anim[k].index )->pszName(); + if (stricmp( s1, s2 ) == 0) + { + break; + } + } + } + // no duplication + if (k == numCheck) + { + virtualgeneric_t tmp; + tmp.group = group; + tmp.index = j; + k = anim.AddToTail( tmp ); + } + + m_group[ group ].masterAnim[ j ] = k; + } + + if ( HasLookupTable() ) + { + for ( j = numCheck; j < anim.Count(); j++ ) + { + const char *s1 = m_group[ anim[j].group ].GetStudioHdr()->pLocalAnimdesc( anim[j].index )->pszName(); + GetAnimTable()->Insert( s1, j ); + } + } + + m_anim = anim; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void virtualmodel_t::AppendBonemap( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + MEM_ALLOC_CREDIT(); + + const studiohdr_t *pBaseStudioHdr = m_group[ 0 ].GetStudioHdr( ); + + m_group[ group ].boneMap.SetCount( pBaseStudioHdr->numbones ); + m_group[ group ].masterBone.SetCount( pStudioHdr->numbones ); + + int j, k; + + if (group == 0) + { + for (j = 0; j < pStudioHdr->numbones; j++) + { + m_group[ group ].boneMap[ j ] = j; + m_group[ group ].masterBone[ j ] = j; + } + } + else + { + for (j = 0; j < pBaseStudioHdr->numbones; j++) + { + m_group[ group ].boneMap[ j ] = -1; + } + for (j = 0; j < pStudioHdr->numbones; j++) + { + // NOTE: studiohdr has a bone table - using the table is ~5% faster than this for alyx.mdl on a P4/3.2GHz + for (k = 0; k < pBaseStudioHdr->numbones; k++) + { + if (stricmp( pStudioHdr->pBone( j )->pszName(), pBaseStudioHdr->pBone( k )->pszName() ) == 0) + { + break; + } + } + if (k < pBaseStudioHdr->numbones) + { + m_group[ group ].masterBone[ j ] = k; + m_group[ group ].boneMap[ k ] = j; + + // FIXME: these runtime messages don't display in hlmv + if ((pStudioHdr->pBone( j )->parent == -1) || (pBaseStudioHdr->pBone( k )->parent == -1)) + { + if ((pStudioHdr->pBone( j )->parent != -1) || (pBaseStudioHdr->pBone( k )->parent != -1)) + { + Warning( "%s/%s : missmatched parent bones on \"%s\"\n", pBaseStudioHdr->pszName(), pStudioHdr->pszName(), pStudioHdr->pBone( j )->pszName() ); + } + } + else if (m_group[ group ].masterBone[ pStudioHdr->pBone( j )->parent ] != m_group[ 0 ].masterBone[ pBaseStudioHdr->pBone( k )->parent ]) + { + Warning( "%s/%s : missmatched parent bones on \"%s\"\n", pBaseStudioHdr->pszName(), pStudioHdr->pszName(), pStudioHdr->pBone( j )->pszName() ); + } + + // merge the bone use flags and pass up the chain + int flags = (pStudioHdr->pBone( j )->flags | pBaseStudioHdr->pBone( k )->flags) & BONE_USED_MASK; + int n = k; + while (n != -1 && flags != pBaseStudioHdr->pBone( n )->flags) + { + pBaseStudioHdr->pBone( n )->flags |= flags; + n = pBaseStudioHdr->pBone( n )->parent; + } + } + else + { + m_group[ group ].masterBone[ j ] = -1; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void virtualmodel_t::AppendAttachments( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_attachment.Count(); + + CUtlVector< virtualgeneric_t > attachment; + attachment = m_attachment; + + MEM_ALLOC_CREDIT(); + + int j, k, n; + + m_group[ group ].masterAttachment.SetCount( pStudioHdr->numlocalattachments ); + + for (j = 0; j < pStudioHdr->numlocalattachments; j++) + { + + n = m_group[ group ].masterBone[ pStudioHdr->pLocalAttachment( j )->localbone ]; + + // skip if the attachments bone doesn't exist in the root model + if (n == -1) + { + continue; + } + + + char *s1 = pStudioHdr->pLocalAttachment( j )->pszName(); + for (k = 0; k < numCheck; k++) + { + char *s2 = m_group[ attachment[k].group ].GetStudioHdr()->pLocalAttachment( attachment[k].index )->pszName(); + + if (stricmp( s1, s2 ) == 0) + { + break; + } + } + // no duplication + if (k == numCheck) + { + virtualgeneric_t tmp; + tmp.group = group; + tmp.index = j; + k = attachment.AddToTail( tmp ); + } + + m_group[ group ].masterAttachment[ j ] = k; + } + + m_attachment = attachment; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void virtualmodel_t::AppendPoseParameters( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_pose.Count(); + + CUtlVector< virtualgeneric_t > pose; + pose = m_pose; + + MEM_ALLOC_CREDIT(); + + int j, k; + + m_group[ group ].masterPose.SetCount( pStudioHdr->numlocalposeparameters ); + + for (j = 0; j < pStudioHdr->numlocalposeparameters; j++) + { + char *s1 = pStudioHdr->pLocalPoseParameter( j )->pszName(); + for (k = 0; k < numCheck; k++) + { + char *s2 = m_group[ pose[k].group ].GetStudioHdr()->pLocalPoseParameter( pose[k].index )->pszName(); + + if (stricmp( s1, s2 ) == 0) + { + break; + } + } + if (k == numCheck) + { + // no duplication + virtualgeneric_t tmp; + tmp.group = group; + tmp.index = j; + k = pose.AddToTail( tmp ); + } + else + { + // duplicate, reset start and end to fit full dynamic range + mstudioposeparamdesc_t *pPose1 = pStudioHdr->pLocalPoseParameter( j ); + mstudioposeparamdesc_t *pPose2 = m_group[ pose[k].group ].GetStudioHdr()->pLocalPoseParameter( pose[k].index ); + float start = MIN( pPose2->end, MIN( pPose1->end, MIN( pPose2->start, pPose1->start ) ) ); + float end = MAX( pPose2->end, MAX( pPose1->end, MAX( pPose2->start, pPose1->start ) ) ); + pPose2->start = start; + pPose2->end = end; + } + + m_group[ group ].masterPose[ j ] = k; + } + + m_pose = pose; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void virtualmodel_t::AppendNodes( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_node.Count(); + + CUtlVector< virtualgeneric_t > node; + node = m_node; + + MEM_ALLOC_CREDIT(); + + int j, k; + + m_group[ group ].masterNode.SetCount( pStudioHdr->numlocalnodes ); + + for (j = 0; j < pStudioHdr->numlocalnodes; j++) + { + char *s1 = pStudioHdr->pszLocalNodeName( j ); + for (k = 0; k < numCheck; k++) + { + char *s2 = m_group[ node[k].group ].GetStudioHdr()->pszLocalNodeName( node[k].index ); + + if (stricmp( s1, s2 ) == 0) + { + break; + } + } + // no duplication + if (k == numCheck) + { + virtualgeneric_t tmp; + tmp.group = group; + tmp.index = j; + k = node.AddToTail( tmp ); + } + + m_group[ group ].masterNode[ j ] = k; + } + + m_node = node; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + + +void virtualmodel_t::AppendIKLocks( int group, const studiohdr_t *pStudioHdr ) +{ + AUTO_LOCK_( CThreadTerminalMutex, m_Lock ); + int numCheck = m_iklock.Count(); + + CUtlVector< virtualgeneric_t > iklock; + iklock = m_iklock; + + int j, k; + + for (j = 0; j < pStudioHdr->numlocalikautoplaylocks; j++) + { + int chain1 = pStudioHdr->pLocalIKAutoplayLock( j )->chain; + for (k = 0; k < numCheck; k++) + { + int chain2 = m_group[ iklock[k].group ].GetStudioHdr()->pLocalIKAutoplayLock( iklock[k].index )->chain; + + if (chain1 == chain2) + { + break; + } + } + // no duplication + if (k == numCheck) + { + MEM_ALLOC_CREDIT(); + + virtualgeneric_t tmp; + tmp.group = group; + tmp.index = j; + k = iklock.AddToTail( tmp ); + } + } + + m_iklock = iklock; +} diff --git a/public/surfinfo.h b/public/surfinfo.h new file mode 100644 index 0000000..3fb3bc6 --- /dev/null +++ b/public/surfinfo.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef SURFINFO_H +#define SURFINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mathlib/vplane.h" + +//#include "mathlib/vector.h" +#define MAX_SURFINFO_VERTS 16 +class SurfInfo +{ +public: + // Shape of the surface. + Vector m_Verts[ MAX_SURFINFO_VERTS ]; + unsigned long m_nVerts; + + // Plane of the surface. + VPlane m_Plane; + + // For engine use only.. + void *m_pEngineData; +}; + +#endif // SURFINFO_H diff --git a/public/texture_group_names.h b/public/texture_group_names.h new file mode 100644 index 0000000..7b169ff --- /dev/null +++ b/public/texture_group_names.h @@ -0,0 +1,43 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef TEXTURE_GROUP_NAMES_H +#define TEXTURE_GROUP_NAMES_H +#ifdef _WIN32 +#pragma once +#endif + +// These are given to FindMaterial to reference the texture groups that show up on the +#define TEXTURE_GROUP_LIGHTMAP "Lightmaps" +#define TEXTURE_GROUP_WORLD "World textures" +#define TEXTURE_GROUP_MODEL "Model textures" +#define TEXTURE_GROUP_VGUI "VGUI textures" +#define TEXTURE_GROUP_PARTICLE "Particle textures" +#define TEXTURE_GROUP_DECAL "Decal textures" +#define TEXTURE_GROUP_SKYBOX "SkyBox textures" +#define TEXTURE_GROUP_CLIENT_EFFECTS "ClientEffect textures" +#define TEXTURE_GROUP_OTHER "Other textures" +#define TEXTURE_GROUP_PRECACHED "Precached" // TODO: assign texture groups to the precached materials +#define TEXTURE_GROUP_CUBE_MAP "CubeMap textures" +#define TEXTURE_GROUP_RENDER_TARGET "RenderTargets" +#define TEXTURE_GROUP_UNACCOUNTED "Unaccounted textures" // Textures that weren't assigned a texture group. +//#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER "Static Vertex" +#define TEXTURE_GROUP_STATIC_INDEX_BUFFER "Static Indices" +#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER_DISP "Displacement Verts" +#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER_COLOR "Lighting Verts" +#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER_WORLD "World Verts" +#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER_MODELS "Model Verts" +#define TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER "Other Verts" +#define TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER "Dynamic Indices" +#define TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER "Dynamic Verts" +#define TEXTURE_GROUP_DEPTH_BUFFER "DepthBuffer" +#define TEXTURE_GROUP_VIEW_MODEL "ViewModel" +#define TEXTURE_GROUP_PIXEL_SHADERS "Pixel Shaders" +#define TEXTURE_GROUP_VERTEX_SHADERS "Vertex Shaders" +#define TEXTURE_GROUP_RENDER_TARGET_SURFACE "RenderTarget Surfaces" +#define TEXTURE_GROUP_MORPH_TARGETS "Morph Targets" + +#endif // TEXTURE_GROUP_NAMES_H diff --git a/public/tier0/EventMasks.h b/public/tier0/EventMasks.h new file mode 100644 index 0000000..4774809 --- /dev/null +++ b/public/tier0/EventMasks.h @@ -0,0 +1,480 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#pragma once + +typedef union EVENT_MASK(TC_deliver_mode) +{ + struct + { + uint16 DD:1; // both logical processors in deliver mode }, + uint16 DB:1; // logical processor 0 in deliver mode, 1 in build mode }, + uint16 DI:1; // logical processor 0 in deliver mode, 1 is inactive }, + uint16 BD:1; // logical processor 0 in build mode, 1 in deliver mode }, + uint16 BB:1; // both logical processors in build mode }, + uint16 BI:1; // logical processor 0 in build mode, 1 is inactive }, + uint16 ID:1; // logical processor 0 is inactive, 1 in deliver mode }, + uint16 IB:1; // logical processor 0 is inactive, 1 in build mode } + }; + uint16 flat; +} EVENT_MASK(TC_deliver_mode); + +typedef union EVENT_MASK(BPU_fetch_request) +{ + + struct + { + uint16 TCMISS:1; // Trace cache lookup miss }, + }; + uint16 flat; +} EVENT_MASK(BPU_fetch_request); + + +typedef union EVENT_MASK(ITLB_reference) +{ + struct + { + uint16 HIT : 1; //ITLB hit }, + uint16 MISS : 1;//ITLB miss }, + uint16 HIT_UC :1; // Uncacheable ITLB hit } + }; + uint16 flat; +} EVENT_MASK(ITLB_reference); + +typedef union EVENT_MASK(memory_cancel) +{ + struct + { + uint16 dummy : 2; + + uint16 ST_RB_FULL:1; //Replayed because no store request buffer is available }, + uint16 _64K_CONF:1; //Conflicts due to 64K aliasing } + }; + uint16 flat; +}EVENT_MASK(memory_cancel); + +typedef union EVENT_MASK(memory_complete) +{ + struct + { + uint16 LSC:1; // Load split completed, excluding UC/WC loads }, + uint16 SSC:1; //Any split stores completed } } + }; + uint16 flat; +} EVENT_MASK(memory_complete); + +typedef union EVENT_MASK(load_port_replay) +{ + struct + { + uint16 dummy:1; + uint16 SPLIT_LD:1; //Split load } } + }; + uint16 flat; +} EVENT_MASK(load_port_replay); + +typedef union EVENT_MASK(store_port_replay) +{ + struct + { + uint16 dummy0:1; + uint16 SPLIT_ST:1; //Split store } } + + }; + uint16 flat; +} EVENT_MASK(store_port_replay); + +typedef union EVENT_MASK(MOB_load_replay) +{ + struct + { + uint16 dummy0:1; + + uint16 NO_STA:1; //Replayed because of unknown store address }, + + uint16 dummy2:1; + + uint16 NO_STD:1; //Replayed because of unknown store data }, + uint16 PARTIAL_DATA:1; //Replayed because of partially overlapped data access between the load and store operations }, + uint16 UNALGN_ADDR:1; //Replayed because the lower 4 bits of the linear address do not match between the load and store operations } } + }; + uint16 flat; +}EVENT_MASK(MOB_load_replay); + +typedef union EVENT_MASK(page_walk_type) +{ + struct + { + uint16 DTMISS:1; // Page walk for a data TLB miss }, + uint16 ITMISS:1; // Page walk for an instruction TLB miss } } + }; + uint16 flat; +}EVENT_MASK(page_walk_type); + + +typedef union EVENT_MASK(BSQ_cache_reference) +{ + struct + { + uint16 RD_2ndL_HITS:1; // Read 2nd level cache hit Shared }, + uint16 RD_2ndL_HITE:1; // Read 2nd level cache hit Exclusive }, + uint16 RD_2ndL_HITM:1; // Read 2nd level cache hit Modified }, + uint16 RD_3rdL_HITS:1; // Read 3rd level cache hit Shared }, + uint16 RD_3rdL_HITE:1; // Read 3rd level cache hit Exclusive }, + uint16 RD_3rdL_HITM:1; // Read 3rd level cache hit Modified }, + uint16 dummy6:1; + uint16 dummy7:1; + uint16 RD_2ndL_MISS:1; // Read 2nd level cache miss }, + uint16 RD_3rdL_MISS:1; // Read 3rd level cache miss }, + uint16 WR_2ndL_MISS:1; // Writeback lookup from DAC misses the 2nd level cache } } + }; + uint16 flat; +} EVENT_MASK(BSQ_cache_reference) ; + +typedef union EVENT_MASK(IOQ) +{ + struct + { + uint16 bit0:1; // bus request type (use 00001 for invalid or default) + uint16 bit1:1; // + uint16 bit2:1; // + uint16 bit3:1; // + uint16 bit4:1; // + uint16 ALL_READ:1; // Count read entries }, + uint16 ALL_WRITE:1; // Count write entries }, + uint16 MEM_UC:1; // Count UC memory access entries }, + uint16 MEM_WC:1; // Count WC memory access entries }, + uint16 MEM_WT:1; // Count WT memory access entries }, + uint16 MEM_WP:1; // Count WP memory access entries }, + uint16 MEM_WB:1; // Count WB memory access entries }, + uint16 dummy12:1; + + uint16 OWN:1; // Count own store requests }, + uint16 OTHER:1; // Count other and DMA store requests }, + uint16 PREFETCH:1; // Include HW and SW prefetch requests } } + }; + uint16 flat; +} EVENT_MASK(IOQ) ; + +typedef union EVENT_MASK(FSB_data_activity) +{ + struct + { + /* DRDY_OWN is mutually exclusive with DRDY_OTHER */ + /* DBSY_OWN is mutually exclusive with DBSY_OTHER */ + uint16 DRDY_DRV:1; // Count when this processor drives data onto the bus }, + uint16 DRDY_OWN:1; // Count when this processor reads data from the bus }, + uint16 DRDY_OTHER:1; // Count when data is on the bus but not being sampled by the processor }, + uint16 DBSY_DRV:1; // Count when this processor reserves the bus for driving data }, + uint16 DBSY_OWN:1; // Count when this processor reserves the bus for sampling data }, + uint16 DBSY_OTHER:1; // Count when the bus is reserved for driving data this processor will not sample } } + }; + uint16 flat; +}EVENT_MASK(FSB_data_activity); + +typedef union EVENT_MASK(BSQ) +{ + struct + { + uint16 REQ_TYPE0:1; // Request type encoding bit 0 }, + uint16 REQ_TYPE1:1; // Request type encoding bit 1 }, + uint16 REQ_LEN0:1; // Request length encoding bit 0 }, + uint16 REQ_LEN1:1; // Request length encoding bit 1 }, + uint16 dummy4: 1; + uint16 REQ_IO_TYPE:1; // Request type is input or output }, + uint16 REQ_LOCK_TYPE:1; // Request type is bus lock }, + uint16 REQ_CACHE_TYPE:1; // Request type is cacheable }, + uint16 REQ_SPLIT_TYPE:1; // Request type is a bus 8-byte chunk split across 8-byte boundary }, + uint16 REQ_DEM_TYPE:1; // Request type is a demand (1) or prefetch (0) }, + uint16 REQ_ORD_TYPE:1; // Request is an ordered type }, + uint16 MEM_TYPE0:1; // Memory type encoding bit 0 }, + uint16 MEM_TYPE1:1; // Memory type encoding bit 1 }, + uint16 MEM_TYPE2:1; // Memory type encoding bit 2 } } + }; + uint16 flat; +} EVENT_MASK(BSQ); + +typedef union EVENT_MASK(firm_uop) +{ + struct + { + uint16 dummy15 : 15; + uint16 ALL:1; // count all uops of this type } } + }; + uint16 flat; +} EVENT_MASK(firm_uop); + + + +typedef union EVENT_MASK(TC_misc) +{ + struct + { + uint16 dymmy4 : 4; + uint16 FLUSH:1; // Number of flushes } } + }; + uint16 flat; +} EVENT_MASK(TC_misc); + +typedef union EVENT_MASK(global_power_events) +{ + struct + { + uint16 Running:1; // The processor is active } } + }; + uint16 flat; +} EVENT_MASK(global_power_events); + +typedef union EVENT_MASK(tc_ms_xfer) +{ + struct + { + uint16 CISC:1; // A TC to MS transfer ocurred } } + }; + uint16 flat; +}EVENT_MASK(tc_ms_xfer); + + + +typedef union EVENT_MASK(uop_queue_writes) +{ + struct + { + uint16 FROM_TC_BUILD:1; // uops written from TC build mode + uint16 FROM_TC_DELIVER:1; // uops written from TC deliver mode + uint16 FROM_ROM:1; // uops written from microcode ROM } } + }; + uint16 flat; +} EVENT_MASK(uop_queue_writes); + +typedef union EVENT_MASK(branch_type) +{ + struct + { + uint16 dummy : 1; + uint16 CONDITIONAL:1; // Conditional jumps + uint16 CALL:1; // Direct or indirect call + uint16 RETURN:1; // Return branches + uint16 INDIRECT:1; // Returns, indirect calls, or indirect jumps + }; + uint16 flat; +} EVENT_MASK(branch_type); + + + +typedef union EVENT_MASK(resource_stall) +{ + struct + { + uint16 dummy1 : 5; + uint16 SBFULL:1; // A Stall due to lack of store buffers } } + }; + uint16 flat; +} EVENT_MASK(resource_stall); + + + + +typedef union EVENT_MASK(WC_Buffer) +{ + struct + { + uint16 WCB_EVICTS : 1; // all causes }, + uint16 WCB_FULL_EVICT : 1; // no WC buffer is available }, + /* XXX: 245472-011 no longer lists bit 2, but that looks like + a table formatting error. Keeping it for now. */ + uint16 WCB_HITM_EVICT : 1; // store encountered a Hit Modified condition } } + }; + uint16 flat; +} EVENT_MASK(WC_Buffer); + + +typedef union EVENT_MASK(b2b_cycles) +{ + struct + { + uint16 dummy0 : 1; + uint16 bit1 : 1; // + uint16 bit2 : 1; // + uint16 bit3 : 1; // + uint16 bit4 : 1; // + uint16 bit5 : 1; // + uint16 bit6 : 1; // + + }; + uint16 flat; +} EVENT_MASK(b2b_cycles); + +typedef union EVENT_MASK(bnr) +{ + struct + { + uint16 bit0:1; // + uint16 bit1:1; // + uint16 bit2:1; // + }; + uint16 flat; +} EVENT_MASK(bnr); + + +typedef union EVENT_MASK(snoop) +{ + struct + { + uint16 dummy0 : 1; + uint16 dummy1 : 1; + + uint16 bit2:1; // + uint16 dummy3:1; // + uint16 dummy4:1; // + uint16 dummy5:1; // + uint16 bit6:1; // + uint16 bit7:1; // + }; + uint16 flat; +} EVENT_MASK(snoop); + + +typedef union EVENT_MASK(response) +{ + struct + { + uint16 dummy0:1; // + uint16 bit1:1; // + uint16 bit2:1; // + uint16 dummy3:1; // + uint16 dummy4:1; // + uint16 dummy5:1; // + uint16 dummy6:1; // + uint16 dummy7:1; // + uint16 bit8:1; // + uint16 bit9:1; // + }; + uint16 flat; +} EVENT_MASK(response); + + +typedef union EVENT_MASK(nbogus_bogus) +{ + struct + { + uint16 NBOGUS:1; // The marked uops are not bogus + uint16 BOGUS:1; // The marked uops are bogus + }; + uint16 flat; +} EVENT_MASK(nbogus_bogus); + + +typedef union EVENT_MASK(execution_event) +{ + struct + { + uint16 NBOGUS0:1; // non-bogus uops with tag bit 0 set }, + uint16 NBOGUS1:1; // non-bogus uops with tag bit 1 set }, + uint16 NBOGUS2:1; // non-bogus uops with tag bit 2 set }, + uint16 NBOGUS3:1; // non-bogus uops with tag bit 3 set }, + uint16 BOGUS0:1; // bogus uops with tag bit 0 set }, + uint16 BOGUS1:1; // bogus uops with tag bit 1 set }, + uint16 BOGUS2:1; // bogus uops with tag bit 2 set }, + uint16 BOGUS3:1; // bogus uops with tag bit 3 set } } + }; + uint16 flat; +}EVENT_MASK(execution_event); + +typedef union EVENT_MASK(instr_retired) +{ + struct + { + uint16 NBOGUSNTAG:1; // Non-bogus instructions that are not tagged }, + uint16 NBOGUSTAG:1; // Non-bogus instructions that are tagged }, + uint16 BOGUSNTAG:1; // Bogus instructions that are not tagged }, + uint16 BOGUSTAG:1; // Bogus instructions that are tagged } } + }; + uint16 flat; +} EVENT_MASK(instr_retired); + + +typedef union EVENT_MASK(uop_type) +{ + struct + { + uint16 dummy0 : 1; + uint16 TAGLOADS:1; // The uop is a load operation }, + uint16 TAGSTORES:1; // The uop is a store operation } } + }; + uint16 flat; +} EVENT_MASK(uop_type); + +typedef union EVENT_MASK(branch_retired) +{ + struct + { + uint16 MMNP:1; // Branch Not-taken Predicted + uint16 MMNM:1; // Branch Not-taken Mispredicted + uint16 MMTP:1; // Branch Taken Predicted + uint16 MMTM:1; // Branch Taken Mispredicted + }; + uint16 flat; +} EVENT_MASK(branch_retired); + +typedef union EVENT_MASK(mispred_branch_retired) +{ + struct + { + uint16 NBOGUS:1; // The retired branch is not bogus } } + }; + uint16 flat; +} EVENT_MASK(mispred_branch_retired); + + +typedef union EVENT_MASK(x87_assist) +{ + struct + { + uint16 FPSU:1; // FP stack underflow }, + uint16 FPSO:1; // FP stack overflow }, + uint16 POAO:1; // x87 output overflow }, + uint16 POAU:1; // x87 output underflow }, + uint16 PREA:1; // x87 input assist } } + }; + uint16 flat; +}EVENT_MASK(x87_assist); + +typedef union EVENT_MASK(machine_clear) +{ + struct + { + uint16 CLEAR:1; // Count a portion of the cycles when the machine is cleared }, + uint16 dummy1: 1; + uint16 MOCLEAR:1; // Count clears due to memory ordering issues }, + uint16 dummy3: 1; + uint16 dummy4: 1; + uint16 dummy5: 1; + + uint16 SMCLEAR:1;// Count clears due to self-modifying code issues } } + }; + uint16 flat; +} EVENT_MASK(machine_clear); + + +typedef union EVENT_MASK(x87_SIMD_moves_uop) +{ + struct + { + uint16 dummy3:3; + uint16 ALLP0:1; // Count all x87/SIMD store/move uops }, + uint16 ALLP2:1; // count all x87/SIMD load uops } } + }; + uint16 flat; +} EVENT_MASK(x87_SIMD_moves_uop); + + + + + + + diff --git a/public/tier0/EventModes.h b/public/tier0/EventModes.h new file mode 100644 index 0000000..05e5b21 --- /dev/null +++ b/public/tier0/EventModes.h @@ -0,0 +1,1787 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef EVENTMODES_H +#define EVENTMODES_H + + +#pragma once + +/* + + + + Event Modes to choose from: + + P4Event_TC_deliver_mode + + P4Event_BPU_fetch_request + + P4Event_ITLB_reference + + P4Event_memory_cancel + + P4Event_memory_complete + + P4Event_load_port_replay + + P4Event_store_port_replay + + P4Event_MOB_load_replay + + P4Event_page_walk_type + + P4Event_BSQ_cache_reference + + P4Event_IOQ_allocation + + P4Event_IOQ_active_entries + + P4Event_FSB_data_activity + + P4Event_BSQ_allocation + + P4Event_BSQ_active_entries + + P4Event_SSE_input_assist + + P4Event_packed_SP_uop + + P4Event_packed_DP_uop + + P4Event_scalar_SP_uop + + P4Event_scalar_DP_uop + + P4Event_64bit_MMX_uop + + P4Event_128bit_MMX_uop + + P4Event_x87_FP_uop + + P4Event_x87_SIMD_moves_uop + + P4Event_TC_misc + + P4Event_global_power_events + + P4Event_tc_ms_xfer + + P4Event_uop_queue_writes + + P4Event_retired_mispred_branch_type + + P4Event_retired_branch_type + + P4Event_resource_stall + + P4Event_WC_Buffer + + P4Event_b2b_cycles + + P4Event_bnr + + P4Event_snoop + + P4Event_response + + P4Event_front_end_event + + P4Event_execution_event + + P4Event_replay_event + + P4Event_instr_retired + + P4Event_uops_retired + + P4Event_uop_type + + P4Event_branch_retired + + P4Event_mispred_branch_retired + + P4Event_x87_assist + + P4Event_machine_clear + + +*/ + + + +class P4P4Event_TC_deliver_mode: public P4BaseEvent +{ +public: + EVENT_MASK(TC_deliver_mode) * eventMask; + + P4P4Event_TC_deliver_mode() + { + eventMask = (EVENT_MASK(TC_deliver_mode) *)&m_eventMask; + + escr.ESCREventSelect = 0x01; + cccr.CCCRSelect = 0x01; + //// eventType = EVENT_TYPE(TC_deliver_mode); + description = _T("TC_deliver_mode"); + UseCounter4(); + } + + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; + +class P4P4Event_BPU_fetch_request: public P4BaseEvent + +{ +public: + EVENT_MASK(BPU_fetch_request)* eventMask; + + P4P4Event_BPU_fetch_request() + { + eventMask = (EVENT_MASK(BPU_fetch_request) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x00; + // eventType = EVENT_TYPE(BPU_fetch_request); + description=_T("BPU_fetch_request"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } + +}; +class P4P4Event_ITLB_reference: public P4BaseEvent + +{ +public: + EVENT_MASK(ITLB_reference) * eventMask; + + P4P4Event_ITLB_reference() + { + eventMask = (EVENT_MASK(ITLB_reference) *)&m_eventMask; + + escr.ESCREventSelect= 0x18; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(ITLB_reference); + description=_T("ITLB_reference"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_memory_cancel: public P4BaseEvent + +{ +public: + EVENT_MASK(memory_cancel) * eventMask; + + P4Event_memory_cancel() + { + eventMask = (EVENT_MASK(memory_cancel) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(memory_cancel); + description=_T("memory_cancel"); + UseCounter8(); + } + + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_memory_complete: public P4BaseEvent + +{ +public: + EVENT_MASK(memory_complete) * eventMask; + + P4Event_memory_complete() + { + eventMask = (EVENT_MASK(memory_complete) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(memory_complete); + description=_T("memory_complete"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_load_port_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(load_port_replay) * eventMask; + + P4Event_load_port_replay() + { + eventMask = (EVENT_MASK(load_port_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(load_port_replay); + description=_T("load_port_replay"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_store_port_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(store_port_replay) * eventMask; + + P4Event_store_port_replay() + { + eventMask = (EVENT_MASK(store_port_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(store_port_replay); + description=_T("store_port_replay"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_MOB_load_replay: public P4BaseEvent + +{ +public: + EVENT_MASK(MOB_load_replay) * eventMask; + + P4Event_MOB_load_replay() + { + eventMask = (EVENT_MASK(MOB_load_replay) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(MOB_load_replay); + description=_T("MOB_load_replay"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_page_walk_type: public P4BaseEvent + +{ +public: + EVENT_MASK(page_walk_type) * eventMask; + + P4Event_page_walk_type() + { + eventMask = (EVENT_MASK(page_walk_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(page_walk_type); + description=_T("page_walk_type"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_BSQ_cache_reference: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ_cache_reference) * eventMask; + + P4Event_BSQ_cache_reference() + { + eventMask = (EVENT_MASK(BSQ_cache_reference) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ_cache_reference); + description=_T("BSQ_cache_reference"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_IOQ_allocation: public P4BaseEvent + +{ +public: + EVENT_MASK(IOQ) * eventMask; + + P4Event_IOQ_allocation() + { + eventMask = (EVENT_MASK(IOQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(IOQ); + description=_T("IOQ_allocation"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_IOQ_active_entries: public P4BaseEvent + +{ +public: + EVENT_MASK(IOQ) * eventMask; + + P4Event_IOQ_active_entries() + { + eventMask = (EVENT_MASK(IOQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x1A; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(IOQ); + description=_T("IOQ_active_entries"); + UseCounter2(); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_FSB_data_activity: public P4BaseEvent + +{ +public: + EVENT_MASK(FSB_data_activity) * eventMask; + + P4Event_FSB_data_activity() + { + eventMask = (EVENT_MASK(FSB_data_activity) *)&m_eventMask; + + escr.ESCREventSelect= 0x17; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(FSB_data_activity); + description=_T("FSB_data_activity"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_BSQ_allocation: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ) * eventMask; + + P4Event_BSQ_allocation() + { + eventMask = (EVENT_MASK(BSQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ); + description=_T("BSQ_allocation"); + UseCounter0(); + } + + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + + } +}; +class P4Event_BSQ_active_entries: public P4BaseEvent + +{ +public: + EVENT_MASK(BSQ) * eventMask; + + P4Event_BSQ_active_entries() + { + eventMask = (EVENT_MASK(BSQ) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x07; + // eventType=EVENT_TYPE(BSQ); + description=_T("bsq_active_entries"); + UseCounter2(); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_SSE_input_assist: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_SSE_input_assist() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x34; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("SSE_input_assist"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_packed_SP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_packed_SP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("packed_SP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_packed_DP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_packed_DP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("packed_DP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_scalar_SP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_scalar_SP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0A; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("scalar_SP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_scalar_DP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_scalar_DP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x0E; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("scalar_DP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_64bit_MMX_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_64bit_MMX_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("64bit_MMX_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_128bit_MMX_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_128bit_MMX_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x1A; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("128bit_MMX_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_x87_FP_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(firm_uop) * eventMask; + + P4Event_x87_FP_uop() + { + eventMask = (EVENT_MASK(firm_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(firm_uop); + description=_T("x87_FP_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_x87_SIMD_moves_uop: public P4BaseEvent + +{ +public: + EVENT_MASK(x87_SIMD_moves_uop) * eventMask; + + P4Event_x87_SIMD_moves_uop() + { + eventMask = (EVENT_MASK(x87_SIMD_moves_uop) *)&m_eventMask; + + escr.ESCREventSelect= 0x2E; + cccr.CCCRSelect= 0; + // eventType=EVENT_TYPE(x87_SIMD_moves_uop); + description=_T("x87_SIMD_moves_uop"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_TC_misc: public P4BaseEvent + +{ +public: + EVENT_MASK(TC_misc) * eventMask; + + P4Event_TC_misc() + { + eventMask = (EVENT_MASK(TC_misc) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x01; + // eventType=EVENT_TYPE(TC_misc); + description=_T("TC_misc"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_global_power_events: public P4BaseEvent + +{ +public: + EVENT_MASK(global_power_events) * eventMask; + + P4Event_global_power_events() + { + eventMask = (EVENT_MASK(global_power_events) *)&m_eventMask; + + escr.ESCREventSelect= 0x13; + cccr.CCCRSelect= 0x06; + // eventType=EVENT_TYPE(global_power_events); + description=_T("global_power_events"); + UseCounter0(); + } + + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_tc_ms_xfer: public P4BaseEvent + +{ +public: + EVENT_MASK(tc_ms_xfer) * eventMask; + + P4Event_tc_ms_xfer() + { + eventMask = (EVENT_MASK(tc_ms_xfer) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x00; + // eventType=EVENT_TYPE(tc_ms_xfer); + description=_T("tc_ms_xfer"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_uop_queue_writes: public P4BaseEvent + +{ +public: + EVENT_MASK(uop_queue_writes) * eventMask; + + P4Event_uop_queue_writes() + { + eventMask = (EVENT_MASK(uop_queue_writes) *)&m_eventMask; + + escr.ESCREventSelect= 0x09; + cccr.CCCRSelect= 0x00; + // eventType=EVENT_TYPE(uop_queue_writes); + description=_T("uop_queue_writes"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_retired_mispred_branch_type: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_type) * eventMask; + + P4Event_retired_mispred_branch_type() + { + eventMask = (EVENT_MASK(branch_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(branch_type); + description=_T("retired_mispred_branch_type"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_retired_branch_type: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_type) * eventMask; + + P4Event_retired_branch_type() + { + eventMask = (EVENT_MASK(branch_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(branch_type); + description=_T("retired_branch_type"); + UseCounter4(); + } + + void UseCounter4() + { + SetCounter(4);; + } + void UseCounter5() + { + SetCounter(5); + } + void UseCounter6() + { + SetCounter(6); + } + void UseCounter7() + { + SetCounter(7); + } +}; +class P4Event_resource_stall: public P4BaseEvent + +{ +public: + EVENT_MASK(resource_stall) * eventMask; + + P4Event_resource_stall() + { + eventMask = (EVENT_MASK(resource_stall) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(resource_stall); + description=_T("resource_stall"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } + +}; +class P4Event_WC_Buffer: public P4BaseEvent + +{ +public: + EVENT_MASK(WC_Buffer) * eventMask; + + P4Event_WC_Buffer() + { + eventMask = (EVENT_MASK(WC_Buffer) *)&m_eventMask; + + escr.ESCREventSelect= 0x05; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(WC_Buffer); + description=_T("WC_Buffer"); + UseCounter8(); + } + void UseCounter8() + { + SetCounter(8); + } + void UseCounter9() + { + SetCounter(9); + } + void UseCounter10() + { + SetCounter(10); + } + void UseCounter11() + { + SetCounter(11); + } + +}; +class P4Event_b2b_cycles: public P4BaseEvent + +{ +public: + EVENT_MASK(b2b_cycles) * eventMask; + + P4Event_b2b_cycles() + { + eventMask = (EVENT_MASK(b2b_cycles) *)&m_eventMask; + + escr.ESCREventSelect= 0x16; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(b2b_cycles); + description=_T("b2b_cycles"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_bnr: public P4BaseEvent + +{ +public: + EVENT_MASK(bnr) * eventMask; + + P4Event_bnr() + { + eventMask = (EVENT_MASK(bnr) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(bnr); + description=_T("bnr"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_snoop: public P4BaseEvent + +{ +public: + EVENT_MASK(snoop) * eventMask; + + P4Event_snoop() + { + eventMask = (EVENT_MASK(snoop) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(snoop); + description=_T("snoop"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_response: public P4BaseEvent + +{ +public: + EVENT_MASK(response) * eventMask; + + P4Event_response() + { + eventMask = (EVENT_MASK(response) *)&m_eventMask; + + escr.ESCREventSelect= 0x04; + cccr.CCCRSelect= 0x03; + // eventType=EVENT_TYPE(response); + description=_T("response"); + UseCounter0(); + } + void UseCounter0() + { + SetCounter(0); + } + void UseCounter1() + { + SetCounter(1); + } + void UseCounter2() + { + SetCounter(2); + } + void UseCounter3() + { + SetCounter(3); + } +}; +class P4Event_front_end_event: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_front_end_event() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x08; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("front_end_event"); + UseCounter12(); + } + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_execution_event: public P4BaseEvent + +{ +public: + EVENT_MASK(execution_event) * eventMask; + + P4Event_execution_event() + { + eventMask = (EVENT_MASK(execution_event) *)&m_eventMask; + + escr.ESCREventSelect= 0x0C; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(execution_event); + description=_T("execution_event"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_replay_event: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_replay_event() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x09; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("replay_event"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_instr_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(instr_retired) * eventMask; + + P4Event_instr_retired() + { + eventMask = (EVENT_MASK(instr_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(instr_retired); + description=_T("instr_retired"); + UseCounter12(); + } + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_uops_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(nbogus_bogus) * eventMask; + + P4Event_uops_retired() + { + eventMask = (EVENT_MASK(nbogus_bogus) *)&m_eventMask; + + escr.ESCREventSelect= 0x01; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(nbogus_bogus); + description=_T("uops_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_uop_type: public P4BaseEvent + +{ +public: + EVENT_MASK(uop_type) * eventMask; + + P4Event_uop_type() + { + eventMask = (EVENT_MASK(uop_type) *)&m_eventMask; + + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x02; + // eventType=EVENT_TYPE(uop_type); + description=_T("uop_type"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_branch_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(branch_retired) * eventMask; + + P4Event_branch_retired() + { + eventMask = (EVENT_MASK(branch_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x06; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(branch_retired); + description=_T("branch_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_mispred_branch_retired: public P4BaseEvent + +{ +public: + EVENT_MASK(mispred_branch_retired) * eventMask; + + P4Event_mispred_branch_retired() + { + eventMask = (EVENT_MASK(mispred_branch_retired) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x04; + // eventType=EVENT_TYPE(mispred_branch_retired); + description=_T("mispred_branch_retired"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_x87_assist: public P4BaseEvent + +{ +public: + EVENT_MASK(x87_assist) * eventMask; + + P4Event_x87_assist() + { + eventMask = (EVENT_MASK(x87_assist) *)&m_eventMask; + + escr.ESCREventSelect= 0x03; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(x87_assist); + description=_T("x87_assist"); + UseCounter12(); + } + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } +}; +class P4Event_machine_clear: public P4BaseEvent + +{ +public: + EVENT_MASK(machine_clear) * eventMask; + + P4Event_machine_clear() + { + eventMask = (EVENT_MASK(machine_clear) *)&m_eventMask; + escr.ESCREventSelect= 0x02; + cccr.CCCRSelect= 0x05; + // eventType=EVENT_TYPE(machine_clear); + description=_T("machine_clear"); + UseCounter12(); + } + + + + void UseCounter12() + { + SetCounter(12); + } + void UseCounter13() + { + SetCounter(13); + + } + + void UseCounter14() + { + SetCounter(14); + } + void UseCounter15() + { + SetCounter(15); + + } + + void UseCounter16() + { + SetCounter(16); + } + void UseCounter17() + { + SetCounter(17); + + } + +}; + +#endif // EVENTMODES_H diff --git a/public/tier0/IOCTLCodes.h b/public/tier0/IOCTLCodes.h new file mode 100644 index 0000000..7fc8efb --- /dev/null +++ b/public/tier0/IOCTLCodes.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef IOCTLCODES_H +#define IOCTLCODES_H +#pragma once + +// Define the IOCTL codes we will use. The IOCTL code contains a command +// identifier, plus other information about the device, the type of access +// with which the file must have been opened, and the type of buffering. + +// Device type -- in the "User Defined" range." +#define DEVICE_FILE_TYPE 40000 + + +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. + +#define IOCTL_WRITE_MSR \ + CTL_CODE( DEVICE_FILE_TYPE, 0x900, METHOD_BUFFERED, FILE_READ_ACCESS ) + +#define IOCTL_READ_MSR \ + CTL_CODE( DEVICE_FILE_TYPE, 0x901, METHOD_BUFFERED, FILE_READ_ACCESS ) + + +#endif IOCTLCODES_H diff --git a/public/tier0/K8PerformanceCounters.h b/public/tier0/K8PerformanceCounters.h new file mode 100644 index 0000000..d405a8d --- /dev/null +++ b/public/tier0/K8PerformanceCounters.h @@ -0,0 +1,2040 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#ifndef K8PERFORMANCECOUNTERS_H +#define K8PERFORMANCECOUNTERS_H + +/* + * AMD K8 events. + * + */ + +#ifdef COMPILER_MSVC64 +extern "C" +{ + unsigned __int64 __readpmc(unsigned long); +} + +#pragma intrinsic(__readpmc) +#endif + + +typedef union EVENT_MASK(NULL_MASK) +{ + // no tests defined + uint16 flat; +} EVENT_MASK(NULL_MASK); + + +#define MSR_K8_EVNTSEL0 0xC0010000 /* .. 0xC0010003 */ +#define MSR_K8_PERFCTR0 0xC0010004 /* .. 0xC0010007 */ + +# pragma pack(push, 1) + + + +// access to these bits is through the methods +typedef union PerfEvtSel +{ + struct + { + uint64 EventMask : 8; + + uint64 UnitMask : 8; + uint64 USR : 1; + uint64 OS : 1; + uint64 Edge : 1; + uint64 PC : 1; + uint64 INTAPIC : 1; + uint64 Reserved21 : 1; + uint64 Enable : 1; + uint64 Complement : 1; // aka INV + uint64 Threshold : 8; // aka CounterMask + uint64 Reserver32 : 32; + }; + uint64 flat; + +} PerfEvtSel; + + +enum UnitEncode +{ + FP, + LS, + DC, + BU, + IC, + UE_Unknown, + FR, + NB +}; + +# pragma pack(pop) + +// Turn off the no return value warning in ReadCounter. +#pragma warning( disable : 4035 ) +#define k8NUM_COUNTERS 4 +class k8BaseEvent +{ +public: + + PME * pme; + + PerfEvtSel eventSelect[k8NUM_COUNTERS]; + + unsigned short m_eventMask; + int event_id; + tchar * name; + tchar revRequired; + int eventSelectNum; + UnitEncode unitEncode; + + + void SetCounter(int n) + { + if (n < 0) + n = 0; + else if (n > 3) + n = 3; + eventSelectNum = n; + + } + k8BaseEvent() + { + pme = PME::Instance(); + + for(int i = 0; i< k8NUM_COUNTERS; i++) + { + eventSelect[i].flat = 0; + + } + eventSelectNum = 0; + + m_eventMask = 0; + event_id = 0; + name = 0; + revRequired = 'A'; + + + } + + void SetCaptureMode(PrivilegeCapture priv) + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + StopCounter(); + + switch (priv) + { + case OS_Only: + select.USR = 0; + select.OS = 1; + break; + + case USR_Only: + select.USR = 1; + select.OS = 0; + break; + + case OS_and_USR: + select.USR = 1; + select.OS = 1; + break; + } + + + select.UnitMask = m_eventMask; + select.EventMask = event_id; + + + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + pme->WriteMSR(selectPort, select.flat); + } + + + void SetFiltering(CompareState compareEnable, + CompareMethod compareMethod, + uint8 threshold, + EdgeState edgeEnable) + { + + PerfEvtSel & select = eventSelect[eventSelectNum]; + + StopCounter(); + + if (compareEnable == CompareDisable) + select.Threshold = 0; + else + select.Threshold = threshold; + + select.Complement = compareMethod; + + select.Edge = edgeEnable; + + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + pme->WriteMSR(selectPort, select.flat); + + + } + + + void StartCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + + select.Enable = 1; + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + + pme->WriteMSR(selectPort, select.flat); + + } + void StopCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + select.Enable = 0; + int selectPort = MSR_K8_EVNTSEL0 + eventSelectNum; + + pme->WriteMSR(selectPort, select.flat); + } + + + + void ClearCounter() + { + PerfEvtSel & select = eventSelect[eventSelectNum]; + + int counterPort = MSR_K8_PERFCTR0 + eventSelectNum; + + pme->WriteMSR(counterPort, 0ui64 ); // clear + } + + void WriteCounter(int64 value) + { + + PerfEvtSel & select = eventSelect[eventSelectNum]; + + int counterPort = MSR_K8_PERFCTR0 + eventSelectNum; + pme->WriteMSR(counterPort, value); // clear + } + + int64 ReadCounter() + { + +#if PME_DEBUG + PerfEvtSel & select = eventSelect[eventSelectNum]; + + if (select.USR == 0 && select.OS == 0) + return -1; // no area to collect, use SetCaptureMode + + if (select.EventMask == 0) + return -2; // no event mask set + + if (eventSelectNum < 0 || eventSelectNum > 3) + return -3; // counter not legal + + // check revision + +#endif + + // ReadMSR should work here too, but RDPMC should be faster + //ReadMSR(counterPort, int64); + + // we need to copy this into a temp for some reason +#ifdef COMPILER_MSVC64 + return __readpmc((unsigned long) eventSelectNum); +#else + int temp = eventSelectNum; + _asm + { + mov ecx, temp + RDPMC + } +#endif + + } + + +}; +#pragma warning( default : 4035 ) + + + + +typedef union EVENT_MASK(k8_dispatched_fpu_ops) +{ + // event 0 + struct + { + uint16 AddPipeOps:1; // Add pipe ops excluding junk ops" }, + uint16 MulPipeOps:1; // Multiply pipe ops excluding junk ops" },, + uint16 StoreOps:1; // Store pipe ops excluding junk ops" }, + uint16 AndPipeOpsJunk:1; // Add pipe junk ops" },, + uint16 MulPipeOpsJunk:1; // Multiply pipe junk ops" }, + uint16 StoreOpsJunk:1; // Store pipe junk ops" } } + }; + uint16 flat; +} EVENT_MASK(k8_dispatched_fpu_ops); + +class k8Event_DISPATCHED_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_DISPATCHED_FPU_OPS() + { + eventMask = (EVENT_MASK(k8_dispatched_fpu_ops) *)&m_eventMask; + + event_id = 0x00; + unitEncode = FP; + name = _T("Dispatched FPU ops"); + revRequired = 'B'; + } + EVENT_MASK(k8_dispatched_fpu_ops) * eventMask; + +}; + +////////////////////////////////////////////////////////// + + + +class k8Event_NO_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_NO_FPU_OPS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x01; + unitEncode = FP; + + name = _T("Cycles with no FPU ops retired"); + revRequired = 'B'; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +////////////////////////////////////////////////////////// + +class k8Event_FAST_FPU_OPS : public k8BaseEvent +{ +public: + + k8Event_FAST_FPU_OPS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x02; + unitEncode = FP; + + name = _T("Dispatched FPU ops that use the fast flag interface"); + revRequired = 'B'; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +////////////////////////////////////////////////////////// + + +typedef union EVENT_MASK(k8_segment_register_load) +{ + + struct + { + uint16 ES:1; + uint16 CS:1; + uint16 SS:1; + uint16 DS:1; + uint16 FS:1; + uint16 GS:1; + uint16 HS:1; + }; + uint16 flat; +} EVENT_MASK(k8_segment_register_load); + + +class k8Event_SEG_REG_LOAD : public k8BaseEvent +{ +public: + + k8Event_SEG_REG_LOAD() + { + eventMask = (EVENT_MASK(k8_segment_register_load) *)&m_eventMask; + name = _T("Segment register load"); + event_id = 0x20; + unitEncode = LS; + + } + EVENT_MASK(k8_segment_register_load) * eventMask; + +}; + + +class k8Event_SELF_MODIFY_RESYNC : public k8BaseEvent +{ +public: + + k8Event_SELF_MODIFY_RESYNC() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural resync caused by self modifying code"); + event_id = 0x21; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; +class k8Event_LS_RESYNC_BY_SNOOP : public k8BaseEvent +{ +public: + + k8Event_LS_RESYNC_BY_SNOOP() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x22; + unitEncode = LS; + + name = _T("Microarchitectural resync caused by snoop"); + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; +class k8Event_LS_BUFFER_FULL : public k8BaseEvent +{ +public: + + k8Event_LS_BUFFER_FULL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("LS Buffer 2 Full"); + event_id = 0x23; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +typedef union EVENT_MASK(k8_locked_op) +{ + + + struct + { + uint16 NumLockInstr : 1; //Number of lock instructions executed + uint16 NumCyclesInRequestGrant : 1; //Number of cycles spent in the lock request/grant stage + + uint16 NumCyclesForLock:1; + /*Number of cycles a lock takes to complete once it is + non-speculative and is the oldest load/store operation + (non-speculative cycles in Ls2 entry 0)*/ + + + }; + uint16 flat; + + +} EVENT_MASK(k8_locked_op); + + + +class k8Event_LOCKED_OP : public k8BaseEvent +{ +public: + + EVENT_MASK(k8_locked_op) * eventMask; + + k8Event_LOCKED_OP() + { + eventMask = (EVENT_MASK(k8_locked_op) *)&m_eventMask; + name = _T("Locked operation"); + event_id = 0x24; + unitEncode = LS; + + revRequired = 'C'; + } + + +}; + +class k8Event_OP_LATE_CANCEL : public k8BaseEvent +{ +public: + + k8Event_OP_LATE_CANCEL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural late cancel of an operation"); + event_id = 0x25; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("OP_LATE_CANCEL"); + + +}; +class k8Event_CFLUSH_RETIRED : public k8BaseEvent +{ +public: + + k8Event_CFLUSH_RETIRED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired CFLUSH instructions"); + event_id = 0x26; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CFLUSH_RETIRED"); + + +}; +class k8Event_CPUID_RETIRED : public k8BaseEvent +{ +public: + + k8Event_CPUID_RETIRED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired CPUID instructions"); + event_id = 0x27; + unitEncode = LS; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CPUID_RETIRED"); + + +}; + +typedef union EVENT_MASK( k8_cache) +{ + + struct + { + uint16 Invalid:1; + uint16 Exclusive:1; + uint16 Shared:1; + uint16 Owner:1; + uint16 Modified:1; + }; + uint16 flat; + +}EVENT_MASK( k8_cache); + /* 0x40-0x47: from K7 official event set */ + + +class k8Event_DATA_CACHE_ACCESSES : public k8BaseEvent +{ + k8Event_DATA_CACHE_ACCESSES() + { + + event_id = 0x40; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //_T("DATA_CACHE_ACCESSES"), + name = _T("Data cache accesses"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +class k8Event_DATA_CACHE_MISSES : public k8BaseEvent +{ + k8Event_DATA_CACHE_MISSES() + { + + event_id = 0x41; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //_T("DATA_CACHE_MISSES"), + name = _T("Data cache misses"); + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_DATA_CACHE_REFILLS_FROM_L2 : public k8BaseEvent +{ + k8Event_DATA_CACHE_REFILLS_FROM_L2() + { + + event_id = 0x42; + unitEncode = DC; + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + + name = _T("Data cache refills from L2"); + } + EVENT_MASK(k8_cache) * eventMask; + +}; + +class k8Event_DATA_CACHE_REFILLS_FROM_SYSTEM : public k8BaseEvent +{ + k8Event_DATA_CACHE_REFILLS_FROM_SYSTEM() + { + + event_id = 0x43; + unitEncode = DC; + + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + //UM(k7_um_moesi), + //_T("DATA_CACHE_REFILLS_FROM_SYSTEM"), + name = _T("Data cache refills from system"); + } + EVENT_MASK(k8_cache) * eventMask; + +}; + +class k8Event_DATA_CACHE_WRITEBACKS : public k8BaseEvent +{ + k8Event_DATA_CACHE_WRITEBACKS() + { + + event_id = 0x44; + unitEncode = DC; + + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + + //UM(k7_um_moesi), + //_T("DATA_CACHE_WRITEBACKS"), + name = _T("Data cache writebacks"); + } + EVENT_MASK(k8_cache) * eventMask; + + +}; + +class k8Event_L1_DTLB_MISSES_AND_L2_DTLB_HITS : public k8BaseEvent +{ + k8Event_L1_DTLB_MISSES_AND_L2_DTLB_HITS() + { + + event_id = 0x45; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("L1 DTLB misses and L2 DTLB hits"); + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; + +class k8Event_L1_AND_L2_DTLB_MISSES : public k8BaseEvent +{ + k8Event_L1_AND_L2_DTLB_MISSES() + { + + event_id = 0x46; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("L1 and L2 DTLB misses") ; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + +class k8Event_MISALIGNED_DATA_REFERENCES : public k8BaseEvent +{ + k8Event_MISALIGNED_DATA_REFERENCES() + { + + event_id = 0x47; + unitEncode = DC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //NULL, _T("MISALIGNED_DATA_REFERENCES"), + name = _T("Misaligned data references"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + + +class k8Event_ACCESS_CANCEL_LATE : public k8BaseEvent +{ +public: + + k8Event_ACCESS_CANCEL_LATE() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural late cancel of an access"); + event_id = 0x48; + unitEncode = DC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("ACCESS_CANCEL_LATE"); + + +}; +class k8Event_ACCESS_CANCEL_EARLY : public k8BaseEvent +{ +public: + + k8Event_ACCESS_CANCEL_EARLY() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Microarchitectural early cancel of an access"); + event_id = 0x49; + unitEncode = DC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("ACCESS_CANCEL_EARLY"); + + +}; +typedef union EVENT_MASK( k8_ecc) +{ + struct + { + uint16 ScrubberError : 1; // Scrubber error" }, + uint16 PiggybackScrubberErrors : 1; // Piggyback scrubber errors" } } + }; + uint16 flat; + +}EVENT_MASK( k8_ecc); + + +class k8Event_ECC_BIT_ERR : public k8BaseEvent +{ +public: + + k8Event_ECC_BIT_ERR() + { + eventMask = (EVENT_MASK(k8_ecc) *)&m_eventMask; + name = _T("One bit ECC error recorded found by scrubber"); + event_id = 0x4A; + unitEncode = DC; + + } + EVENT_MASK(k8_ecc) * eventMask; + // name = _T("ECC_BIT_ERR"); + + +}; + +// 4B +typedef union EVENT_MASK( k8_distpatch_prefetch_instructions) +{ + struct + { + uint16 Load : 1; + uint16 Store : 1; + uint16 NTA : 1; + }; + uint16 flat; + + +}EVENT_MASK( k8_distpatch_prefetch_instructions); + +class k8Event_DISPATCHED_PRE_INSTRS : public k8BaseEvent +{ +public: + + k8Event_DISPATCHED_PRE_INSTRS() + { + eventMask = (EVENT_MASK(k8_distpatch_prefetch_instructions) *)&m_eventMask; + name = _T("Dispatched prefetch instructions"); + event_id = 0x4B; + unitEncode = DC; + + } + EVENT_MASK(k8_distpatch_prefetch_instructions) * eventMask; + // name = _T("DISPATCHED_PRE_INSTRS"); + + /* 0x4C: added in Revision C */ + +}; + + + +typedef union EVENT_MASK( k8_lock_accesses) +{ + struct + { + uint16 DcacheAccesses:1; // Number of dcache accesses by lock instructions" }, + uint16 DcacheMisses:1; // Number of dcache misses by lock instructions" } } + }; + uint16 flat; + +}EVENT_MASK( k8_lock_accesses); + + + +class k8Event_LOCK_ACCESSES : public k8BaseEvent +{ +public: + + k8Event_LOCK_ACCESSES() + { + eventMask = (EVENT_MASK(k8_lock_accesses) *)&m_eventMask; + name = _T("DCACHE accesses by locks") ; + event_id = 0x4C; + unitEncode = DC; + + revRequired = 'C'; + } + EVENT_MASK(k8_lock_accesses) * eventMask; + + +}; + + +class k8Event_CYCLES_PROCESSOR_IS_RUNNING : public k8BaseEvent +{ +public: + + k8Event_CYCLES_PROCESSOR_IS_RUNNING() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Cycles processor is running (not in HLT or STPCLK)"); + event_id = 0x76; + unitEncode = BU; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("CYCLES_PROCESSOR_IS_RUNNING"); /* undocumented *; + + +}; + + +typedef union EVENT_MASK( k8_internal_L2_request) +{ + struct + { + uint16 ICFill:1; // IC fill" }, + uint16 DCFill:1; // DC fill" }, + uint16 TLBReload:1; // TLB reload" }, + uint16 TagSnoopRequest:1; // Tag snoop request" }, + uint16 CancelledRequest:1; // Cancelled request" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_internal_L2_request); + +class k8Event_BU_INT_L2_REQ : public k8BaseEvent +{ +public: + + k8Event_BU_INT_L2_REQ() + { + eventMask = (EVENT_MASK(k8_internal_L2_request) *)&m_eventMask; + name = _T("Internal L2 request"); + unitEncode = BU; + event_id = 0x7D; + } + + EVENT_MASK(k8_internal_L2_request) * eventMask; +} ; + + // name = _T("BU_INT_L2_REQ"); + + + +// 7E +typedef union EVENT_MASK( k8_fill_request_missed_L2) +{ + + struct + { + uint16 ICFill:1; // IC fill" }, + uint16 DCFill:1; // DC fill" }, + uint16 TLBReload:1; // TLB reload" }, + }; + uint16 flat; + +} EVENT_MASK( k8_fill_request_missed_L2); + + +class k8Event_BU_FILL_REQ : public k8BaseEvent +{ +public: + + k8Event_BU_FILL_REQ() + { + eventMask = (EVENT_MASK(k8_fill_request_missed_L2) *)&m_eventMask; + name = _T("Fill request that missed in L2"); + event_id = 0x7E; + unitEncode = BU; + + } + EVENT_MASK(k8_fill_request_missed_L2) * eventMask; + // name = _T("BU_FILL_REQ"); + + + +}; + + + + +// 7F +typedef union EVENT_MASK( k8_fill_into_L2) +{ + + struct + { + uint16 DirtyL2Victim:1; // Dirty L2 victim + uint16 VictimFromL2:1; // Victim from L2 + }; + uint16 flat; + +}EVENT_MASK( k8_fill_into_L2); + +class k8Event_BU_FILL_L2 : public k8BaseEvent +{ +public: + + k8Event_BU_FILL_L2() + { + eventMask = (EVENT_MASK(k8_fill_into_L2) *)&m_eventMask; + name = _T("Fill into L2"); + event_id = 0x7F; + unitEncode = BU; + + } + EVENT_MASK(k8_fill_into_L2) * eventMask; + // name = _T("BU_FILL_L2"); + + +}; + +class k8Event_INSTRUCTION_CACHE_FETCHES : public k8BaseEvent +{ +public: + k8Event_INSTRUCTION_CACHE_FETCHES() + { + event_id = 0x80; + unitEncode = IC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + name = _T("Instruction cache fetches"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_INSTRUCTION_CACHE_MISSES : public k8BaseEvent +{ +public: + k8Event_INSTRUCTION_CACHE_MISSES() + { + event_id = 0x81; + unitEncode = IC; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //0xF, NULL, _T("INSTRUCTION_CACHE_MISSES"), + name = _T("Instruction cache misses"); + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_IC_REFILL_FROM_L2 : public k8BaseEvent +{ +public: + + k8Event_IC_REFILL_FROM_L2() + { + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + name = _T("Refill from L2"); + event_id = 0x82; + unitEncode = IC; + + } + EVENT_MASK(k8_cache) * eventMask; + // name = _T("IC_REFILL_FROM_L2"); + + + +}; +class k8Event_IC_REFILL_FROM_SYS : public k8BaseEvent +{ +public: + + k8Event_IC_REFILL_FROM_SYS() + { + eventMask = (EVENT_MASK(k8_cache) *)&m_eventMask; + name = _T("Refill from system"); + event_id = 0x83; + unitEncode = IC; + + } + EVENT_MASK(k8_cache) * eventMask; + // name = _T("IC_REFILL_FROM_SYS"); + + + +}; +class k8Event_L1_ITLB_MISSES_AND_L2_ITLB_HITS : public k8BaseEvent +{ +public: + k8Event_L1_ITLB_MISSES_AND_L2_ITLB_HITS() + { + + event_id = 0x84; + unitEncode = IC; + + name = _T("L1 ITLB misses (and L2 ITLB hits)"); + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + + } + EVENT_MASK(NULL_MASK) * eventMask; + + +}; + +class k8Event_L1_AND_L2_ITLB_MISSES : public k8BaseEvent +{ +public: + k8Event_L1_AND_L2_ITLB_MISSES() + { + event_id = 0x85; + unitEncode = IC; + + name = _T("(L1 and) L2 ITLB misses"); + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + + + +class k8Event_IC_RESYNC_BY_SNOOP : public k8BaseEvent +{ +public: + + k8Event_IC_RESYNC_BY_SNOOP() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + event_id = 0x86; + unitEncode = IC; + + name = _T("Microarchitectural resync caused by snoop"); + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_RESYNC_BY_SNOOP"); + /* similar to 0x22; but IC unit instead of LS unit */ + + + +}; +class k8Event_IC_FETCH_STALL : public k8BaseEvent +{ +public: + + k8Event_IC_FETCH_STALL() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Instruction fetch stall"); + event_id = 0x87; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_FETCH_STALL"); + + + +}; +class k8Event_IC_STACK_HIT : public k8BaseEvent +{ +public: + + k8Event_IC_STACK_HIT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Return stack hit"); + event_id = 0x88; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_STACK_HIT"); + + + +}; +class k8Event_IC_STACK_OVERFLOW : public k8BaseEvent +{ +public: + + k8Event_IC_STACK_OVERFLOW() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Return stack overflow"); + event_id = 0x89; + unitEncode = IC; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("IC_STACK_OVERFLOW"); + + + + +}; + + /* 0xC0-0xC7: from K7 official event set */ +class k8Event_RETIRED_INSTRUCTIONS : public k8BaseEvent +{ +public: + k8Event_RETIRED_INSTRUCTIONS() + { + event_id = 0xC0; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + //0xF, NULL, _T("RETIRED_INSTRUCTIONS"), + name = _T("Retired instructions (includes exceptions, interrupts, resyncs)"); + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_RETIRED_OPS : public k8BaseEvent +{ +public: + k8Event_RETIRED_OPS() + { + event_id = 0xC1; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_OPS"), + name = _T("Retired Ops") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_BRANCHES() + { + event_id = 0xC2; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_BRANCHES"), + name = _T("Retired branches (conditional, unconditional, exceptions, interrupts)") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_BRANCHES_MISPREDICTED : public k8BaseEvent +{ +public: + k8Event_RETIRED_BRANCHES_MISPREDICTED() + { + event_id = 0xC3; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_BRANCHES_MISPREDICTED"), + name = _T("Retired branches mispredicted") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_TAKEN_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_TAKEN_BRANCHES() + { + event_id = 0xC4; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_TAKEN_BRANCHES"), + name = _T("Retired taken branches") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_TAKEN_BRANCHES_MISPREDICTED : public k8BaseEvent +{ +public: + k8Event_RETIRED_TAKEN_BRANCHES_MISPREDICTED() + { + event_id = 0xC5; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_TAKEN_BRANCHES_MISPREDICTED"), + name = _T("Retired taken branches mispredicted") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_FAR_CONTROL_TRANSFERS : public k8BaseEvent +{ +public: + k8Event_RETIRED_FAR_CONTROL_TRANSFERS() + { + event_id = 0xC6; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_FAR_CONTROL_TRANSFERS"), + name = _T("Retired far control transfers") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; +class k8Event_RETIRED_RESYNC_BRANCHES : public k8BaseEvent +{ +public: + k8Event_RETIRED_RESYNC_BRANCHES() + { + event_id = 0xC7; + unitEncode = FR; + + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + //0xF, NULL, _T("RETIRED_RESYNC_BRANCHES"), + name = _T("Retired resync branches (only non-control transfer branches counted)") ; + } + EVENT_MASK(NULL_MASK) * eventMask; +}; + +class k8Event_RETIRED_NEAR_RETURNS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_NEAR_RETURNS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired near returns"); + event_id = 0xC8; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + + + +}; +class k8Event_RETIRED_RETURNS_MISPREDICT : public k8BaseEvent +{ +public: + + k8Event_RETIRED_RETURNS_MISPREDICT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired near returns mispredicted"); + event_id = 0xC9; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("RETIRED_RETURNS_MISPREDICT"); + + +}; +class k8Event_RETIRED_BRANCH_MISCOMPARE : public k8BaseEvent +{ +public: + + k8Event_RETIRED_BRANCH_MISCOMPARE() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Retired taken branches mispredicted due to address miscompare"); + event_id = 0xCA; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("RETIRED_BRANCH_MISCOMPARE"); + + +}; + + + /* Revision B and later */ + +typedef union EVENT_MASK( k8_retired_fpu_instr) +{ + struct + { + uint16 DirtyL2Victim:1; // x87 instructions + uint16 CombinedMMX_3DNow:1; // Combined MMX & 3DNow! instructions" }, + uint16 CombinedPackedSSE_SSE2:1; // Combined packed SSE and SSE2 instructions" }, + uint16 CombinedScalarSSE_SSE2:1; // Combined scalar SSE and SSE2 instructions" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_retired_fpu_instr); + + +class k8Event_RETIRED_FPU_INSTRS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_FPU_INSTRS() + { + eventMask = (EVENT_MASK(k8_retired_fpu_instr) *)&m_eventMask; + event_id = 0xCB; + unitEncode = FR; + + name = _T("Retired FPU instructions"); + revRequired = 'B'; + } + EVENT_MASK(k8_retired_fpu_instr) * eventMask; + /* Revision B and later */ + + + +}; + +// CC +typedef union EVENT_MASK( k8_retired_fastpath_double_op_instr ) +{ + + struct + { + uint16 LowOpPosition0:1; // With low op in position 0" }, + uint16 LowOpPosition1:1; // With low op in position 1" }, + uint16 LowOpPosition2:1; // With low op in position 2" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_retired_fastpath_double_op_instr); + +class k8Event_RETIRED_FASTPATH_INSTRS : public k8BaseEvent +{ +public: + + k8Event_RETIRED_FASTPATH_INSTRS() + { + eventMask = (EVENT_MASK(k8_retired_fastpath_double_op_instr) *)&m_eventMask; + event_id = 0xCC; + unitEncode = FR; + + name = _T("Retired fastpath double op instructions"); + revRequired = 'B'; + + } + EVENT_MASK(k8_retired_fastpath_double_op_instr) * eventMask; + + +}; + +class k8Event_INTERRUPTS_MASKED_CYCLES : public k8BaseEvent +{ +public: + k8Event_INTERRUPTS_MASKED_CYCLES() + { + event_id = 0xCD; + unitEncode = FR; + + //0xF, NULL, _T("INTERRUPTS_MASKED_CYCLES"), + name = _T("Interrupts masked cycles (IF=0)") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; +class k8Event_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES : public k8BaseEvent +{ +public: + k8Event_INTERRUPTS_MASKED_WHILE_PENDING_CYCLES() + { + event_id = 0xCE; + unitEncode = FR; + + //0xF, NULL, _T("INTERRUPTS_MASKED_WHILE_PENDING_CYCLES"), + name = _T("Interrupts masked while pending cycles (INTR while IF=0)") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; +class k8Event_NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS : public k8BaseEvent +{ +public: + k8Event_NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS() + { + event_id = 0xCF; + unitEncode = FR; + + //0xF, NULL, _T("NUMBER_OF_TAKEN_HARDWARE_INTERRUPTS"), + name = _T("Number of taken hardware interrupts") ; + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + } + EVENT_MASK(NULL_MASK) * eventMask; + +}; + + +class k8Event_DECODER_EMPTY : public k8BaseEvent +{ +public: + + k8Event_DECODER_EMPTY() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Nothing to dispatch (decoder empty)"); + event_id = 0xD0; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DECODER_EMPTY"); + + +}; +class k8Event_DISPATCH_STALLS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALLS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stalls (events 0xD2-0xDA combined)"); + event_id = 0xD1; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALLS"); + + + +}; +class k8Event_DISPATCH_STALL_FROM_BRANCH_ABORT : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_FROM_BRANCH_ABORT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall from branch abort to retire"); + event_id = 0xD2; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_FROM_BRANCH_ABORT"); + + + +}; +class k8Event_DISPATCH_STALL_SERIALIZATION : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_SERIALIZATION() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall for serialization"); + event_id = 0xD3; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_SERIALIZATION"); + + +}; +class k8Event_DISPATCH_STALL_SEG_LOAD : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_SEG_LOAD() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall for segment load"); + event_id = 0xD4; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_SEG_LOAD"); + + + +}; +class k8Event_DISPATCH_STALL_REORDER_BUFFER : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_REORDER_BUFFER() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when reorder buffer is full"); + event_id = 0xD5; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_REORDER_BUFFER"); + + +}; +class k8Event_DISPATCH_STALL_RESERVE_STATIONS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_RESERVE_STATIONS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when reservation stations are full"); + event_id = 0xD6; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_RESERVE_STATIONS"); + + +}; +class k8Event_DISPATCH_STALL_FPU : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_FPU() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when FPU is full"); + event_id = 0xD7; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_FPU"); + + +}; +class k8Event_DISPATCH_STALL_LS : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_LS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when LS is full"); + event_id = 0xD8; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_LS"); + + +}; +class k8Event_DISPATCH_STALL_QUIET_WAIT : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_QUIET_WAIT() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when waiting for all to be quiet"); + event_id = 0xD9; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_QUIET_WAIT"); + + + +}; +class k8Event_DISPATCH_STALL_PENDING : public k8BaseEvent +{ +public: + + k8Event_DISPATCH_STALL_PENDING() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Dispatch stall when far control transfer or resync branch is pending"); + event_id = 0xDA; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DISPATCH_STALL_PENDING"); + + + +}; + + +typedef union EVENT_MASK( k8_fpu_exceptions) +{ + + + + struct + { + uint16 x87ReclassMicrofaults:1; // x87 reclass microfaults" }, + uint16 SSERetypeMicrofaults:1; // SSE retype microfaults" }, + uint16 SSEReclassMicrofaults:1; // SSE reclass microfaults" }, + uint16 SSE_x87Microtraps:1; // SSE and x87 microtraps" } } + }; + uint16 flat; + + + +}EVENT_MASK( k8_fpu_exceptions); + +class k8Event_FPU_EXCEPTIONS : public k8BaseEvent +{ +public: + + k8Event_FPU_EXCEPTIONS() + { + eventMask = (EVENT_MASK(k8_fpu_exceptions) *)&m_eventMask; + event_id = 0xDB; + unitEncode = FR; + + name = _T("FPU exceptions"); + revRequired = 'B'; + + } + EVENT_MASK(k8_fpu_exceptions) * eventMask; + // name = _T("FPU_EXCEPTIONS"); + /* Revision B and later */ + + + +}; +class k8Event_DR0_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR0_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR0"); + event_id = 0xDC; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR0_BREAKPOINTS"); + + + +}; +class k8Event_DR1_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR1_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR1"); + event_id = 0xDD; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR1_BREAKPOINTS"); + + + +}; +class k8Event_DR2_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR2_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR2"); + event_id = 0xDE; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR2_BREAKPOINTS"); + + +}; +class k8Event_DR3_BREAKPOINTS : public k8BaseEvent +{ +public: + + k8Event_DR3_BREAKPOINTS() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Number of breakpoints for DR3"); + event_id = 0xDF; + unitEncode = FR; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DR3_BREAKPOINTS"); + + +}; + + + +// E0 +typedef union EVENT_MASK( k8_page_access_event) +{ + struct + { + uint16 PageHit:1; // Page hit" }, + uint16 PageMiss:1; // Page miss" }, + uint16 PageConflict:1; // Page conflict" } } + }; + uint16 flat; + +}EVENT_MASK( k8_page_access_event); + +class k8Event_MEM_PAGE_ACCESS : public k8BaseEvent +{ +public: + + k8Event_MEM_PAGE_ACCESS() + { + eventMask = (EVENT_MASK(k8_page_access_event) *)&m_eventMask; + name = _T("Memory controller page access"); + event_id = 0xE0; + unitEncode = NB; + + } + EVENT_MASK(k8_page_access_event) * eventMask; + // name = _T("MEM_PAGE_ACCESS"); + + +}; +class k8Event_MEM_PAGE_TBL_OVERFLOW : public k8BaseEvent +{ +public: + + k8Event_MEM_PAGE_TBL_OVERFLOW() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Memory controller page table overflow"); + event_id = 0xE1; + unitEncode = NB; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("MEM_PAGE_TBL_OVERFLOW"); + + +}; +class k8Event_DRAM_SLOTS_MISSED : public k8BaseEvent +{ +public: + + k8Event_DRAM_SLOTS_MISSED() + { + eventMask = (EVENT_MASK(NULL_MASK) *)&m_eventMask; + name = _T("Memory controller DRAM command slots missed (in MemClks)"); + event_id = 0xE2; + unitEncode = NB; + + } + EVENT_MASK(NULL_MASK) * eventMask; + // name = _T("DRAM_SLOTS_MISSED"); + + +}; + + +// e3 +typedef union EVENT_MASK( k8_turnaround) +{ + + struct + { + uint16 DIMMTurnaround:1; //DIMM turnaround" }, + uint16 ReadToWriteTurnaround:1; //Read to write turnaround" }, + uint16 WriteToReadTurnaround:1; //Write to read turnaround" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_turnaround); + +class k8Event_MEM_TURNAROUND : public k8BaseEvent +{ +public: + + k8Event_MEM_TURNAROUND() + { + eventMask = (EVENT_MASK(k8_turnaround) *)&m_eventMask; + name = _T("Memory controller turnaround"); + event_id = 0xE3; + unitEncode = NB; + + } + EVENT_MASK(k8_turnaround) * eventMask; + // name = _T("MEM_TURNAROUND"); + + +}; + + + + +// E4 +typedef union EVENT_MASK( k8_bypass_counter_saturation) +{ + struct + { + uint16 MEM_HighPriorityBypass:1; // Memory controller high priority bypass" }, + uint16 MEM_LowPriorityBypass:1; // Memory controller low priority bypass" }, + uint16 DRAM_InterfaceBypass:1; // DRAM controller interface bypass" }, + uint16 DRAM_QueueBypass:1; // DRAM controller queue bypass" } } + }; + uint16 flat; + +}EVENT_MASK( k8_bypass_counter_saturation); + +class k8Event_MEM_BYPASS_SAT : public k8BaseEvent +{ +public: + + k8Event_MEM_BYPASS_SAT() + { + eventMask = (EVENT_MASK(k8_bypass_counter_saturation) *)&m_eventMask; + name = _T("Memory controller bypass counter saturation"); + event_id = 0xE4; + unitEncode = NB; + + } + EVENT_MASK(k8_bypass_counter_saturation) * eventMask; + // name = _T("MEM_BYPASS_SAT"); + + +}; + + + +//EB +typedef union EVENT_MASK( k8_sized_commands) +{ + + struct + { + uint16 NonPostWrSzByte:1; // NonPostWrSzByte" }, + uint16 NonPostWrSzDword:1; // NonPostWrSzDword" }, + uint16 PostWrSzByte:1; // PostWrSzByte" }, + uint16 PostWrSzDword:1; // PostWrSzDword" }, + uint16 RdSzByte:1; // RdSzByte" }, + uint16 RdSzDword:1; // RdSzDword" }, + uint16 RdModWr:1; // RdModWr" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_sized_commands); + + +class k8Event_SIZED_COMMANDS : public k8BaseEvent +{ +public: + + k8Event_SIZED_COMMANDS() + { + eventMask = (EVENT_MASK(k8_sized_commands) *)&m_eventMask; + name = _T("Sized commands"); + event_id = 0xEB; + unitEncode = NB; + + } + EVENT_MASK(k8_sized_commands) * eventMask; + // name = _T("SIZED_COMMANDS"); + + +}; + +typedef union EVENT_MASK( k8_probe_result) +{ + struct + { + uint16 ProbeMiss:1; // Probe miss" }, + uint16 ProbeHit:1; // Probe hit" }, + uint16 ProbeHitDirtyWithoutMemoryCancel:1; // Probe hit dirty without memory cancel" }, + uint16 ProbeHitDirtyWithMemoryCancel:1; // Probe hit dirty with memory cancel" } } + uint16 UpstreamDisplayRefreshReads:1; // Rev D and later + uint16 UpstreamNonDisplayRefreshReads:1; // Rev D and later + uint16 UpstreamWrites:1; // Rev D and later + }; + uint16 flat; + + +}EVENT_MASK( k8_probe_result); + + +class k8Event_PROBE_RESULT : public k8BaseEvent +{ +public: + + k8Event_PROBE_RESULT() + { + eventMask = (EVENT_MASK(k8_probe_result) *)&m_eventMask; + name = _T("Probe result"); + event_id = 0xEC; + unitEncode = NB; + + } + EVENT_MASK(k8_probe_result) * eventMask; + // name = _T("PROBE_RESULT"); + + +}; + +typedef union EVENT_MASK( k8_ht) +{ + + struct + { + uint16 CommandSent:1; //Command sent" }, + uint16 DataSent:1; //Data sent" }, + uint16 BufferReleaseSent:1; //Buffer release sent" + uint16 NopSent:1; //Nop sent" } } + }; + uint16 flat; + + +}EVENT_MASK( k8_ht); + + +class k8Event_HYPERTRANSPORT_BUS0_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS0_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 0 bandwidth"); + event_id = 0xF6; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS0_WIDTH"); + + +}; +class k8Event_HYPERTRANSPORT_BUS1_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS1_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 1 bandwidth"); + event_id = 0xF7; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS1_WIDTH"); + + +}; +class k8Event_HYPERTRANSPORT_BUS2_WIDTH : public k8BaseEvent +{ +public: + + k8Event_HYPERTRANSPORT_BUS2_WIDTH() + { + eventMask = (EVENT_MASK(k8_ht) *)&m_eventMask; + name = _T("Hypertransport (tm) bus 2 bandwidth"); + event_id = 0xF8; + unitEncode = NB; + + } + EVENT_MASK(k8_ht) * eventMask; + // name = _T("HYPERTRANSPORT_BUS2_WIDTH"); + +}; + +// +//typedef union EVENT_MASK( perfctr_event_set k8_common_event_set) +//{ +// +// .cpu_type = PERFCTR_X86_AMD_K8, +// .event_prefix = _T("K8_"), +// .include = &k7_official_event_set, +// .nevents = ARRAY_SIZE(k8_common_events), +// .events = k8_common_events, +//}EVENT_MASK( perfctr_event_set k8_common_event_set); +// +//typedef union EVENT_MASK( perfctr_event k8_events[]) +//{ +// +// { 0x24, 0xF, UM(NULL), _T("LOCKED_OP"), /* unit mask changed in Rev. C */ +// _T("Locked operation") }, +//}EVENT_MASK( perfctr_event k8_events[]); + + + + +//const struct perfctr_event_set perfctr_k8_event_set) +//{ +// +// .cpu_type = PERFCTR_X86_AMD_K8, +// .event_prefix = _T("K8_"), +// .include = &k8_common_event_set, +// .nevents = ARRAY_SIZE(k8_events), +// .events = k8_events, +//}; +// +/* + * K8 Revision C. Starts at CPUID 0xF58 for Opteron/Athlon64FX and + * CPUID 0xF48 for Athlon64. (CPUID 0xF51 is Opteron Revision B3.) + */ + + + + + + + + +// +//typedef union EVENT_MASK( k8_lock_accesses) +//{ +// struct +// { +// uint16 DcacheAccesses:1; // Number of dcache accesses by lock instructions" }, +// uint16 DcacheMisses:1; // Number of dcache misses by lock instructions" } } +// }; +// uint16 flat; +// +//}EVENT_MASK( k8_lock_accesses); +// + +#endif // K8PERFORMANCECOUNTERS_H diff --git a/public/tier0/P4PerformanceCounters.h b/public/tier0/P4PerformanceCounters.h new file mode 100644 index 0000000..d0882b9 --- /dev/null +++ b/public/tier0/P4PerformanceCounters.h @@ -0,0 +1,322 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef P4PERFORMANCECOUNTERS_H +#define P4PERFORMANCECOUNTERS_H + +#pragma once +// Pentium 4 support + +/* + http://developer.intel.com/design/Pentium4/documentation.htm + + IA-32 Intel Architecture Software Developer's Manual Volume 1: Basic Architecture + + IA-32 Intel Architecture Software Developer's Manual Volume 2A: Instruction Set Reference, A-M + + IA-32 Intel Architecture Software Developer's Manual Volume 2B: Instruction Set Reference, N-Z + + IA-32 Intel Architecture Software Developer's Manual Volume 3: System Programming Guide + + + From Mikael Pettersson's perfctr: + + http://user.it.uu.se/~mikpe/linux/perfctr/ + + * Known quirks: + - OVF_PMI+FORCE_OVF counters must have an ireset value of -1. + This allows the regular overflow check to also handle FORCE_OVF + counters. Not having this restriction would lead to MAJOR + complications in the driver's "detect overflow counters" code. + There is no loss of functionality since the ireset value doesn't + affect the counter's PMI rate for FORCE_OVF counters. + + - In experiments with FORCE_OVF counters, and regular OVF_PMI + counters with small ireset values between -8 and -1, it appears + that the faulting instruction is subjected to a new PMI before + it can complete, ad infinitum. This occurs even though the driver + clears the CCCR (and in testing also the ESCR) and invokes a + user-space signal handler before restoring the CCCR and resuming + the instruction. +*/ + +#define NCOUNTERS 18 + +// The 18 counters +enum Counters +{ + MSR_BPU_COUNTER0, + MSR_BPU_COUNTER1, + MSR_BPU_COUNTER2, + MSR_BPU_COUNTER3, + MSR_MS_COUNTER0, + MSR_MS_COUNTER1, + MSR_MS_COUNTER2, + MSR_MS_COUNTER3, + MSR_FLAME_COUNTER0, + MSR_FLAME_COUNTER1, + MSR_FLAME_COUNTER2, + MSR_FLAME_COUNTER3, + MSR_IQ_COUNTER0, + MSR_IQ_COUNTER1, + MSR_IQ_COUNTER2, + MSR_IQ_COUNTER3, + MSR_IQ_COUNTER4, + MSR_IQ_COUNTER5 +}; + +// register base for counters +#define MSR_COUNTER_BASE 0x300 + +// register base for CCCR register +#define MSR_CCCR_BASE 0x360 + +#pragma pack(push, 1) +// access to these bits is through the methods +typedef union ESCR +{ + struct + { + uint64 Reserved0_1 : 2; // + uint64 USR : 1; // + uint64 OS : 1; // + uint64 TagEnable : 1; // + uint64 TagValue : 4; // + uint64 EventMask : 16; // from event select + uint64 ESCREventSelect : 6; // 31:25 class of event + uint64 Reserved31 : 1; // + + uint64 Reserved32_63 : 32; // + }; + uint64 flat; + +} ESCR; + +typedef union CCCR +{ + struct + { + uint64 Reserved0_11 : 12;// 0 -11 + uint64 Enable : 1; // 12 + uint64 CCCRSelect : 3; // 13-15 + uint64 Reserved16_17 : 2; // 16 17 + + uint64 Compare : 1; // 18 + uint64 Complement : 1; // 19 + uint64 Threshold : 4; // 20-23 + uint64 Edge : 1; // 24 + uint64 FORCE_OVF : 1; // 25 + uint64 OVF_PMI : 1; // 26 + uint64 Reserved27_29 : 3; // 27-29 + uint64 Cascade : 1; // 30 + uint64 OVF : 1; // 31 + + uint64 Reserved32_63 : 32; // + }; + uint64 flat; + +} CCCR; + +#pragma pack(pop) + +extern const unsigned short cccr_escr_map[NCOUNTERS][8]; + +enum P4TagState +{ + TagDisable, // + TagEnable, // +}; + +enum P4ForceOverflow +{ + ForceOverflowDisable, + ForceOverflowEnable, +}; + +enum P4OverflowInterrupt +{ + OverflowInterruptDisable, + OverflowInterruptEnable, +}; + +// Turn off the no return value warning in ReadCounter. +#pragma warning( disable : 4035 ) +class P4BaseEvent +{ + int m_counter; + +protected: + + void SetCounter(int counter) + { + m_counter = counter; + cccrPort = MSR_CCCR_BASE + m_counter; + counterPort = MSR_COUNTER_BASE + m_counter; + escrPort = cccr_escr_map[m_counter][cccr.CCCRSelect]; + } + +public: + + unsigned short m_eventMask; + const tchar *description; + PME *pme; + ESCR escr; + CCCR cccr; + int counterPort; + int cccrPort; + int escrPort; + + P4BaseEvent() + { + pme = PME::Instance(); + m_eventMask = 0; + description = _T(""); + escr.flat = 0; + cccr.flat = 0; + cccr.Reserved16_17 = 3; // must be set + escrPort = 0; + m_counter = -1; + } + + void StartCounter() + { + cccr.Enable = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void StopCounter() + { + cccr.Enable = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearCounter() + { + pme->WriteMSR( counterPort, 0ui64 ); // clear + } + + void WriteCounter( int64 value ) + { + pme->WriteMSR( counterPort, value ); // clear + } + + int64 ReadCounter() + { +#if PME_DEBUG + if ( escr.USR == 0 && escr.OS == 0 ) + return -1; // no area to collect, use SetCaptureMode + + if ( escr.EventMask == 0 ) + return -2; // no event mask set + + if ( m_counter == -1 ) + return -3; // counter not legal +#endif + + // ReadMSR should work here too, but RDPMC should be faster + int64 value = 0; + pme->ReadMSR( counterPort, &value ); + return value; +#if 0 + // we need to copy this into a temp for some reason + int temp = m_counter; + _asm + { + mov ecx, temp + RDPMC + } +#endif + } + + void SetCaptureMode( PrivilegeCapture priv ) + { + switch ( priv ) + { + case OS_Only: + { + escr.USR = 0; + escr.OS = 1; + break; + } + case USR_Only: + { + escr.USR = 1; + escr.OS = 0; + break; + } + case OS_and_USR: + { + escr.USR = 1; + escr.OS = 1; + break; + } + } + + escr.EventMask = m_eventMask; + pme->WriteMSR( escrPort, escr.flat ); + } + + void SetTagging( P4TagState tagEnable, uint8 tagValue ) + { + escr.TagEnable = tagEnable; + escr.TagValue = tagValue; + pme->WriteMSR( escrPort, escr.flat ); + } + + void SetFiltering( CompareState compareEnable, CompareMethod compareMethod, uint8 threshold, EdgeState edgeEnable ) + { + cccr.Compare = compareEnable; + cccr.Complement = compareMethod; + cccr.Threshold = threshold; + cccr.Edge = edgeEnable; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void SetOverflowEnables( P4ForceOverflow overflowEnable, P4OverflowInterrupt overflowInterruptEnable ) + { + cccr.FORCE_OVF = overflowEnable; + cccr.OVF_PMI = overflowInterruptEnable; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void SetOverflow() + { + cccr.OVF = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearOverflow() + { + cccr.OVF = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + bool isOverflow() + { + CCCR cccr_temp; + pme->ReadMSR( cccrPort, &cccr_temp.flat ); + return cccr_temp.OVF; + } + + void SetCascade() + { + cccr.Cascade = 1; + pme->WriteMSR( cccrPort, cccr.flat ); + } + + void ClearCascade() + { + cccr.Cascade = 0; + pme->WriteMSR( cccrPort, cccr.flat ); + } +}; +#pragma warning( default : 4035 ) + +#include "eventmasks.h" +#include "eventmodes.h" + +#endif // P4PERFORMANCECOUNTERS_H diff --git a/public/tier0/P5P6PerformanceCounters.h b/public/tier0/P5P6PerformanceCounters.h new file mode 100644 index 0000000..6e2d8b1 --- /dev/null +++ b/public/tier0/P5P6PerformanceCounters.h @@ -0,0 +1,225 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef P5P6PERFORMANCECOUNTERS_H +#define P5P6PERFORMANCECOUNTERS_H + +// defined for < Pentium 4 + +//--------------------------------------------------------------------------- +// Format of the performance event IDs within this header file in case you +// wish to add any additional events that may not be present here. +// +// BITS 0-8 Unit Mask, Unsed on P5 processors +// BIT 9 Set if event can be set on counter 0 +// BIT 10 Set if event can be set on counter 1 +// BITS 11-15 Unused Set to zero +// BITS 16-23 Event Select ID, Only bits 16-21 used on P5 Family +// BITS 24-27 Unused set to zero +// BITS 28-32 Process family that the event belong to, as returned by +// the CPUID instruction. +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// PENTIUM PERFORMANCE COUNTERS. +//--------------------------------------------------------------------------- +#define P5_DTCRD 0x50000300 //Data Cache Reads +#define P5_DWRIT 0x50010300 //Data Cache Writes +#define P5_DTTLB 0x50020300 //Data TLB Miss +#define P5_DTRMS 0x50030300 //Data Read Misses +#define P5_DWRMS 0x50040300 //Data Write Miss +#define P5_WHLCL 0x50050300 //Write (Hit) to M- or E-state line +#define P5_DCLWB 0x50060300 //Data Cache Lines Written Back +#define P5_DCSNP 0x50070300 //External Snoops +#define P5_DCSHT 0x50080300 //Data Cache Snoop Hits +#define P5_MAIBP 0x50090300 //Memory Access in Both Pipes +#define P5_BANKS 0x500A0300 //Bank Conflicts +#define P5_MISAL 0x500B0300 //Misaligned Data Memory Reference +#define P5_COCRD 0x500C0300 //Code Cache Reads +#define P5_COTLB 0x500D0300 //Code TLB Misses +#define P5_COCMS 0x500E0300 //Code Cache Misses +#define P5_ANYSG 0x500F0300 //Any Segment Register Loaded +#define P5_BRANC 0x50120300 //Branches +#define P5_BTBHT 0x50130300 //BTB Hits +#define P5_TBRAN 0x50140300 //Taken Branch or BTB hit +#define P5_PFLSH 0x50150300 //Pipeline flushes +#define P5_INSTR 0x50160300 //Instructions Executed +#define P5_INSTV 0x50170300 //Instructions Executed in the V-Pipe (Pairing) +#define P5_CLOCL 0x50180300 //Bus active +#define P5_PSDWR 0x50190300 //Full write buffers +#define P5_PSWDR 0x501A0300 //Waiting for Data Memory Read +#define P5_NCLSW 0x501B0300 //Clocks stalled writing an E or M state line +#define P5_IORWC 0x501D0300 //I/O Read or Write Cycle +#define P5_NOCMR 0x501E0300 //Non-Cacheable Memory Reads +#define P5_PSLDA 0x501F0300 //Clocks stalled due to AGI +#define P5_FLOPS 0x50220300 //Floating Point Operations +#define P5_DBGR0 0x50230300 //Breakpoint match on DR0 +#define P5_DBGR1 0x50240300 //Breakpoint match on DR1 +#define P5_DBGR2 0x50250300 //Breakpoint match on DR2 +#define P5_DBGR3 0x50260300 //Breakpoint match on DR3 +#define P5_HWINT 0x50270300 //Hardware interrupts +#define P5_DTRWR 0x50280300 //Data reads or writes +#define P5_DTRWM 0x50290300 //Data read or write miss +#define P5_BOLAT 0x502A0100 //Bus ownership latency +#define P5_BOTFR 0x502A0200 //Bus ownership transfer +#define P5_MMXA1 0x502B0100 //MMX Instruction Executed in U-pipe +#define P5_MMXA2 0x502B0200 //MMX Instruction Executed in V-pipe +#define P5_MMXMS 0x502C0100 //Cache M state line sharing +#define P5_MMSLS 0x502C0200 //Cache line sharing +#define P5_MMXB1 0x502D0100 //EMMS Instructions Executed +#define P5_MMXB2 0x502D0200 //Transition from MMX to FP instructions +#define P5_NOCMW 0x502E0200 //Non-Cacheable Memory Writes +#define P5_MMXC1 0x502F0100 //Saturated MMX Instructions Executed +#define P5_MMXC2 0x502F0200 //Saturations Performed +#define P5_MMXHS 0x50300100 //Cycles Not in HALT State +#define P5_MMXD2 0x50310100 //MMX Data Read +#define P5_MMXFP 0x50320100 //Floating Point Stalls +#define P5_MMXTB 0x50320200 //Taken Branches +#define P5_MMXD0 0x50330100 //D1 Starvation and FIFO Empty +#define P5_MMXD1 0x50330200 //D1 Starvation and one instruction in FIFO +#define P5_MMXE1 0x50340100 //MMX Data Writes +#define P5_MMXE2 0x50340200 //MMX Data Write Misses +#define P5_MMXWB 0x50350100 //Pipeline flushes, wrong branch prediction +#define P5_MMXWJ 0x50350200 //Pipeline flushes, branch prediction WB-stage +#define P5_MMXF1 0x50360100 //Misaligned MMX Data Memory Reference +#define P5_MMXF2 0x50360200 //Pipeline Stalled Waiting for MMX data read +#define P5_MMXRI 0x50370100 //Returns Predicted Incorrectly +#define P5_MMXRP 0x50370200 //Returns Predicted +#define P5_MMXG1 0x50380100 //MMX Multiply Unit Interlock +#define P5_MMXG2 0x50380200 //MOVD/MOVQ store stall, previous operation +#define P5_MMXRT 0x50390100 //Returns +#define P5_MMXRO 0x50390200 //RSB Overflows +#define P5_MMXBF 0x503A0100 //BTB False entries +#define P5_MMXBM 0x503A0200 //BTB misprediction on a Not-Taken Branch +#define P5_PXDWR 0x503B0100 //stalled due MMX Full write buffers +#define P5_PXZWR 0x503B0200 //stalled on MMX write to E or M state line + +#define P5_CLOCK 0x503F0300 //Special value to count clocks on P5 + + +//--------------------------------------------------------------------------- +// PENTIUM PRO / PENTIUM II PERFORMANCE COUNTERS. +//--------------------------------------------------------------------------- +#define P6_STRBB 0x60030300 //Store Buffer Block +#define P6_STBDC 0x60040300 //Store Buffer Drain Cycles +#define P6_MISMM 0x60050300 //Misaligned Data Memory Reference +#define P6_SEGLD 0x60060300 //Segment register loads +#define P6_FPOPE 0x60100100 //FP Computational Op. (COUNTER 0 ONLY) +#define P6_FPEOA 0x60110200 //FP Microcode Exceptions (COUNTER 1 ONLY) +#define P6_FMULT 0x60120200 //Multiplies (COUNTER 1 ONLY) +#define P6_FPDIV 0x60130200 //Divides (COUNTER 1 ONLY) +#define P6_DBUSY 0x60140200 //Cycles Divider Busy (COUNTER 1 ONLY) +#define P6_L2STR 0x60210300 //L2 address strobes => address bus utilization +#define P6_L2BBS 0x60220300 //Cycles L2 Bus Busy +#define P6_L2BBT 0x60230300 //Cycles L2 Bus Busy transferring data to CPU +#define P6_L2ALO 0x60240300 //L2 Lines Allocated +#define P6_L2MAL 0x60250300 //L2 M-state Lines Allocated +#define P6_L2CEV 0x60260300 //L2 Lines Evicted +#define P6_L2MEV 0x60270300 //L2 M-state Lines Evicted +#define P6_L2MCF 0x60280301 //L2 Cache Instruction Fetch Misses +#define P6_L2FET 0x6028030F //L2 Cache Instruction Fetches +#define P6_L2DRM 0x60290301 //L2 Cache Read Misses +#define P6_L2DMR 0x6029030F //L2 Cache Reads +#define P6_L2DWM 0x602A0301 //L2 Cache Write Misses +#define P6_L2DMW 0x602A030F //L2 Cache Writes +#define P6_L2CMS 0x602E0301 //L2 Cache Request Misses +#define P6_L2DCR 0x602E030F //L2 Cache Requests +#define P6_DMREF 0x60430300 //Data Memory References +#define P6_DCALO 0x6045030F //L1 Lines Allocated +#define P6_DCMAL 0x60460300 //L1 M-state Data Cache Lines Allocated +#define P6_DCMEV 0x60470300 //L1 M-state Data Cache Lines Evicted +#define P6_DCOUT 0x60480300 //L1 Misses outstanding +#define P6_TSMCD 0x60520300 //Time Self-Modifiying Code Detected +#define P6_BRWRA 0x60600300 //External Bus Cycles While Receive Active +#define P6_BRDCD 0x60600300 //External Bus Request Outstanding +#define P6_BRBNR 0x60610300 //External Bus Cycles While BNR Asserted +#define P6_BUSBS 0x60620300 //External Bus Cycles-DRDY Asserted (busy) +#define P6_BLOCK 0x60630300 //External Bus Cycles-LOCK signal asserted +#define P6_BBRCV 0x60640300 //External Bus Cycles-Processor receiving data +#define P6_BURST 0x60650300 //External Bus Burst Read Operations +#define P6_BRINV 0x60660300 //External Bus Read for Ownership Transaction +#define P6_BMLEV 0x60670300 //External Bus Writeback M-state Evicted +#define P6_BBIFT 0x60680300 //External Bus Burst Instruction Fetches +#define P6_BINVL 0x60690300 //External Bus Invalidate Transactions +#define P6_BPRBT 0x606A0300 //External Bus Partial Read Transactions +#define P6_BPTMO 0x606B0300 //External Bus Partial Memory Transactions +#define P6_BUSIO 0x606C0300 //External Bus I/O Bus Transactions +#define P6_BUSDF 0x606D0300 //External Bus Deferred Transactions +#define P6_BUSTB 0x606E0300 //External Bus Burst Transactions +#define P6_BMALL 0x606F0300 //External Bus Memory Transactions +#define P6_BSALL 0x60700300 //External Bus Transactions +#define P6_CLOCK 0x60790300 //Clockticks +#define P6_BRHIT 0x607A0300 //External Bus Cycles While HIT Asserted +#define P6_BRHTM 0x607B0300 //External Bus Cycles While HITM Asserted +#define P6_BRSST 0x607E0300 //External Bus Cycles While Snoop Stalled +#define P6_CMREF 0x60800300 //Total Instruction Fetches +#define P6_TOIFM 0x60810300 //Total Instruction Fetch Misses +#define P6_INTLB 0x60850300 //Instructions TLB Misses +#define P6_CSFET 0x60860300 //Cycles Instruction Fetch Stalled +#define P6_FTSTL 0x60870300 //Cycles Instruction Fetch stalled +#define P6_RSTAL 0x60A20300 //Resource Related Stalls +#define P6_MMXIE 0x60B00300 //MMX Instructions Executed +#define P6_SAISE 0x60B10300 //Saturated Arithmetic Instructions Executed +#define P6_PORT0 0x60B20301 //MMX micro-ops executed on Port 0 +#define P6_PORT1 0x60B20302 //MMX micro-ops executed on Port 1 +#define P6_PORT2 0x60B20304 //MMX micro-ops executed on Port 2 +#define P6_PORT3 0x60B20308 //MMX micro-ops executed on Port 3 +#define P6_MMXPA 0x60B30300 //MMX Packed Arithmetic +#define P6_MMXPM 0x60B30301 //MMX Packed Multiply +#define P6_MMXPS 0x60B30302 //MMX Packed Shift +#define P6_MMXPO 0x60B30304 //MMX Packed Operations +#define P6_MMXUO 0x60B30308 //MMX Unpacked Operations +#define P6_MMXPL 0x60B30310 //MMX Packed Logical +#define P6_INSTR 0x60C00300 //Instructions Retired +#define P6_FPOPS 0x60C10100 //FP operations retired (COUNTER 0 ONLY) +#define P6_UOPSR 0x60C20300 //Micro-Ops Retired +#define P6_BRRET 0x60C40300 //Branch Instructions Retired +#define P6_BRMSR 0x60C50300 //Branch Mispredictions Retired +#define P6_MASKD 0x60C60300 //Clocks while interrupts masked +#define P6_MSKPN 0x60C70300 //Clocks while interrupt is pending +#define P6_HWINT 0x60C80300 //Hardware Interrupts Received +#define P6_BTAKR 0x60C90300 //Taken Branch Retired +#define P6_BTAKM 0x60CA0300 //Taken Branch Mispredictions +#define P6_FPMMX 0x60CC0301 //Transitions from Floating Point to MMX +#define P6_MMXFP 0x60CC0300 //Transitions from MMX to Floating Point +#define P6_SIMDA 0x60CD0300 //SIMD Assists (EMMS Instructions Executed) +#define P6_MMXIR 0x60CE0300 //MMX Instructions Retired +#define P6_SAISR 0x60CF0300 //Saturated Arithmetic Instructions Retired +#define P6_INSTD 0x60D00300 //Instructions Decoded +#define P6_NPRTL 0x60D20300 //Renaming Stalls +#define P6_SRSES 0x60D40301 //Segment Rename Stalls - ES +#define P6_SRSDS 0x60D40302 //Segment Rename Stalls - DS +#define P6_SRSFS 0x60D40304 //Segment Rename Stalls - FS +#define P6_SRSGS 0x60D40308 //Segment Rename Stalls - GS +#define P6_SRSXS 0x60D4030F //Segment Rename Stalls - ES DS FS GS +#define P6_SRNES 0x60D50301 //Segment Renames - ES +#define P6_SRNDS 0x60D50302 //Segment Renames - DS +#define P6_SRNFS 0x60D50304 //Segment Renames - FS +#define P6_SRNGS 0x60D50308 //Segment Renames - GS +#define P6_SRNXS 0x60D5030F //Segment Renames - ES DS FS GS +#define P6_BRDEC 0x60E00300 //Branch Instructions Decoded +#define P6_BTBMS 0x60E20301 //BTB Misses +#define P6_RETDC 0x60E40300 //Bogus Branches +#define P6_BACLR 0x60E60300 //BACLEARS Asserted (Testing) + + + + + + +// INTEL +#define PENTIUM_FAMILY 5 // define for pentium +#define PENTIUMPRO_FAMILY 6 // define for pentium pro +#define PENTIUM4_FAMILY 15 // define for pentium 4 + + +// AMD +#define K6_FAMILY 5 +#define K8_FAMILY 6 +#define EXTENDED_FAMILY 15 // AMD 64 and AMD Opteron + +#endif // P5P6PERFORMANCECOUNTERS_H diff --git a/public/tier0/PMELib.h b/public/tier0/PMELib.h new file mode 100644 index 0000000..dcdec5f --- /dev/null +++ b/public/tier0/PMELib.h @@ -0,0 +1,212 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#ifndef PMELIB_H +#define PMELIB_H + +//#include "windows.h" + +#include "tier0/platform.h" + +// Get rid of a bunch of STL warnings! +#pragma warning( push, 3 ) +#pragma warning( disable : 4018 ) + +#define VERSION "1.0.2" + +// uncomment this list to add some runtime checks +//#define PME_DEBUG + +#include "tier0/valve_off.h" +#include +#include "tier0/valve_on.h" + +using namespace std; + +// RDTSC Instruction macro +#ifdef COMPILER_MSVC64 +#define RDTSC(var) (var = __rdtsc()) +#else +#define RDTSC(var) \ +_asm RDTSC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx +#endif + +// RDPMC Instruction macro +#ifdef COMPILER_MSVC64 +#define RDPMC(counter, var) (var = __readpmc(counter)) +#else +#define RDPMC(counter, var) \ +_asm mov ecx, counter \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx +#endif + +// RDPMC Instruction macro, for performance counter 1 (ecx = 1) +#ifdef COMPILER_MSVC64 +#define RDPMC0(var) (var = __readpmc(0)) +#else +#define RDPMC0(var) \ +_asm mov ecx, 0 \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx +#endif + +#ifdef COMPILER_MSVC64 +#define RDPMC1(var) (var = __readpmc(1)) +#else +#define RDPMC1(var) \ +_asm mov ecx, 1 \ +_asm RDPMC \ +_asm mov DWORD PTR var,eax \ +_asm mov DWORD PTR var+4,edx +#endif + +#define EVENT_TYPE(mode) EventType##mode +#define EVENT_MASK(mode) EventMask##mode + +#include "ia32detect.h" + +enum ProcessPriority +{ + ProcessPriorityNormal, + ProcessPriorityHigh, +}; + +enum PrivilegeCapture +{ + OS_Only, // ring 0, kernel level + USR_Only, // app level + OS_and_USR, // all levels +}; + +enum CompareMethod +{ + CompareGreater, // + CompareLessEqual, // +}; + +enum EdgeState +{ + RisingEdgeDisabled, // + RisingEdgeEnabled, // +}; + +enum CompareState +{ + CompareDisable, // + CompareEnable, // +}; + +// Singletion Class +class PME : public ia32detect +{ +public: +//private: + + static PME* _singleton; + + HANDLE hFile; + bool bDriverOpen; + double m_CPUClockSpeed; + + //ia32detect detect; + HRESULT Init(); + HRESULT Close(); + +protected: + + PME() + { + hFile = NULL; + bDriverOpen = FALSE; + m_CPUClockSpeed = 0; + Init(); + } + +public: + + static PME* Instance(); // gives back a real object + + ~PME() + { + Close(); + } + + double GetCPUClockSpeedSlow( void ); + double GetCPUClockSpeedFast( void ); + + HRESULT SelectP5P6PerformanceEvent( uint32 dw_event, uint32 dw_counter, bool b_user, bool b_kernel ); + + HRESULT ReadMSR( uint32 dw_reg, int64 * pi64_value ); + HRESULT ReadMSR( uint32 dw_reg, uint64 * pi64_value ); + + HRESULT WriteMSR( uint32 dw_reg, const int64 & i64_value ); + HRESULT WriteMSR( uint32 dw_reg, const uint64 & i64_value ); + + void SetProcessPriority( ProcessPriority priority ) + { + switch( priority ) + { + case ProcessPriorityNormal: + { + SetPriorityClass (GetCurrentProcess(),NORMAL_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_NORMAL); + break; + } + case ProcessPriorityHigh: + { + SetPriorityClass (GetCurrentProcess(),REALTIME_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread(),THREAD_PRIORITY_HIGHEST); + break; + } + } + } + + //--------------------------------------------------------------------------- + // Return the family of the processor + //--------------------------------------------------------------------------- + CPUVendor GetVendor(void) + { + return vendor; + } + + int GetProcessorFamily(void) + { + return version.Family; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +}; + +#include "p5p6performancecounters.h" +#include "p4performancecounters.h" +#include "k8performancecounters.h" + +enum PerfErrors +{ + E_UNKNOWN_CPU_VENDOR = -1, + E_BAD_COUNTER = -2, + E_UNKNOWN_CPU = -3, + E_CANT_OPEN_DRIVER = -4, + E_DRIVER_ALREADY_OPEN = -5, + E_DRIVER_NOT_OPEN = -6, + E_DISABLED = -7, + E_BAD_DATA = -8, + E_CANT_CLOSE = -9, + E_ILLEGAL_OPERATION = -10, +}; + +#pragma warning( pop ) + +#endif // PMELIB_H \ No newline at end of file diff --git a/public/tier0/afxmem_override.cpp b/public/tier0/afxmem_override.cpp new file mode 100644 index 0000000..f19cf2e --- /dev/null +++ b/public/tier0/afxmem_override.cpp @@ -0,0 +1,460 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// File extracted from MFC due to symbol conflicts + +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#include "stdafx.h" + +#ifdef AFX_CORE1_SEG +#pragma code_seg(AFX_CORE1_SEG) +#endif + + +///////////////////////////////////////////////////////////////////////////// +// Debug memory globals and implementation helpers + +#ifdef _DEBUG // most of this file is for debugging + +void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine); +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize, int nType, LPCSTR lpszFileName, int nLine); +#endif + +///////////////////////////////////////////////////////////////////////////// +// test allocation routines + +void* PASCAL CObject::operator new(size_t nSize) +{ +#ifdef _AFX_NO_DEBUG_CRT + return ::operator new(nSize); +#else + return ::operator new(nSize, _AFX_CLIENT_BLOCK, NULL, 0); +#endif // _AFX_NO_DEBUG_CRT +} + +void PASCAL CObject::operator delete(void* p) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(p); +#else + _free_dbg(p, _AFX_CLIENT_BLOCK); +#endif +} + +#if _MSC_VER >= 1200 +void PASCAL CObject::operator delete(void* p, void*) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(p); +#else + _free_dbg(p, _AFX_CLIENT_BLOCK); +#endif +} +#endif + +#ifndef _AFX_NO_DEBUG_CRT + +void* __cdecl operator new(size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine); +} + +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new[](nSize, _NORMAL_BLOCK, lpszFileName, nLine); +} +#endif + +#if _MSC_VER >= 1200 +void __cdecl operator delete(void* pData, LPCSTR /* lpszFileName */, + int /* nLine */) +{ + ::operator delete(pData); +} +#endif + +#if _MSC_VER >= 1210 +void __cdecl operator delete[](void* pData, LPCSTR /* lpszFileName */, + int /* nLine */) +{ + ::operator delete(pData); +} +#endif + +void* PASCAL +CObject::operator new(size_t nSize, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, _AFX_CLIENT_BLOCK, lpszFileName, nLine); +} + +#if _MSC_VER >= 1200 +void PASCAL +CObject::operator delete(void *pObject, LPCSTR /* lpszFileName */, + int /* nLine */) +{ +#ifdef _AFX_NO_DEBUG_CRT + free(pObject); +#else + _free_dbg(pObject, _AFX_CLIENT_BLOCK); +#endif +} +#endif + +void* AFXAPI AfxAllocMemoryDebug(size_t nSize, BOOL bIsObject, LPCSTR lpszFileName, int nLine) +{ + return _malloc_dbg(nSize, bIsObject ? _AFX_CLIENT_BLOCK : _NORMAL_BLOCK, + lpszFileName, nLine); +} + +void AFXAPI AfxFreeMemoryDebug(void* pbData, BOOL bIsObject) +{ + _free_dbg(pbData, bIsObject ? _AFX_CLIENT_BLOCK : _NORMAL_BLOCK); +} + +///////////////////////////////////////////////////////////////////////////// +// allocation failure hook, tracking turn on + +BOOL AFXAPI _AfxDefaultAllocHook(size_t, BOOL, LONG) + { return TRUE; } + +AFX_STATIC_DATA AFX_ALLOC_HOOK pfnAllocHook = _AfxDefaultAllocHook; + +AFX_STATIC_DATA _CRT_ALLOC_HOOK pfnCrtAllocHook = NULL; +#if _MSC_VER >= 1200 +int __cdecl _AfxAllocHookProxy(int nAllocType, void * pvData, size_t nSize, + int nBlockUse, long lRequest, const unsigned char * szFilename, int nLine) +#else +int __cdecl _AfxAllocHookProxy(int nAllocType, void * pvData, size_t nSize, + int nBlockUse, long lRequest, const char * szFilename, int nLine) +#endif +{ +#if _MSC_VER >= 1200 + if (nAllocType != _HOOK_ALLOC) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, (const unsigned char*) szFilename, nLine); + if ((pfnAllocHook)(nSize, _BLOCK_TYPE(nBlockUse) == _AFX_CLIENT_BLOCK, lRequest)) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, (const unsigned char*) szFilename, nLine); +#else + if (nAllocType != _HOOK_ALLOC) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, szFilename, nLine); + if ((pfnAllocHook)(nSize, _BLOCK_TYPE(nBlockUse) == _AFX_CLIENT_BLOCK, lRequest)) + return (pfnCrtAllocHook)(nAllocType, pvData, nSize, + nBlockUse, lRequest, szFilename, nLine); +#endif + return FALSE; +} + +AFX_ALLOC_HOOK AFXAPI AfxSetAllocHook(AFX_ALLOC_HOOK pfnNewHook) +{ + if (pfnCrtAllocHook == NULL) + pfnCrtAllocHook = _CrtSetAllocHook(_AfxAllocHookProxy); + + AFX_ALLOC_HOOK pfnOldHook = pfnAllocHook; + pfnAllocHook = pfnNewHook; + return pfnOldHook; +} + +// This can be set to TRUE to override all AfxEnableMemoryTracking calls, +// allowing all allocations, even MFC internal allocations to be tracked. +BOOL _afxMemoryLeakOverride = FALSE; + +BOOL AFXAPI AfxEnableMemoryTracking(BOOL bTrack) +{ + if (_afxMemoryLeakOverride) + return TRUE; + + int nOldState = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + if (bTrack) + _CrtSetDbgFlag(nOldState | _CRTDBG_ALLOC_MEM_DF); + else + _CrtSetDbgFlag(nOldState & ~_CRTDBG_ALLOC_MEM_DF); + return nOldState & _CRTDBG_ALLOC_MEM_DF; +} + +///////////////////////////////////////////////////////////////////////////// +// stop on a specific memory request + +// Obsolete API +void AFXAPI AfxSetAllocStop(LONG lRequestNumber) +{ + _CrtSetBreakAlloc(lRequestNumber); +} + +BOOL AFXAPI AfxCheckMemory() + // check all of memory (look for memory tromps) +{ + return _CrtCheckMemory(); +} + +// -- true if block of exact size, allocated on the heap +// -- set *plRequestNumber to request number (or 0) +BOOL AFXAPI AfxIsMemoryBlock(const void* pData, UINT nBytes, + LONG* plRequestNumber) +{ + return _CrtIsMemoryBlock(pData, nBytes, plRequestNumber, NULL, NULL); +} + +///////////////////////////////////////////////////////////////////////////// +// CMemoryState + +CMemoryState::CMemoryState() +{ + memset(this, 0, sizeof(*this)); +} + +void CMemoryState::UpdateData() +{ + for(int i = 0; i < nBlockUseMax; i++) + { + m_lCounts[i] = m_memState.lCounts[i]; + m_lSizes[i] = m_memState.lSizes[i]; + } + m_lHighWaterCount = m_memState.lHighWaterCount; + m_lTotalCount = m_memState.lTotalCount; +} + +// fills 'this' with the difference, returns TRUE if significant +BOOL CMemoryState::Difference(const CMemoryState& oldState, + const CMemoryState& newState) +{ + int nResult = _CrtMemDifference(&m_memState, &oldState.m_memState, &newState.m_memState); + UpdateData(); + return nResult != 0; +} + +void CMemoryState::DumpStatistics() const +{ + _CrtMemDumpStatistics(&m_memState); +} + +// -- fill with current memory state +void CMemoryState::Checkpoint() +{ + _CrtMemCheckpoint(&m_memState); + UpdateData(); +} + +// Dump objects created after this memory state was checkpointed +// Will dump all objects if this memory state wasn't checkpointed +// Dump all objects, report about non-objects also +// List request number in {} +void CMemoryState::DumpAllObjectsSince() const +{ + _CrtMemDumpAllObjectsSince(&m_memState); +} + +///////////////////////////////////////////////////////////////////////////// +// Enumerate all objects allocated in the diagnostic memory heap + +struct _AFX_ENUM_CONTEXT +{ + void (*m_pfn)(CObject*,void*); + void* m_pContext; +}; + +AFX_STATIC void _AfxDoForAllObjectsProxy(void* pObject, void* pContext) +{ + _AFX_ENUM_CONTEXT* p = (_AFX_ENUM_CONTEXT*)pContext; + (*p->m_pfn)((CObject*)pObject, p->m_pContext); +} + +void AFXAPI +AfxDoForAllObjects(void (AFX_CDECL *pfn)(CObject*, void*), void* pContext) +{ + if (pfn == NULL) + { + AfxThrowInvalidArgException(); + } + _AFX_ENUM_CONTEXT context; + context.m_pfn = pfn; + context.m_pContext = pContext; + _CrtDoForAllClientObjects(_AfxDoForAllObjectsProxy, &context); +} + +///////////////////////////////////////////////////////////////////////////// +// Automatic debug memory diagnostics + +BOOL AFXAPI AfxDumpMemoryLeaks() +{ + return _CrtDumpMemoryLeaks(); +} + +#endif // _AFX_NO_DEBUG_CRT +#endif // _DEBUG + +///////////////////////////////////////////////////////////////////////////// +// Non-diagnostic memory routines + +int AFX_CDECL AfxNewHandler(size_t /* nSize */) +{ + AfxThrowMemoryException(); +} + +#pragma warning(disable: 4273) + +#ifndef _AFXDLL +_PNH _afxNewHandler = &AfxNewHandler; +#endif + +_PNH AFXAPI AfxGetNewHandler(void) +{ +#ifdef _AFXDLL + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + return pState->m_pfnNewHandler; +#else + return _afxNewHandler; +#endif +} + +_PNH AFXAPI AfxSetNewHandler(_PNH pfnNewHandler) +{ +#ifdef _AFXDLL + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + _PNH pfnOldHandler = pState->m_pfnNewHandler; + pState->m_pfnNewHandler = pfnNewHandler; + return pfnOldHandler; +#else + _PNH pfnOldHandler = _afxNewHandler; + _afxNewHandler = pfnNewHandler; + return pfnOldHandler; +#endif +} + +AFX_STATIC_DATA const _PNH _pfnUninitialized = (_PNH)-1; + +void* __cdecl operator new(size_t nSize) +{ + void* pResult; +#ifdef _AFXDLL + _PNH pfnNewHandler = _pfnUninitialized; +#endif + for (;;) + { +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + pResult = _malloc_dbg(nSize, _NORMAL_BLOCK, NULL, 0); +#else + pResult = malloc(nSize); +#endif + if (pResult != NULL) + return pResult; + +#ifdef _AFXDLL + if (pfnNewHandler == _pfnUninitialized) + { + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + pfnNewHandler = pState->m_pfnNewHandler; + } + if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0) + break; +#else + if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0) + break; +#endif + } + return pResult; +} + +void __cdecl operator delete(void* p) +{ +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + _free_dbg(p, _NORMAL_BLOCK); +#else + free(p); +#endif +} + +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize) +{ + return ::operator new(nSize); +} + +void __cdecl operator delete[](void* p) +{ + ::operator delete(p); +} +#endif + +#ifdef _DEBUG + +void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine) +{ +#ifdef _AFX_NO_DEBUG_CRT + UNUSED_ALWAYS(nType); + UNUSED_ALWAYS(lpszFileName); + UNUSED_ALWAYS(nLine); + return ::operator new(nSize); +#else + void* pResult; +#ifdef _AFXDLL + _PNH pfnNewHandler = _pfnUninitialized; +#endif + for (;;) + { + pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine); + if (pResult != NULL) + return pResult; + +#ifdef _AFXDLL + if (pfnNewHandler == _pfnUninitialized) + { + AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); + pfnNewHandler = pState->m_pfnNewHandler; + } + if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0) + break; +#else + if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0) + break; +#endif + } + return pResult; +#endif +} + +#if 0 +#if _MSC_VER >= 1200 +void __cdecl operator delete(void* p, int nType, LPCSTR /* lpszFileName */, int /* nLine */) +{ +#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG) + _free_dbg(p, nType); +#else + free(p); +#endif +} +#endif // _MSC_VER >= 1200 +#endif + +#if _MSC_VER >= 1210 +void* __cdecl operator new[](size_t nSize, int nType, LPCSTR lpszFileName, int nLine) +{ + return ::operator new(nSize, nType, lpszFileName, nLine); +} +#if 0 +void __cdecl operator delete[](void* p, int nType, LPCSTR lpszFileName, int nLine) +{ + ::operator delete(p, nType, lpszFileName, nLine); +} +#endif +#endif // _MSC_VER >= 1210 + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// diff --git a/public/tier0/annotations.h b/public/tier0/annotations.h new file mode 100644 index 0000000..5e946fc --- /dev/null +++ b/public/tier0/annotations.h @@ -0,0 +1,116 @@ +#ifndef ANALYSIS_ANNOTATIONS_H +#define ANALYSIS_ANNOTATIONS_H + +#if _MSC_VER >= 1600 // VS 2010 and above. +//----------------------------------------------------------------------------- +// Upgrading important helpful warnings to errors +//----------------------------------------------------------------------------- +#pragma warning(error : 4789 ) // warning C4789: destination of memory copy is too small + +// Suppress some code analysis warnings +#ifdef _PREFAST_ +// Include the annotation header file. +#include + +// /Analyze warnings can only be suppressed when using a compiler that supports them, which VS 2010 +// Professional does not. + +// We don't care about these warnings because they are bugs that can only occur during resource +// exhaustion or other unexpected API failure, which we are nowhere near being able to handle. +#pragma warning(disable : 6308) // warning C6308: 'realloc' might return null pointer: assigning null pointer to 's_ppTestCases', which is passed as an argument to 'realloc', will cause the original memory block to be leaked +#pragma warning(disable : 6255) // warning C6255: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead +#pragma warning(disable : 6387) // warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'GetProcAddress' +#pragma warning(disable : 6309) // warning C6309: Argument '1' is null: this does not adhere to function specification of 'GetProcAddress' +#pragma warning(disable : 6011) // warning C6011: Dereferencing NULL pointer 'm_ppTestCases' +#pragma warning(disable : 6211) // warning C6211: Leaking memory 'newKeyValue' due to an exception. Consider using a local catch block to clean up memory +#pragma warning(disable : 6031) // warning C6031: Return value ignored: '_getcwd' + +// These warnings are because /analyze doesn't like our use of constants, especially things like IsPC() +#pragma warning(disable : 6326) // warning C6326: Potential comparison of a constant with another constant +#pragma warning(disable : 6239) // warning C6239: ( && ) always evaluates to the result of . Did you intend to use the bitwise-and operator? +#pragma warning(disable : 6285) // warning C6285: ( || ) is always a non-zero constant. Did you intend to use the bitwise-and operator? +#pragma warning(disable : 6237) // warning C6237: ( && ) is always zero. is never evaluated and might have side effects +#pragma warning(disable : 6235) // warning C6235: ( || ) is always a non-zero constant +#pragma warning(disable : 6240) // warning C6240: ( && ) always evaluates to the result of . Did you intend to use the bitwise-and operator? + +// These warnings aren't really important: +#pragma warning(disable : 6323) // warning C6323: Use of arithmetic operator on Boolean type(s) + +// Miscellaneous other /analyze warnings. We should consider fixing these at some point. +//#pragma warning(disable : 6204) // warning C6204: Possible buffer overrun in call to 'memcpy': use of unchecked parameter 'src' +//#pragma warning(disable : 6262) // warning C6262: Function uses '16464' bytes of stack: exceeds /analyze:stacksize'16384'. Consider moving some data to heap +// This is a serious warning. Don't suppress it. +//#pragma warning(disable : 6263) // warning C6263: Using _alloca in a loop: this can quickly overflow stack +// 6328 is also used for passing __int64 to printf when int is expected so we can't suppress it. +//#pragma warning(disable : 6328) // warning C6328: 'char' passed as parameter '1' when 'unsigned char' is required in call to 'V_isdigit' +// /analyze doesn't like GCOMPILER_ASSERT's implementation of compile-time asserts +#pragma warning(disable : 6326) // warning C6326: Potential comparison of a constant with another constant +#pragma warning(disable : 6335) // warning C6335: Leaking process information handle 'pi.hThread' +#pragma warning(disable : 6320) // warning C6320: Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled +#pragma warning(disable : 6250) // warning C6250: Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks +#pragma warning(disable : 6384) // ientity2_class_h_schema_gen.cpp(76): warning C6384: Dividing sizeof a pointer by another value + +// For temporarily suppressing warnings -- the warnings are suppressed for the next source line. +#define ANALYZE_SUPPRESS(wnum) __pragma(warning(suppress: wnum)) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) __pragma(warning(supress: wnum1 wnum2)) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) __pragma(warning(suppress: wnum1 wnum2 wnum3)) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) __pragma(warning(suppress: wnum1 wnum2 wnum3 wnum4)) + +// Tag all printf style format strings with this +#define PRINTF_FORMAT_STRING _Printf_format_string_ +#define SCANF_FORMAT_STRING _Scanf_format_string_impl_ +// Various macros for specifying the capacity of the buffer pointed +// to by a function parameter. Variations include in/out/inout, +// CAP (elements) versus BYTECAP (bytes), and null termination or +// not (_Z). +#define IN_Z _In_z_ +#define IN_CAP(x) _In_count_(x) +#define IN_BYTECAP(x) _In_bytecount_(x) +#define OUT_Z_CAP(x) _Out_z_cap_(x) +#define OUT_CAP(x) _Out_cap_(x) +#define OUT_BYTECAP(x) _Out_bytecap_(x) +#define OUT_Z_BYTECAP(x) _Out_z_bytecap_(x) +#define INOUT_BYTECAP(x) _Inout_bytecap_(x) +#define INOUT_Z_CAP(x) _Inout_z_cap_(x) +#define INOUT_Z_BYTECAP(x) _Inout_z_bytecap_(x) +// These macros are use for annotating array reference parameters, typically used in functions +// such as V_strcpy_safe. Because they are array references the capacity is already known. +#if _MSC_VER >= 1700 +#define IN_Z_ARRAY _Pre_z_ +#define OUT_Z_ARRAY _Post_z_ +#define INOUT_Z_ARRAY _Prepost_z_ +#else +#define IN_Z_ARRAY _Deref_pre_z_ +#define OUT_Z_ARRAY _Deref_post_z_ +#define INOUT_Z_ARRAY _Deref_prepost_z_ +#endif // _MSC_VER >= 1700 +// Use the macros above to annotate string functions that fill buffers as shown here, +// in order to give VS's /analyze more opportunities to find bugs. +// void V_wcsncpy( OUT_Z_BYTECAP(maxLenInBytes) wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ); +// int V_snwprintf( OUT_Z_CAP(maxLenInCharacters) wchar_t *pDest, int maxLenInCharacters, PRINTF_FORMAT_STRING const wchar_t *pFormat, ... ); + +#endif // _PREFAST_ +#endif // _MSC_VER >= 1600 // VS 2010 and above. + +#ifndef ANALYZE_SUPPRESS +#define ANALYZE_SUPPRESS(wnum) +#define ANALYZE_SUPPRESS2(wnum1, wnum2) +#define ANALYZE_SUPPRESS3(wnum1, wnum2, wnum3) +#define ANALYZE_SUPPRESS4(wnum1, wnum2, wnum3, wnum4) +#define PRINTF_FORMAT_STRING +#define SCANF_FORMAT_STRING +#define IN_Z +#define IN_CAP(x) +#define IN_BYTECAP(x) +#define OUT_Z_CAP(x) +#define OUT_CAP(x) +#define OUT_BYTECAP(x) +#define OUT_Z_BYTECAP(x) +#define INOUT_BYTECAP(x) +#define INOUT_Z_CAP(x) +#define INOUT_Z_BYTECAP(x) +#define OUT_Z_ARRAY +#define INOUT_Z_ARRAY +#endif + +#endif // ANALYSIS_ANNOTATIONS_H \ No newline at end of file diff --git a/public/tier0/basetypes.h b/public/tier0/basetypes.h new file mode 100644 index 0000000..c200cc4 --- /dev/null +++ b/public/tier0/basetypes.h @@ -0,0 +1,557 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BASETYPES_H +#define BASETYPES_H + +#ifdef COMPILER_MSVC +#pragma once +#endif + +// This is a trick to get the DLL extension off the -D option on the command line. +#define DLLExtTokenPaste(x) #x +#define DLLExtTokenPaste2(x) DLLExtTokenPaste(x) +#define DLL_EXT_STRING DLLExtTokenPaste2( _DLL_EXT ) + +////////////////////////////////////////////////////////////////////////// + +#ifndef schema +#define schema namespace ValveSchemaMarker {} +#endif + +#ifdef COMPILING_SCHEMA +#define UNSCHEMATIZED_METHOD( x ) +#else +#define UNSCHEMATIZED_METHOD( x ) x +#endif + +////////////////////////////////////////////////////////////////////////// + +#include "tier0/platform.h" +#include "commonmacros.h" +#include "wchartypes.h" + +#include "tier0/valve_off.h" + +// There's a different version of this file in the xbox codeline +// so the PC version built in the xbox branch includes things like +// tickrate changes. +#include "xbox_codeline_defines.h" + +// stdio.h +#ifndef NULL +#define NULL 0 +#endif + + +#ifdef POSIX +template +T abs( const T &a ) +{ + if ( a < 0 ) + return -a; + else + return a; +} +#endif + + +#define ExecuteNTimes( nTimes, x ) \ + { \ + static int __executeCount=0;\ + if ( __executeCount < nTimes )\ + { \ + ++__executeCount; \ + x; \ + } \ + } + + +#define ExecuteOnce( x ) ExecuteNTimes( 1, x ) + + + +// Pad a number so it lies on an N byte boundary. +// So PAD_NUMBER(0,4) is 0 and PAD_NUMBER(1,4) is 4 +#define PAD_NUMBER(number, boundary) \ + ( ((number) + ((boundary)-1)) / (boundary) ) * (boundary) + +// In case this ever changes +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +// #define COMPILETIME_MAX and COMPILETIME_MIN for max/min in constant expressions +#define COMPILETIME_MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#define COMPILETIME_MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#ifndef MIN +#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef MAX +#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + + + +#if !defined(_X360) +FORCEINLINE float fpmin( float a, float b ) +{ + return ( a < b ) ? a : b; +} + +FORCEINLINE float fpmax( float a, float b ) +{ + return ( a > b ) ? a : b; +} +#endif + +#ifdef __cplusplus + +template< class T, class Y > +inline T clamp( T const &val, Y const &minVal, Y const &maxVal ) +{ + if( val < minVal ) + return minVal; + else if( val > maxVal ) + return maxVal; + else + return val; +} + +#else + +#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) + +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE (!FALSE) +#endif + +//----------------------------------------------------------------------------- +// fsel +//----------------------------------------------------------------------------- +#ifndef _X360 + +#define fsel(c,x,y) ( (c) >= 0 ? (x) : (y) ) + +// integer conditional move +// if a >= 0, return x, else y +#define isel(a,x,y) ( ((a) >= 0) ? (x) : (y) ) + +// if x = y, return a, else b +#define ieqsel(x,y,a,b) (( (x) == (y) ) ? (a) : (b)) + +// if the nth bit of a is set (counting with 0 = LSB), +// return x, else y +// this is fast if nbit is a compile-time immediate +#define ibitsel(a, nbit, x, y) ( ( ((a) & (1 << (nbit))) != 0 ) ? (x) : (y) ) + +#else + +// __fsel(double fComparand, double fValGE, double fLT) == fComparand >= 0 ? fValGE : fLT +// this is much faster than if ( aFloat > 0 ) { x = .. } +// the XDK defines two intrinsics, one for floats and one for doubles -- it's the same +// opcode, but the __fself version tells the compiler not to do a wasteful unnecessary +// rounding op after each sel. +// #define fsel __fsel +FORCEINLINE double fsel(double fComparand, double fValGE, double fLT) { return __fsel( fComparand, fValGE, fLT ); } +FORCEINLINE float fsel(float fComparand, float fValGE, float fLT) { return __fself( fComparand, fValGE, fLT ); } + +// if a >= 0, return x, else y +FORCEINLINE int isel( int a, int x, int y ) +{ + int mask = a >> 31; // arithmetic shift right, splat out the sign bit + return x + ((y - x) & mask); +}; + +// if a >= 0, return x, else y +FORCEINLINE unsigned isel( int a, unsigned x, unsigned y ) +{ + int mask = a >> 31; // arithmetic shift right, splat out the sign bit + return x + ((y - x) & mask); +}; + +// ( x == y ) ? a : b +FORCEINLINE unsigned ieqsel( unsigned x, unsigned y, unsigned a, unsigned b ) +{ + unsigned mask = (x == y) ? 0 : -1; + return a + ((b - a) & mask); +}; + +// ( x == y ) ? a : b +FORCEINLINE int ieqsel( int x, int y, int a, int b ) +{ + int mask = (x == y) ? 0 : -1; + return a + ((b - a) & mask); +}; + +// if the nth bit of a is set (counting with 0 = LSB), +// return x, else y +// this is fast if nbit is a compile-time immediate +#define ibitsel(a, nbit, x, y) ( (x) + (((y) - (x)) & (((a) & (1 << (nbit))) ? 0 : -1)) ) + +#endif + +#if CROSS_PLATFORM_VERSION < 1 + +#ifndef DONT_DEFINE_BOOL // Needed for Cocoa stuff to compile. +typedef int BOOL; +#endif + +typedef int qboolean; +//typedef uint32 ULONG; +typedef uint8 BYTE; +typedef uint8 byte; +typedef uint16 word; +#endif + +#ifdef _WIN32 +typedef wchar_t ucs2; // under windows wchar_t is ucs2 +#else +typedef unsigned short ucs2; +#endif + +enum ThreeState_t +{ + TRS_FALSE, + TRS_TRUE, + TRS_NONE, +}; + +typedef float vec_t; +#ifdef _WIN32 +typedef __int32 vec_t_as_gpr; // a general purpose register type equal in size to a vec_t (in case we have to avoid the fpu for some reason) +#endif + + +template +inline T AlignValue( T val, uintp alignment ) +{ + return (T)( ( (uintp)val + alignment - 1 ) & ~( alignment - 1 ) ); +} + + +// FIXME: this should move +#ifndef __cplusplus +#define true TRUE +#define false FALSE +#endif + +//----------------------------------------------------------------------------- +// look for NANs, infinities, and underflows. +// This assumes the ANSI/IEEE 754-1985 standard +//----------------------------------------------------------------------------- + +#ifdef __cplusplus + +inline uint32& FloatBits( vec_t& f ) +{ + return *reinterpret_cast((char*)(&f)); +} + +inline uint32 const FloatBits( const vec_t &f ) +{ + union Convertor_t + { + vec_t f; + uint32 ul; + }tmp; + tmp.f = f; + return tmp.ul; +} + +inline vec_t BitsToFloat( uint32 i ) +{ + union Convertor_t + { + vec_t f; + unsigned long ul; + }tmp; + tmp.ul = i; + return tmp.f; +} + +inline bool IsFinite( const vec_t &f ) +{ +#if _X360 + return f == f && fabs(f) <= FLT_MAX; +#else + return ((FloatBits(f) & 0x7F800000) != 0x7F800000); +#endif +} + +inline uint32 FloatAbsBits( vec_t f ) +{ + return FloatBits(f) & 0x7FFFFFFF; +} + +inline float FloatMakeNegative( vec_t f ) +{ + return BitsToFloat( FloatBits(f) | 0x80000000 ); +} + +#if defined( WIN32 ) + +//#include +// Just use prototype from math.h +#ifdef __cplusplus +extern "C" +{ +#endif + double __cdecl fabs(double); +#ifdef __cplusplus +} +#endif + +// In win32 try to use the intrinsic fabs so the optimizer can do it's thing inline in the code +#pragma intrinsic( fabs ) +// Also, alias float make positive to use fabs, too +// NOTE: Is there a perf issue with double<->float conversion? +inline float FloatMakePositive( vec_t f ) +{ + return (float)fabs( f ); +} +#else +inline float FloatMakePositive( vec_t f ) +{ + return BitsToFloat( FloatBits(f) & 0x7FFFFFFF ); +} +#endif + +inline float FloatNegate( vec_t f ) +{ + return BitsToFloat( FloatBits(f) ^ 0x80000000 ); +} + + +#define FLOAT32_NAN_BITS (uint32)0x7FC00000 // not a number! +#define FLOAT32_NAN BitsToFloat( FLOAT32_NAN_BITS ) + +#define VEC_T_NAN FLOAT32_NAN + +#endif + +// FIXME: why are these here? Hardly anyone actually needs them. +struct color24 +{ + byte r, g, b; +}; + +typedef struct color32_s +{ + bool operator!=( const struct color32_s &other ) const; + byte r, g, b, a; + + // assign and copy by using the whole register rather + // than byte-by-byte copy. (No, the compiler is not + // smart enough to do this for you. /FAcs if you + // don't believe me.) + inline unsigned *asInt(void) { return reinterpret_cast(this); } + inline const unsigned *asInt(void) const { return reinterpret_cast(this); } + // This thing is in a union elsewhere, and union members can't have assignment + // operators, so you have to explicitly assign using this, or be slow. SUCK. + inline void Copy(const color32_s &rhs) + { + *asInt() = *rhs.asInt(); + } + +} color32; + +inline bool color32::operator!=( const color32 &other ) const +{ + return r != other.r || g != other.g || b != other.b || a != other.a; +} + +struct colorVec +{ + unsigned r, g, b, a; +}; + + +#ifndef NOTE_UNUSED +#define NOTE_UNUSED(x) (x = x) // for pesky compiler / lint warnings +#endif +#ifdef __cplusplus + +struct vrect_t +{ + int x,y,width,height; + vrect_t *pnext; +}; + +#endif + + +//----------------------------------------------------------------------------- +// MaterialRect_t struct - used for DrawDebugText +//----------------------------------------------------------------------------- +struct Rect_t +{ + int x, y; + int width, height; +}; + +struct Rect3D_t +{ + int x, y, z; + int width, height, depth; + + FORCEINLINE Rect3D_t( int nX, int nY, int nZ, int nWidth, int nHeight, int nDepth ) + { + x = nX; + y = nY; + z = nZ; + width = nWidth; + height = nHeight; + depth = nDepth; + } + + FORCEINLINE Rect3D_t( void ) + { + } + +}; + + + +//----------------------------------------------------------------------------- +// Interval, used by soundemittersystem + the game +//----------------------------------------------------------------------------- +struct interval_t +{ + float start; + float range; +}; + + +//----------------------------------------------------------------------------- +// Declares a type-safe handle type; you can't assign one handle to the next +//----------------------------------------------------------------------------- + +// 32-bit pointer handles. + +// Typesafe 8-bit and 16-bit handles. +template< class HandleType > +class CBaseIntHandle +{ +public: + + inline bool operator==( const CBaseIntHandle &other ) { return m_Handle == other.m_Handle; } + inline bool operator!=( const CBaseIntHandle &other ) { return m_Handle != other.m_Handle; } + + // Only the code that doles out these handles should use these functions. + // Everyone else should treat them as a transparent type. + inline HandleType GetHandleValue() { return m_Handle; } + inline void SetHandleValue( HandleType val ) { m_Handle = val; } + + typedef HandleType HANDLE_TYPE; + +protected: + + HandleType m_Handle; +}; + +template< class DummyType > +class CIntHandle16 : public CBaseIntHandle< unsigned short > +{ +public: + inline CIntHandle16() {} + + static inline CIntHandle16 MakeHandle( HANDLE_TYPE val ) + { + return CIntHandle16( val ); + } + +protected: + inline CIntHandle16( HANDLE_TYPE val ) + { + m_Handle = val; + } +}; + + +template< class DummyType > +class CIntHandle32 : public CBaseIntHandle< uint32 > +{ +public: + inline CIntHandle32() {} + + static inline CIntHandle32 MakeHandle( HANDLE_TYPE val ) + { + return CIntHandle32( val ); + } + +protected: + inline CIntHandle32( HANDLE_TYPE val ) + { + m_Handle = val; + } +}; + + +// NOTE: This macro is the same as windows uses; so don't change the guts of it +#define DECLARE_HANDLE_16BIT(name) typedef CIntHandle16< struct name##__handle * > name; +#define DECLARE_HANDLE_32BIT(name) typedef CIntHandle32< struct name##__handle * > name; + +#define DECLARE_POINTER_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name +#define FORWARD_DECLARE_HANDLE(name) typedef struct name##__ *name + +#define DECLARE_DERIVED_POINTER_HANDLE( _name, _basehandle ) struct _name##__ : public _basehandle##__ {}; typedef struct _name##__ *_name +#define DECLARE_ALIASED_POINTER_HANDLE( _name, _alias ) typedef struct _alias##__ *name + +// @TODO: Find a better home for this +#if !defined(_STATIC_LINKED) && !defined(PUBLISH_DLL_SUBSYSTEM) +// for platforms built with dynamic linking, the dll interface does not need spoofing +#define PUBLISH_DLL_SUBSYSTEM() +#endif + +#define UID_PREFIX generated_id_ +#define UID_CAT1(a,c) a ## c +#define UID_CAT2(a,c) UID_CAT1(a,c) +#define EXPAND_CONCAT(a,c) UID_CAT1(a,c) +#ifdef _MSC_VER +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__COUNTER__) +#else +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__LINE__) +#endif + +#define _MKSTRING(arg) #arg +#define MKSTRING(arg) _MKSTRING(arg) + + +// this allows enumerations to be used as flags, and still remain type-safe! +#define DEFINE_ENUM_BITWISE_OPERATORS( Type ) \ + inline Type operator| ( Type a, Type b ) { return Type( int( a ) | int( b ) ); } \ + inline Type operator& ( Type a, Type b ) { return Type( int( a ) & int( b ) ); } \ + inline Type operator^ ( Type a, Type b ) { return Type( int( a ) ^ int( b ) ); } \ + inline Type operator<< ( Type a, int b ) { return Type( int( a ) << b ); } \ + inline Type operator>> ( Type a, int b ) { return Type( int( a ) >> b ); } \ + inline Type &operator|= ( Type &a, Type b ) { return a = a | b; } \ + inline Type &operator&= ( Type &a, Type b ) { return a = a & b; } \ + inline Type &operator^= ( Type &a, Type b ) { return a = a ^ b; } \ + inline Type &operator<<=( Type &a, int b ) { return a = a << b; } \ + inline Type &operator>>=( Type &a, int b ) { return a = a >> b; } \ + inline Type operator~( Type a ) { return Type( ~int( a ) ); } + +// defines increment/decrement operators for enums for easy iteration +#define DEFINE_ENUM_INCREMENT_OPERATORS( Type ) \ + inline Type &operator++( Type &a ) { return a = Type( int( a ) + 1 ); } \ + inline Type &operator--( Type &a ) { return a = Type( int( a ) - 1 ); } \ + inline Type operator++( Type &a, int ) { Type t = a; ++a; return t; } \ + inline Type operator--( Type &a, int ) { Type t = a; --a; return t; } + +#define MAX_SPLITSCREEN_CLIENT_BITS 2 +// this should == MAX_JOYSTICKS in InputEnums.h +#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 + +#include "tier0/valve_on.h" + +#endif // BASETYPES_H diff --git a/public/tier0/cache_hints.h b/public/tier0/cache_hints.h new file mode 100644 index 0000000..3d77a3c --- /dev/null +++ b/public/tier0/cache_hints.h @@ -0,0 +1,26 @@ +#ifndef TIER0_CACHE_HINTS_HDR +#define TIER0_CACHE_HINTS_HDR + +#if defined(_X360) +#define PREFETCH_CACHE_LINE(POINTER,OFFSET) {__dcbt((OFFSET), (POINTER));} +#elif defined(WIN32) +#define PREFETCH_CACHE_LINE(POINTER,OFFSET) {_mm_prefetch((const char*)(POINTER),(OFFSET));} +#else +#define PREFETCH_CACHE_LINE(POINTER,OFFSET) {} +#endif + +#ifdef IVP_VECTOR_INCLUDED +template +inline void UnsafePrefetchLastElementOf(IVP_U_Vector&array) +{ + PREFETCH_CACHE_LINE(array.element_at(array.len()-1),0); +} +template +inline void PrefetchLastElementOf(IVP_U_Vector&array) +{ + if(array.len() > 0) + PREFETCH_CACHE_LINE(array.element_at(array.len()-1),0); +} +#endif + +#endif \ No newline at end of file diff --git a/public/tier0/commonmacros.h b/public/tier0/commonmacros.h new file mode 100644 index 0000000..c052502 --- /dev/null +++ b/public/tier0/commonmacros.h @@ -0,0 +1,149 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef COMMONMACROS_H +#define COMMONMACROS_H + +#ifdef _WIN32 +#pragma once +#endif + +// ------------------------------------------------------- +// +// commonmacros.h +// +// This should contain ONLY general purpose macros that are +// appropriate for use in engine/launcher/all tools +// +// ------------------------------------------------------- + +// Makes a 4-byte "packed ID" int out of 4 characters +#define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) ) + +// Compares a string with a 4-byte packed ID constant +#define STRING_MATCHES_ID( p, id ) ( (*((int *)(p)) == (id) ) ? true : false ) +#define ID_TO_STRING( id, p ) ( (p)[3] = (((id)>>24) & 0xFF), (p)[2] = (((id)>>16) & 0xFF), (p)[1] = (((id)>>8) & 0xFF), (p)[0] = (((id)>>0) & 0xFF) ) + +#define Q_ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + +#define SETBITS(iBitVector, bits) ((iBitVector) |= (bits)) +#define CLEARBITS(iBitVector, bits) ((iBitVector) &= ~(bits)) +#define FBitSet(iBitVector, bits) ((iBitVector) & (bits)) + +inline bool IsPowerOfTwo( int value ) +{ + return (value & ( value - 1 )) == 0; +} + +#ifndef REFERENCE +#define REFERENCE(arg) ((void)arg) +#endif + +#define CONST_INTEGER_AS_STRING(x) #x //Wraps the integer in quotes, allowing us to form constant strings with it +#define __HACK_LINE_AS_STRING__(x) CONST_INTEGER_AS_STRING(x) //__LINE__ can only be converted to an actual number by going through this, otherwise the output is literally "__LINE__" +#define __LINE__AS_STRING __HACK_LINE_AS_STRING__(__LINE__) //Gives you the line number in constant string form + +// Using ARRAYSIZE implementation from winnt.h: +#ifdef ARRAYSIZE +#undef ARRAYSIZE +#endif + +// Return the number of elements in a statically sized array. +// DWORD Buffer[100]; +// RTL_NUMBER_OF(Buffer) == 100 +// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc. +// +#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0])) + +#if defined(__cplusplus) && \ + !defined(MIDL_PASS) && \ + !defined(RC_INVOKED) && \ + !defined(_PREFAST_) && \ + (_MSC_FULL_VER >= 13009466) && \ + !defined(SORTPP_PASS) + +// From crtdefs.h +#if !defined(UNALIGNED) +#if defined(_M_IA64) || defined(_M_AMD64) +#define UNALIGNED __unaligned +#else +#define UNALIGNED +#endif +#endif + +// RtlpNumberOf is a function that takes a reference to an array of N Ts. +// +// typedef T array_of_T[N]; +// typedef array_of_T &reference_to_array_of_T; +// +// RtlpNumberOf returns a pointer to an array of N chars. +// We could return a reference instead of a pointer but older compilers do not accept that. +// +// typedef char array_of_char[N]; +// typedef array_of_char *pointer_to_array_of_char; +// +// sizeof(array_of_char) == N +// sizeof(*pointer_to_array_of_char) == N +// +// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T); +// +// We never even call RtlpNumberOf, we just take the size of dereferencing its return type. +// We do not even implement RtlpNumberOf, we just decare it. +// +// Attempts to pass pointers instead of arrays to this macro result in compile time errors. +// That is the point. +extern "C++" // templates cannot be declared to have 'C' linkage +template +char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N]; + +#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A))) + +// This does not work with: +// +// void Foo() +// { +// struct { int x; } y[2]; +// RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation +// } +// +// You must instead do: +// +// struct Foo1 { int x; }; +// +// void Foo() +// { +// Foo1 y[2]; +// RTL_NUMBER_OF_V2(y); // ok +// } +// +// OR +// +// void Foo() +// { +// struct { int x; } y[2]; +// RTL_NUMBER_OF_V1(y); // ok +// } +// +// OR +// +// void Foo() +// { +// struct { int x; } y[2]; +// _ARRAYSIZE(y); // ok +// } + +#else +#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A) +#endif + +// ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2 +// _ARRAYSIZE is a version useful for anonymous types +#define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A) +#define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A) + +#endif // COMMONMACROS_H + diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h new file mode 100644 index 0000000..fe8b88a --- /dev/null +++ b/public/tier0/dbg.h @@ -0,0 +1,643 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ========// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#ifndef DBG_H +#define DBG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/basetypes.h" +#include "dbgflag.h" +#include "logging.h" +#include +#include +#include + +#ifdef POSIX +#define __cdecl +#endif + +//----------------------------------------------------------------------------- +// dll export stuff +//----------------------------------------------------------------------------- + +class Color; + + +//----------------------------------------------------------------------------- +// Usage model for the Dbg library +// +// 1. Assertions. +// +// Assertions are used to detect and warn about invalid states +// +// To use an assertion, use +// +// Assert( (f == 5) ); +// AssertMsg( (f == 5), ("F needs to be %d here!\n", 5) ); +// AssertFunc( (f == 5), BadFunc() ); +// AssertEquals( f, 5 ); +// AssertFloatEquals( f, 5.0f, 1e-3 ); +// +// The first will simply report that an assertion failed on a particular +// code file and line. The second version will display a print-f formatted message +// along with the file and line, the third will display a generic message and +// will also cause the function BadFunc to be executed, and the last two +// will report an error if f is not equal to 5 (the last one asserts within +// a particular tolerance). +// +// 2. Code activation +// +// To cause code to be run only in debug builds, use DBG_CODE: +// An example is below. +// +// DBG_CODE( +// { +// int x = 5; +// ++x; +// } +// ); +// +// Code can be activated based on the dynamic spew groups also. Use +// +// DBG_DCODE( "group", level, +// { int x = 5; ++x; } +// ); +// +// 3. Breaking into the debugger. +// +// To cause an unconditional break into the debugger in debug builds only, use DBG_BREAK +// +// DBG_BREAK(); +// +// You can force a break in any build (release or debug) using +// +// DebuggerBreak(); +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE void _ExitOnFatalAssert( const tchar* pFile, int line ); +PLATFORM_INTERFACE bool ShouldUseNewAssertDialog(); + +PLATFORM_INTERFACE bool SetupWin32ConsoleIO(); + +// Returns true if they want to break in the debugger. +PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFile, int line, const tchar *pExpression ); + +/* Used to define macros, never use these directly. */ + +#define _AssertMsg( _exp, _msg, _executeExp, _bFatal ) \ + do { \ + if (!(_exp)) \ + { \ + LoggingResponse_t ret = Log_Assert( "%s (%d) : %s\n", __TFILE__, __LINE__, _msg ); \ + _executeExp; \ + if ( ret == LR_DEBUGGER ) \ + { \ + if ( !ShouldUseNewAssertDialog() || DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ + DebuggerBreak(); \ + if ( _bFatal ) \ + _ExitOnFatalAssert( __TFILE__, __LINE__ ); \ + } \ + } \ + } while (0) + +#define _AssertMsgOnce( _exp, _msg, _bFatal ) \ + do { \ + static bool fAsserted; \ + if (!fAsserted ) \ + { \ + _AssertMsg( _exp, _msg, (fAsserted = true), _bFatal ); \ + } \ + } while (0) + +/* Spew macros... */ + +// AssertFatal macros +// AssertFatal is used to detect an unrecoverable error condition. +// If enabled, it may display an assert dialog (if DBGFLAG_ASSERTDLG is turned on or running under the debugger), +// and always terminates the application + +#ifdef DBGFLAG_ASSERTFATAL + +#define AssertFatal( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), true ) +#define AssertFatalOnce( _exp ) _AssertMsgOnce( _exp, _T("Assertion Failed: ") _T(#_exp), true ) +#define AssertFatalMsg( _exp, _msg ) _AssertMsg( _exp, _msg, ((void)0), true ) +#define AssertFatalMsgOnce( _exp, _msg ) _AssertMsgOnce( _exp, _msg, true ) +#define AssertFatalFunc( _exp, _f ) _AssertMsg( _exp, _T("Assertion Failed: " _T(#_exp), _f, true ) +#define AssertFatalEquals( _exp, _expectedValue ) AssertFatalMsg2( (_exp) == (_expectedValue), _T("Expected %d but got %d!"), (_expectedValue), (_exp) ) +#define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) AssertFatalMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) +#define VerifyFatal( _exp ) AssertFatal( _exp ) +#define VerifyEqualsFatal( _exp, _expectedValue ) AssertFatalEquals( _exp, _expectedValue ) + +#define AssertFatalMsg1( _exp, _msg, a1 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1 ))) +#define AssertFatalMsg2( _exp, _msg, a1, a2 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2 ))) +#define AssertFatalMsg3( _exp, _msg, a1, a2, a3 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3 ))) +#define AssertFatalMsg4( _exp, _msg, a1, a2, a3, a4 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4 ))) +#define AssertFatalMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5 ))) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5, a6 ))) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5, a6 ))) +#define AssertFatalMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5, a6, a7 ))) +#define AssertFatalMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5, a6, a7, a8 ))) +#define AssertFatalMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) AssertFatalMsg( _exp, (const tchar *)(CDbgFmtMsg( _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ))) + +#else // DBGFLAG_ASSERTFATAL + +#define AssertFatal( _exp ) ((void)0) +#define AssertFatalOnce( _exp ) ((void)0) +#define AssertFatalMsg( _exp, _msg ) ((void)0) +#define AssertFatalMsgOnce( _exp, _msg ) ((void)0) +#define AssertFatalFunc( _exp, _f ) ((void)0) +#define AssertFatalEquals( _exp, _expectedValue ) ((void)0) +#define AssertFatalFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define VerifyFatal( _exp ) (_exp) +#define VerifyEqualsFatal( _exp, _expectedValue ) (_exp) + +#define AssertFatalMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertFatalMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertFatalMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertFatalMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertFatalMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertFatalMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertFatalMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertFatalMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertFatalMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#endif // DBGFLAG_ASSERTFATAL + +// Assert macros +// Assert is used to detect an important but survivable error. +// It's only turned on when DBGFLAG_ASSERT is true. + +#ifdef DBGFLAG_ASSERT + +#define Assert( _exp ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) +#define AssertAligned( adr ) Assert( ( ( ( intp ) ( adr ) ) & 0xf ) == 0 ) +#define AssertMsg_( _exp, _msg ) _AssertMsg( _exp, _msg, ((void)0), false ) +#define AssertOnce( _exp ) _AssertMsgOnce( _exp, _T("Assertion Failed: ") _T(#_exp), false ) +#define AssertMsgOnce( _exp, _msg ) _AssertMsgOnce( _exp, _msg, false ) +#define AssertFunc( _exp, _f ) _AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), _f, false ) +#define AssertEquals( _exp, _expectedValue ) AssertMsg2( (_exp) == (_expectedValue), _T("Expected %d but got %d!"), (_expectedValue), (_exp) ) +#define AssertFloatEquals( _exp, _expectedValue, _tol ) AssertMsg2( fabs((_exp) - (_expectedValue)) <= (_tol), _T("Expected %f but got %f!"), (_expectedValue), (_exp) ) +#define Verify( _exp ) Assert( _exp ) +#define VerifyEquals( _exp, _expectedValue ) AssertEquals( _exp, _expectedValue ) + +#define AssertMsg( _exp, _msg ) AssertMsg_( _exp, _T( _msg ) ) +#define AssertMsg1( _exp, _msg, a1 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1 )) ) +#define AssertMsg2( _exp, _msg, a1, a2 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2 )) ) +#define AssertMsg3( _exp, _msg, a1, a2, a3 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3 )) ) +#define AssertMsg4( _exp, _msg, a1, a2, a3, a4 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4 )) ) +#define AssertMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4, a5 )) ) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4, a5, a6 )) ) +#define AssertMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4, a5, a6, a7 )) ) +#define AssertMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4, a5, a6, a7, a8 )) ) +#define AssertMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) AssertMsg_( _exp, (const tchar *)(CDbgFmtMsg( _T( _msg ), a1, a2, a3, a4, a5, a6, a7, a8, a9 )) ) + +#else // DBGFLAG_ASSERT + +#define Assert( _exp ) ((void)0) +#define AssertAligned( ptr ) ((void)0) +#define AssertOnce( _exp ) ((void)0) +#define AssertMsg( _exp, _msg ) ((void)0) +#define AssertMsgOnce( _exp, _msg ) ((void)0) +#define AssertFunc( _exp, _f ) ((void)0) +#define AssertEquals( _exp, _expectedValue ) ((void)0) +#define AssertFloatEquals( _exp, _expectedValue, _tol ) ((void)0) +#define Verify( _exp ) (_exp) +#define VerifyEquals( _exp, _expectedValue ) (_exp) + +#define AssertMsg1( _exp, _msg, a1 ) ((void)0) +#define AssertMsg2( _exp, _msg, a1, a2 ) ((void)0) +#define AssertMsg3( _exp, _msg, a1, a2, a3 ) ((void)0) +#define AssertMsg4( _exp, _msg, a1, a2, a3, a4 ) ((void)0) +#define AssertMsg5( _exp, _msg, a1, a2, a3, a4, a5 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg6( _exp, _msg, a1, a2, a3, a4, a5, a6 ) ((void)0) +#define AssertMsg7( _exp, _msg, a1, a2, a3, a4, a5, a6, a7 ) ((void)0) +#define AssertMsg8( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8 ) ((void)0) +#define AssertMsg9( _exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) ((void)0) + +#endif // DBGFLAG_ASSERT + +#define FILE_LINE_FUNCTION_STRINGIFY(x) #x +#define FILE_LINE_FUNCTION_TOSTRING(x) FILE_LINE_FUNCTION_STRINGIFY(x) +#define FILE_LINE_FUNCTION_STRING __FILE__ "(" FILE_LINE_FUNCTION_TOSTRING(__LINE__) "):" __FUNCTION__ ":" + +#define FILE_LINE_STRINGIFY(x) #x +#define FILE_LINE_TOSTRING(x) FILE_LINE_STRINGIFY(x) +#define FILE_LINE_STRING __FILE__ "(" FILE_LINE_TOSTRING(__LINE__) "):" + +#define FUNCTION_LINE_STRINGIFY(x) #x +#define FUNCTION_LINE_TOSTRING(x) FUNCTION_LINE_STRINGIFY(x) +#define FUNCTION_LINE_STRING __FUNCTION__ "(" FUNCTION_LINE_TOSTRING(__LINE__) "): " + +////////////////////////////////////////////////////////////////////////// +// Legacy Logging System +////////////////////////////////////////////////////////////////////////// + +// Channels which map the legacy logging system to the new system. + +// Channel for all default Msg/Warning/Error commands. +DECLARE_LOGGING_CHANNEL( LOG_GENERAL ); +// Channel for all asserts. +DECLARE_LOGGING_CHANNEL( LOG_ASSERT ); +// Channel for all ConMsg and ConColorMsg commands. +DECLARE_LOGGING_CHANNEL( LOG_CONSOLE ); +// Channel for all DevMsg and DevWarning commands with level < 2. +DECLARE_LOGGING_CHANNEL( LOG_DEVELOPER ); +// Channel for ConDMsg commands. +DECLARE_LOGGING_CHANNEL( LOG_DEVELOPER_CONSOLE ); +// Channel for all DevMsg and DevWarning commands with level >= 2. +DECLARE_LOGGING_CHANNEL( LOG_DEVELOPER_VERBOSE ); + +// Legacy logging functions + +PLATFORM_INTERFACE void Msg( const tchar* pMsg, ... ); +PLATFORM_INTERFACE void Warning( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_INTERFACE void Error( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_INTERFACE void Error_SpewCallStack( int iMaxCallStackLength, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +// @TODO: these callstack spew functions are currently disabled in the new logging system. Need to add support for these if desired. +PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Enable( bool bEnable ); +PLATFORM_INTERFACE void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); + +PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Enable( bool bEnable ); +PLATFORM_INTERFACE void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength ); + +PLATFORM_INTERFACE void DevMsg( int level, const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_INTERFACE void DevWarning( int level, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); + +PLATFORM_OVERLOAD void DevMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); +PLATFORM_OVERLOAD void DevWarning( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); + +PLATFORM_OVERLOAD void ConColorMsg( const Color& clr, const tchar* pMsg, ... ) FMTFUNCTION( 2, 3 ); +PLATFORM_OVERLOAD void ConMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + +PLATFORM_INTERFACE void ConDMsg( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); + +// You can use this macro like a runtime assert macro. +// If the condition fails, then Error is called with the message. This macro is called +// like AssertMsg, where msg must be enclosed in parenthesis: +// +// ErrorIfNot( bCondition, ("a b c %d %d %d", 1, 2, 3) ); +#define ErrorIfNot( condition, msg ) \ + if ( (condition) ) \ + ; \ + else \ + { \ + Error msg; \ + } + +#ifdef _DEBUG +#define DebugMsg(...) DevMsg(__VA_ARGS__) +#else +#define DebugMsg(...) +#endif + +PLATFORM_INTERFACE void COM_TimestampedLog( char const *fmt, ... ) FMTFUNCTION( 1, 2 ); + +/* Code macros, debugger interface */ + +#ifdef _DEBUG + +#define DBG_CODE( _code ) if (0) ; else { _code } +#define DBG_CODE_NOSCOPE( _code ) _code +#define DBG_DCODE( _g, _l, _code ) if (IsSpewActive( _g, _l )) { _code } else {} +#define DBG_BREAK() DebuggerBreak() /* defined in platform.h */ + +#else /* not _DEBUG */ + +#define DBG_CODE( _code ) ((void)0) +#define DBG_CODE_NOSCOPE( _code ) +#define DBG_DCODE( _g, _l, _code ) ((void)0) +#define DBG_BREAK() ((void)0) + +#endif /* _DEBUG */ + +//----------------------------------------------------------------------------- +// Macro to assist in asserting constant invariants during compilation + +#ifdef _DEBUG +#define COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;} +#define ASSERT_INVARIANT( pred ) static void UNIQUE_ID() { COMPILE_TIME_ASSERT( pred ) } +#else +#define COMPILE_TIME_ASSERT( pred ) +#define ASSERT_INVARIANT( pred ) +#endif + +#ifdef _DEBUG +template +inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE* pSource) +{ + Assert( static_cast(pSource) == dynamic_cast(pSource) ); + return static_cast(pSource); +} +#else +#define assert_cast static_cast +#endif + +//----------------------------------------------------------------------------- +// Templates to assist in validating pointers: + +// Have to use these stubs so we don't have to include windows.h here. +PLATFORM_INTERFACE void _AssertValidReadPtr( void* ptr, int count = 1 ); +PLATFORM_INTERFACE void _AssertValidWritePtr( void* ptr, int count = 1 ); +PLATFORM_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count = 1 ); + +PLATFORM_INTERFACE void AssertValidStringPtr( const tchar* ptr, int maxchar = 0xFFFFFF ); +template inline void AssertValidReadPtr( T* ptr, int count = 1 ) { _AssertValidReadPtr( (void*)ptr, count ); } +template inline void AssertValidWritePtr( T* ptr, int count = 1 ) { _AssertValidWritePtr( (void*)ptr, count ); } +template inline void AssertValidReadWritePtr( T* ptr, int count = 1 ) { _AssertValidReadWritePtr( (void*)ptr, count ); } + +#define AssertValidThis() AssertValidReadWritePtr(this,sizeof(*this)) + +//----------------------------------------------------------------------------- +// Macro to protect functions that are not reentrant + +#ifdef _DEBUG +class CReentryGuard +{ +public: + CReentryGuard(int *pSemaphore) + : m_pSemaphore(pSemaphore) + { + ++(*m_pSemaphore); + } + + ~CReentryGuard() + { + --(*m_pSemaphore); + } + +private: + int *m_pSemaphore; +}; + +#define ASSERT_NO_REENTRY() \ + static int fSemaphore##__LINE__; \ + Assert( !fSemaphore##__LINE__ ); \ + CReentryGuard ReentryGuard##__LINE__( &fSemaphore##__LINE__ ) +#else +#define ASSERT_NO_REENTRY() +#endif + +// Tier0 uses these for string functions since this abstraction is normally done in tier1. +#ifdef POSIX + #define Tier0Internal_sntprintf snprintf + #define Tier0Internal_vsntprintf vsnprintf + #define Tier0Internal_vsnprintf vsnprintf +#else + #define Tier0Internal_sntprintf _sntprintf + #define Tier0Internal_vsntprintf _vsntprintf + #define Tier0Internal_vsnprintf _vsnprintf +#endif + +//----------------------------------------------------------------------------- +// +// Purpose: Inline string formatter +// + +#include "tier0/valve_off.h" +class CDbgFmtMsg +{ +public: + CDbgFmtMsg(const tchar *pszFormat, ...) + { + va_list arg_ptr; + + va_start(arg_ptr, pszFormat); + Tier0Internal_vsntprintf(m_szBuf, sizeof(m_szBuf)-1, pszFormat, arg_ptr); + va_end(arg_ptr); + + m_szBuf[sizeof(m_szBuf)-1] = 0; + } + + operator const tchar *() const + { + return m_szBuf; + } + +private: + tchar m_szBuf[256]; +}; +#include "tier0/valve_on.h" + +//----------------------------------------------------------------------------- +// +// Purpose: Embed debug info in each file. +// +#if defined( _WIN32 ) && !defined( _X360 ) + + #ifdef _DEBUG + #pragma comment(compiler) + #endif + +#endif + +//----------------------------------------------------------------------------- +// +// Purpose: Wrap around a variable to create a simple place to put a breakpoint +// + +#ifdef _DEBUG + +template< class Type > +class CDataWatcher +{ +public: + const Type& operator=( const Type &val ) + { + return Set( val ); + } + + const Type& operator=( const CDataWatcher &val ) + { + return Set( val.m_Value ); + } + + const Type& Set( const Type &val ) + { + // Put your breakpoint here + m_Value = val; + return m_Value; + } + + Type& GetForModify() + { + return m_Value; + } + + const Type& operator+=( const Type &val ) + { + return Set( m_Value + val ); + } + + const Type& operator-=( const Type &val ) + { + return Set( m_Value - val ); + } + + const Type& operator/=( const Type &val ) + { + return Set( m_Value / val ); + } + + const Type& operator*=( const Type &val ) + { + return Set( m_Value * val ); + } + + const Type& operator^=( const Type &val ) + { + return Set( m_Value ^ val ); + } + + const Type& operator|=( const Type &val ) + { + return Set( m_Value | val ); + } + + const Type& operator++() + { + return (*this += 1); + } + + Type operator--() + { + return (*this -= 1); + } + + Type operator++( int ) // postfix version.. + { + Type val = m_Value; + (*this += 1); + return val; + } + + Type operator--( int ) // postfix version.. + { + Type val = m_Value; + (*this -= 1); + return val; + } + + // For some reason the compiler only generates type conversion warnings for this operator when used like + // CNetworkVarBase = 0x1 + // (it warns about converting from an int to an unsigned char). + template< class C > + const Type& operator&=( C val ) + { + return Set( m_Value & val ); + } + + operator const Type&() const + { + return m_Value; + } + + const Type& Get() const + { + return m_Value; + } + + const Type* operator->() const + { + return &m_Value; + } + + Type m_Value; + +}; + +#else + +template< class Type > +class CDataWatcher +{ +private: + CDataWatcher(); // refuse to compile in non-debug builds +}; + +#endif + +// Code for programmatically setting/unsetting hardware breakpoints (there's probably a 360 and +#ifdef IS_WINDOWS_PC + +typedef void * HardwareBreakpointHandle_t; + +enum EHardwareBreakpointType +{ + BREAKPOINT_EXECUTE = 0, + BREAKPOINT_WRITE, + BREAKPOINT_READWRITE, +}; + +enum EHardwareBreakpointSize +{ + BREAKPOINT_SIZE_1 = 1, + BREAKPOINT_SIZE_2 = 2, + BREAKPOINT_SIZE_4 = 4, + BREAKPOINT_SIZE_8 = 8, +}; + +PLATFORM_INTERFACE HardwareBreakpointHandle_t SetHardwareBreakpoint( EHardwareBreakpointType eType, EHardwareBreakpointSize eSize, const void *pvLocation ); +PLATFORM_INTERFACE bool ClearHardwareBreakpoint( HardwareBreakpointHandle_t handle ); + +class CHardwareBreakPointScopeGuard +{ +public: + CHardwareBreakPointScopeGuard( const void *pvLocation, size_t nLocationSize, EHardwareBreakpointType eType = BREAKPOINT_WRITE ) + { + EHardwareBreakpointSize eSize = BREAKPOINT_SIZE_4; + switch ( nLocationSize ) + { + case 1: + eSize = BREAKPOINT_SIZE_1; + break; + case 2: + eSize = BREAKPOINT_SIZE_2; + break; + case 4: + eSize = BREAKPOINT_SIZE_4; + break; + case 8: + eSize = BREAKPOINT_SIZE_8; + break; + default: + Warning( _T( "SetHardwareBreakpoint can only work with 1, 2, 4 or 8 byte data fields." ) ); + break; + } + + m_hBreakPoint = SetHardwareBreakpoint( eType, eSize, pvLocation ); + m_bActive = m_hBreakPoint != (HardwareBreakpointHandle_t)0; + } + + ~CHardwareBreakPointScopeGuard() + { + Release(); + } + + void Release() + { + if ( !m_bActive ) + return; + ClearHardwareBreakpoint( m_hBreakPoint ); + } + +private: + bool m_bActive; + HardwareBreakpointHandle_t m_hBreakPoint; +}; + +#endif // IS_WINDOWS_PC +//----------------------------------------------------------------------------- + +#endif /* DBG_H */ diff --git a/public/tier0/dbgflag.h b/public/tier0/dbgflag.h new file mode 100644 index 0000000..d57c195 --- /dev/null +++ b/public/tier0/dbgflag.h @@ -0,0 +1,64 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This file sets all of our debugging flags. It should be +// called before all other header files. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DBGFLAG_H +#define DBGFLAG_H +#ifdef _WIN32 +#pragma once +#endif + + +// Here are all the flags we support: +// DBGFLAG_MEMORY: Enables our memory debugging system, which overrides malloc & free +// DBGFLAG_MEMORY_NEWDEL: Enables new / delete tracking for memory debug system. Requires DBGFLAG_MEMORY to be enabled. +// DBGFLAG_VALIDATE: Enables our recursive validation system for checking integrity and memory leaks +// DBGFLAG_ASSERT: Turns Assert on or off (when off, it isn't compiled at all) +// DBGFLAG_ASSERTFATAL: Turns AssertFatal on or off (when off, it isn't compiled at all) +// DBGFLAG_ASSERTDLG: Turns assert dialogs on or off and debug breaks on or off when not under the debugger. +// (Dialogs will always be on when process is being debugged.) +// DBGFLAG_STRINGS: Turns on hardcore string validation (slow but safe) + +#undef DBGFLAG_MEMORY +#undef DBGFLAG_MEMORY_NEWDEL +#undef DBGFLAG_VALIDATE +#undef DBGFLAG_ASSERT +#undef DBGFLAG_ASSERTFATAL +#undef DBGFLAG_ASSERTDLG +#undef DBGFLAG_STRINGS + +//----------------------------------------------------------------------------- +// Default flags for debug builds +//----------------------------------------------------------------------------- +#ifdef _DEBUG + +#define DBGFLAG_MEMORY +#ifdef _SERVER // only enable new & delete tracking for server; on client it conflicts with CRT mem leak tracking +#define DBGFLAG_MEMORY_NEWDEL +#endif +#ifdef STEAM +#define DBGFLAG_VALIDATE +#endif +#define DBGFLAG_ASSERT +#define DBGFLAG_ASSERTFATAL +#define DBGFLAG_ASSERTDLG +#define DBGFLAG_STRINGS + + +//----------------------------------------------------------------------------- +// Default flags for release builds +//----------------------------------------------------------------------------- +#else // _DEBUG +#ifdef STEAM +#define DBGFLAG_ASSERT +#endif +#define DBGFLAG_ASSERTFATAL // note: fatal asserts are enabled in release builds +#define DBGFLAG_ASSERTDLG + +#endif // _DEBUG + +#endif // DBGFLAG_H diff --git a/public/tier0/fasttimer.h b/public/tier0/fasttimer.h new file mode 100644 index 0000000..0f1eac6 --- /dev/null +++ b/public/tier0/fasttimer.h @@ -0,0 +1,600 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef FASTTIMER_H +#define FASTTIMER_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier0/platform.h" + +PLATFORM_INTERFACE uint64 g_ClockSpeed; +PLATFORM_INTERFACE unsigned long g_dwClockSpeed; + +PLATFORM_INTERFACE double g_ClockSpeedMicrosecondsMultiplier; +PLATFORM_INTERFACE double g_ClockSpeedMillisecondsMultiplier; +PLATFORM_INTERFACE double g_ClockSpeedSecondsMultiplier; + +#ifdef COMPILER_MSVC64 +extern "C" +{ + unsigned __int64 __rdtsc(); +} + +#pragma intrinsic(__rdtsc) +#endif + +class CCycleCount +{ + friend class CFastTimer; + +public: + CCycleCount(); + CCycleCount( uint64 cycles ); + + void Sample(); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900). + + void Init(); // Set to zero. + void Init( float initTimeMsec ); + void Init( double initTimeMsec ) { Init( (float)initTimeMsec ); } + void Init( uint64 cycles ); + bool IsLessThan( CCycleCount const &other ) const; // Compare two counts. + + // Convert to other time representations. These functions are slow, so it's preferable to call them + // during display rather than inside a timing block. + unsigned long GetCycles() const; + uint64 GetLongCycles() const; + + unsigned long GetMicroseconds() const; + uint64 GetUlMicroseconds() const; + double GetMicrosecondsF() const; + void SetMicroseconds( unsigned long nMicroseconds ); + + unsigned long GetMilliseconds() const; + double GetMillisecondsF() const; + + double GetSeconds() const; + + CCycleCount& operator+=( CCycleCount const &other ); + + // dest = rSrc1 + rSrc2 + static void Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together. + + // dest = rSrc1 - rSrc2 + static void Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together. + + static uint64 GetTimestamp(); + + uint64 m_Int64; +}; + +class CClockSpeedInit +{ +public: + CClockSpeedInit() + { + Init(); + } + + static void Init() + { + const CPUInformation& pi = GetCPUInformation(); + + if ( !IsX360() ) + { + g_ClockSpeed = pi.m_Speed; + } + else + { + // cycle counter runs as doc'd at 1/64 Xbox 3.2GHz clock speed, thus 50 Mhz + g_ClockSpeed = pi.m_Speed / 64L; + } + g_dwClockSpeed = (unsigned long)g_ClockSpeed; + + g_ClockSpeedMicrosecondsMultiplier = 1000000.0 / (double)g_ClockSpeed; + g_ClockSpeedMillisecondsMultiplier = 1000.0 / (double)g_ClockSpeed; + g_ClockSpeedSecondsMultiplier = 1.0f / (double)g_ClockSpeed; + } +}; + +class CFastTimer +{ +public: + // These functions are fast to call and should be called from your sampling code. + void Start(); + void End(); + + const CCycleCount & GetDuration() const; // Get the elapsed time between Start and End calls. + CCycleCount GetDurationInProgress() const; // Call without ending. Not that cheap. + + // Return number of cycles per second on this processor. + static inline unsigned long GetClockSpeed(); + +private: + CCycleCount m_Duration; +#ifdef DEBUG_FASTTIMER + bool m_bRunning; // Are we currently running? +#endif +}; + + +// This is a helper class that times whatever block of code it's in +class CTimeScope +{ +public: + CTimeScope( CFastTimer *pTimer ); + ~CTimeScope(); + +private: + CFastTimer *m_pTimer; +}; + +inline CTimeScope::CTimeScope( CFastTimer *pTotal ) +{ + m_pTimer = pTotal; + m_pTimer->Start(); +} + +inline CTimeScope::~CTimeScope() +{ + m_pTimer->End(); +} + +// This is a helper class that times whatever block of code it's in and +// adds the total (int microseconds) to a global counter. +class CTimeAdder +{ +public: + CTimeAdder( CCycleCount *pTotal ); + ~CTimeAdder(); + + void End(); + +private: + CCycleCount *m_pTotal; + CFastTimer m_Timer; +}; + +inline CTimeAdder::CTimeAdder( CCycleCount *pTotal ) +{ + m_pTotal = pTotal; + m_Timer.Start(); +} + +inline CTimeAdder::~CTimeAdder() +{ + End(); +} + +inline void CTimeAdder::End() +{ + if( m_pTotal ) + { + m_Timer.End(); + *m_pTotal += m_Timer.GetDuration(); + m_pTotal = 0; + } +} + + + +// -------------------------------------------------------------------------- // +// Simple tool to support timing a block of code, and reporting the results on +// program exit or at each iteration +// +// Macros used because dbg.h uses this header, thus Msg() is unavailable +// -------------------------------------------------------------------------- // + +#define PROFILE_SCOPE(name) \ + class C##name##ACC : public CAverageCycleCounter \ + { \ + public: \ + ~C##name##ACC() \ + { \ + Msg("%-48s: %6.3f avg (%8.1f total, %7.3f peak, %5d iters)\n", \ + #name, \ + GetAverageMilliseconds(), \ + GetTotalMilliseconds(), \ + GetPeakMilliseconds(), \ + GetIters() ); \ + } \ + }; \ + static C##name##ACC name##_ACC; \ + CAverageTimeMarker name##_ATM( &name##_ACC ) + +#define TIME_SCOPE(name) \ + class CTimeScopeMsg_##name \ + { \ + public: \ + CTimeScopeMsg_##name() { m_Timer.Start(); } \ + ~CTimeScopeMsg_##name() \ + { \ + m_Timer.End(); \ + Msg( #name "time: %.4fms\n", m_Timer.GetDuration().GetMillisecondsF() ); \ + } \ + private: \ + CFastTimer m_Timer; \ + } name##_TSM; + + +// -------------------------------------------------------------------------- // + +class CAverageCycleCounter +{ +public: + CAverageCycleCounter(); + + void Init(); + void MarkIter( const CCycleCount &duration ); + + unsigned GetIters() const; + + double GetAverageMilliseconds() const; + double GetTotalMilliseconds() const; + double GetPeakMilliseconds() const; + +private: + unsigned m_nIters; + CCycleCount m_Total; + CCycleCount m_Peak; + bool m_fReport; + const tchar *m_pszName; +}; + +// -------------------------------------------------------------------------- // + +class CAverageTimeMarker +{ +public: + CAverageTimeMarker( CAverageCycleCounter *pCounter ); + ~CAverageTimeMarker(); + +private: + CAverageCycleCounter *m_pCounter; + CFastTimer m_Timer; +}; + + +// -------------------------------------------------------------------------- // +// CCycleCount inlines. +// -------------------------------------------------------------------------- // + +inline CCycleCount::CCycleCount() +{ + Init( (uint64)0 ); +} + +inline CCycleCount::CCycleCount( uint64 cycles ) +{ + Init( cycles ); +} + +inline void CCycleCount::Init() +{ + Init( (uint64)0 ); +} + +inline void CCycleCount::Init( float initTimeMsec ) +{ + if ( g_ClockSpeedMillisecondsMultiplier > 0 ) + Init( (uint64)(initTimeMsec / g_ClockSpeedMillisecondsMultiplier) ); + else + Init( (uint64)0 ); +} + +inline void CCycleCount::Init( uint64 cycles ) +{ + m_Int64 = cycles; +} + +#pragma warning(push) +#pragma warning(disable : 4189) // warning C4189: local variable is initialized but not referenced + +inline void CCycleCount::Sample() +{ +#ifdef COMPILER_MSVC64 + unsigned __int64* pSample = (unsigned __int64*)&m_Int64; + *pSample = __rdtsc(); + // Msg( "Sample = %I64x", pSample ); +#elif defined( _X360 ) + // only need lower 32 bits, avoids doc'd read bug and 32 bit rollover is in 85 seconds + m_Int64 = (uint64)__mftb32(); + // scale back up, needs to be viewed as 1 cycle/clock +#elif defined( __GNUC__ ) + unsigned long* pSample = (unsigned long *)&m_Int64; + __asm__ __volatile__ ( + "rdtsc\n\t" + "movl %%eax, (%0)\n\t" + "movl %%edx, 4(%0)\n\t" + : /* no output regs */ + : "D" (pSample) + : "%eax", "%edx" ); +#elif defined( _WIN32 ) + unsigned long* pSample = (unsigned long *)&m_Int64; + __asm + { + // force the cpu to synchronize the instruction queue + // NJS: CPUID can really impact performance in tight loops. + //cpuid + //cpuid + //cpuid + mov ecx, pSample + rdtsc + mov [ecx], eax + mov [ecx+4], edx + } +#elif defined( POSIX ) + unsigned long* pSample = (unsigned long *)&m_Int64; + __asm__ __volatile__ ( + "rdtsc\n\t" + "movl %%eax, (%0)\n\t" + "movl %%edx, 4(%0)\n\t" + : /* no output regs */ + : "D" (pSample) + : "%eax", "%edx" ); +#endif +} + +#pragma warning(pop) + + +inline CCycleCount& CCycleCount::operator+=( CCycleCount const &other ) +{ + m_Int64 += other.m_Int64; + return *this; +} + + +inline void CCycleCount::Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ) +{ + dest.m_Int64 = rSrc1.m_Int64 + rSrc2.m_Int64; +} + +inline void CCycleCount::Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ) +{ + dest.m_Int64 = rSrc1.m_Int64 - rSrc2.m_Int64; +} + +inline uint64 CCycleCount::GetTimestamp() +{ + CCycleCount c; + c.Sample(); + return c.GetLongCycles(); +} + +inline bool CCycleCount::IsLessThan(CCycleCount const &other) const +{ + return m_Int64 < other.m_Int64; +} + + +inline unsigned long CCycleCount::GetCycles() const +{ + return (unsigned long)m_Int64; +} + +inline uint64 CCycleCount::GetLongCycles() const +{ + return m_Int64; +} + +inline unsigned long CCycleCount::GetMicroseconds() const +{ + return (unsigned long)((m_Int64 * 1000000) / g_ClockSpeed); +} + +inline uint64 CCycleCount::GetUlMicroseconds() const +{ + return ((m_Int64 * 1000000) / g_ClockSpeed); +} + + +inline double CCycleCount::GetMicrosecondsF() const +{ + return (double)( m_Int64 * g_ClockSpeedMicrosecondsMultiplier ); +} + + +inline void CCycleCount::SetMicroseconds( unsigned long nMicroseconds ) +{ + m_Int64 = ((uint64)nMicroseconds * g_ClockSpeed) / 1000000; +} + + +inline unsigned long CCycleCount::GetMilliseconds() const +{ + return (unsigned long)((m_Int64 * 1000) / g_ClockSpeed); +} + + +inline double CCycleCount::GetMillisecondsF() const +{ + return (double)( m_Int64 * g_ClockSpeedMillisecondsMultiplier ); +} + + +inline double CCycleCount::GetSeconds() const +{ + return (double)( m_Int64 * g_ClockSpeedSecondsMultiplier ); +} + + +// -------------------------------------------------------------------------- // +// CFastTimer inlines. +// -------------------------------------------------------------------------- // +inline void CFastTimer::Start() +{ + m_Duration.Sample(); +#ifdef DEBUG_FASTTIMER + m_bRunning = true; +#endif +} + + +inline void CFastTimer::End() +{ + CCycleCount cnt; + cnt.Sample(); + if ( IsX360() ) + { + // have to handle rollover, hires timer is only accurate to 32 bits + // more than one overflow should not have occurred, otherwise caller should use a slower timer + if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 ) + { + // rollover occurred + cnt.m_Int64 += 0x100000000LL; + } + } + + m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64; + +#ifdef DEBUG_FASTTIMER + m_bRunning = false; +#endif +} + +inline CCycleCount CFastTimer::GetDurationInProgress() const +{ + CCycleCount cnt; + cnt.Sample(); + if ( IsX360() ) + { + // have to handle rollover, hires timer is only accurate to 32 bits + // more than one overflow should not have occurred, otherwise caller should use a slower timer + if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 ) + { + // rollover occurred + cnt.m_Int64 += 0x100000000LL; + } + } + + CCycleCount result; + result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64; + + return result; +} + + +inline unsigned long CFastTimer::GetClockSpeed() +{ + return g_dwClockSpeed; +} + + +inline CCycleCount const& CFastTimer::GetDuration() const +{ +#ifdef DEBUG_FASTTIMER + assert( !m_bRunning ); +#endif + return m_Duration; +} + + +// -------------------------------------------------------------------------- // +// CAverageCycleCounter inlines + +inline CAverageCycleCounter::CAverageCycleCounter() + : m_nIters( 0 ) +{ +} + +inline void CAverageCycleCounter::Init() +{ + m_Total.Init(); + m_Peak.Init(); + m_nIters = 0; +} + +inline void CAverageCycleCounter::MarkIter( const CCycleCount &duration ) +{ + ++m_nIters; + m_Total += duration; + if ( m_Peak.IsLessThan( duration ) ) + m_Peak = duration; +} + +inline unsigned CAverageCycleCounter::GetIters() const +{ + return m_nIters; +} + +inline double CAverageCycleCounter::GetAverageMilliseconds() const +{ + if ( m_nIters ) + return (m_Total.GetMillisecondsF() / (double)m_nIters); + else + return 0; +} + +inline double CAverageCycleCounter::GetTotalMilliseconds() const +{ + return m_Total.GetMillisecondsF(); +} + +inline double CAverageCycleCounter::GetPeakMilliseconds() const +{ + return m_Peak.GetMillisecondsF(); +} + +// -------------------------------------------------------------------------- // + +inline CAverageTimeMarker::CAverageTimeMarker( CAverageCycleCounter *pCounter ) +{ + m_pCounter = pCounter; + m_Timer.Start(); +} + +inline CAverageTimeMarker::~CAverageTimeMarker() +{ + m_Timer.End(); + m_pCounter->MarkIter( m_Timer.GetDuration() ); +} + + +// CLimitTimer +// Use this to time whether a desired interval of time has passed. It's extremely fast +// to check while running. +class CLimitTimer +{ +public: + void SetLimit( uint64 m_cMicroSecDuration ); + bool BLimitReached( void ); + +private: + uint64 m_lCycleLimit; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Initializes the limit timer with a period of time to measure. +// Input : cMicroSecDuration - How long a time period to measure +//----------------------------------------------------------------------------- +inline void CLimitTimer::SetLimit( uint64 m_cMicroSecDuration ) +{ + uint64 dlCycles = ( ( uint64 ) m_cMicroSecDuration * ( uint64 ) g_dwClockSpeed ) / ( uint64 ) 1000000L; + CCycleCount cycleCount; + cycleCount.Sample( ); + m_lCycleLimit = cycleCount.GetLongCycles( ) + dlCycles; +} + + +//----------------------------------------------------------------------------- +// Purpose: Determines whether our specified time period has passed +// Output: true if at least the specified time period has passed +//----------------------------------------------------------------------------- +inline bool CLimitTimer::BLimitReached( ) +{ + CCycleCount cycleCount; + cycleCount.Sample( ); + return ( cycleCount.GetLongCycles( ) >= m_lCycleLimit ); +} + + + +#endif // FASTTIMER_H diff --git a/public/tier0/ia32detect.h b/public/tier0/ia32detect.h new file mode 100644 index 0000000..e4a63a7 --- /dev/null +++ b/public/tier0/ia32detect.h @@ -0,0 +1,383 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#ifndef IA32DETECT_H +#define IA32DETECT_H + +#ifdef COMPILER_MSVC64 +extern "C" void __cpuid(int* CPUInfo, int InfoType); +#pragma intrinsic (__cpuid) +#endif +/* + This section from http://iss.cs.cornell.edu/ia32.htm + + + */ +typedef unsigned bit; + +enum CPUVendor +{ + INTEL, + AMD, + UNKNOWN_VENDOR +}; +class ia32detect +{ +public: + + enum type_t + { + type_OEM, + type_OverDrive, + type_Dual, + type_reserved + }; + + enum brand_t + { + brand_na, + brand_Celeron, + brand_PentiumIII, + brand_PentiumIIIXeon, + brand_reserved1, + brand_reserved2, + brand_PentiumIIIMobile, + brand_reserved3, + brand_Pentium4, + brand_invalid + }; + +# pragma pack(push, 1) + + struct version_t + { + bit Stepping : 4; + bit Model : 4; + bit Family : 4; + bit Type : 2; + bit Reserved1 : 2; + bit XModel : 4; + bit XFamily : 8; + bit Reserved2 : 4; + }; + + struct misc_t + { + byte Brand; + byte CLFLUSH; + byte Reserved; + byte APICId; + }; + + struct feature_t + { + bit FPU : 1; // Floating Point Unit On-Chip + bit VME : 1; // Virtual 8086 Mode Enhancements + bit DE : 1; // Debugging Extensions + bit PSE : 1; // Page Size Extensions + bit TSC : 1; // Time Stamp Counter + bit MSR : 1; // Model Specific Registers + bit PAE : 1; // Physical Address Extension + bit MCE : 1; // Machine Check Exception + bit CX8 : 1; // CMPXCHG8 Instruction + bit APIC : 1; // APIC On-Chip + bit Reserved1 : 1; + bit SEP : 1; // SYSENTER and SYSEXIT instructions + bit MTRR : 1; // Memory Type Range Registers + bit PGE : 1; // PTE Global Bit + bit MCA : 1; // Machine Check Architecture + bit CMOV : 1; // Conditional Move Instructions + bit PAT : 1; // Page Attribute Table + bit PSE36 : 1; // 32-bit Page Size Extension + bit PSN : 1; // Processor Serial Number + bit CLFSH : 1; // CLFLUSH Instruction + bit Reserved2 : 1; + bit DS : 1; // Debug Store + bit ACPI : 1; // Thermal Monitor and Software Controlled Clock Facilities + bit MMX : 1; // Intel MMX Technology + bit FXSR : 1; // FXSAVE and FXRSTOR Instructions + bit SSE : 1; // Intel SSE Technology + bit SSE2 : 1; // Intel SSE2 Technology + bit SS : 1; // Self Snoop + bit HTT : 1; // Hyper Threading + bit TM : 1; // Thermal Monitor + bit Reserved3 : 1; + bit PBE : 1; // Pending Brk. EN. + }; + +# pragma pack(pop) + + tstring vendor_name; + CPUVendor vendor; + tstring brand; + version_t version; + misc_t misc; + feature_t feature; + byte *cache; + + ia32detect () + { + + cache = 0; + uint32 m = init0(); + + uint32 *d = new uint32[m * 4]; + + for (uint32 i = 1; i <= m; i++) + { + +#ifdef COMPILER_MSVC64 + __cpuid((int *) (d + (i-1) * 4), i); + +#else + uint32 *t = d + (i - 1) * 4; + __asm + { + mov eax, i; + mov esi, t; + + cpuid; + + mov dword ptr [esi + 0x0], eax; + mov dword ptr [esi + 0x4], ebx; + mov dword ptr [esi + 0x8], ecx; + mov dword ptr [esi + 0xC], edx; + } +#endif + } + + if (m >= 1) + init1(d); + + if (m >= 2) + init2(d[4] & 0xFF); + + delete [] d; + + init0x80000000(); + + + //----------------------------------------------------------------------- + // Get the vendor of the processor + //----------------------------------------------------------------------- + if (_tcscmp(vendor_name.c_str(), _T("GenuineIntel")) == 0) + { + vendor = INTEL; + + } + else if (_tcscmp(vendor_name.c_str(), _T("AuthenticAMD")) == 0) + { + vendor = AMD; + + } + else + { + vendor = UNKNOWN_VENDOR; + } + } + + const tstring version_text () const + { + tchar b[128]; + + _stprintf(b, _T("%d.%d.%d %s XVersion(%d.%d)"), + version.Family, version.Model, version.Stepping, type_text(), version.XFamily, version.XModel); + + return tstring(b); + } + +protected: + + const tchar * type_text () const + { + static const tchar *text[] = + { + _T("Intel OEM Processor"), + _T("Intel OverDrive(R) Processor"), + _T("Intel Dual Processor"), + _T("reserved") + }; + + return text[version.Type]; + } + + const tstring brand_text () const + { + static const tchar *text[] = + { + _T("n/a"), + _T("Celeron"), + _T("Pentium III"), + _T("Pentium III Xeon"), + _T("reserved (4)"), + _T("reserved (5)"), + _T("Pentium III Mobile"), + _T("reserved (7)"), + _T("Pentium 4") + }; + + if (misc.Brand < brand_invalid) + return tstring(text[misc.Brand]); + else + { + tchar b[32]; + + _stprintf(b, _T("Brand %d (Update)"), misc.Brand); + + return tstring(b); + } + } + +private: + + uint32 init0 () + { + uint32 m; + +#ifdef COMPILER_MSVC64 + int data[4]; + tchar * s1; + + s1 = (tchar *) &data[1]; + s1[12] = '\0'; + __cpuid(data, 0); + m = data[0]; + vendor_name = s1; +#else + tchar s1[13]; + + s1[12] = '\0'; + __asm + { + xor eax, eax; + cpuid; + mov m, eax; + mov dword ptr s1 + 0, ebx; + mov dword ptr s1 + 4, edx; + mov dword ptr s1 + 8, ecx; + } + vendor_name = s1; +#endif + return m; + } + + void init1 (uint32 *d) + { + version = *(version_t *)&d[0]; + misc = *(misc_t *)&d[1]; + feature = *(feature_t *)&d[3]; + } + + void process2 (uint32 d, bool c[]) + { + if ((d & 0x80000000) == 0) + for (int i = 0; i < 32; i += 8) + c[(d >> i) & 0xFF] = true; + } + + void init2 (byte count) + { + uint32 d[4]; + bool c[256]; + + for (int ci1 = 0; ci1 < 256; ci1++) + c[ci1] = false; + + for (int i = 0; i < count; i++) + { +#ifdef COMPILER_MSVC64 + __cpuid((int *) d, 2); +#else + __asm + { + mov eax, 2; + lea esi, d; + cpuid; + mov [esi + 0x0], eax; + mov [esi + 0x4], ebx; + mov [esi + 0x8], ecx; + mov [esi + 0xC], edx; + } +#endif + + if (i == 0) + d[0] &= 0xFFFFFF00; + + process2(d[0], c); + process2(d[1], c); + process2(d[2], c); + process2(d[3], c); + } + + int m = 0; + + for (int ci2 = 0; ci2 < 256; ci2++) + if (c[ci2]) + m++; + + cache = new byte[m]; + + m = 0; + + for (int ci3 = 1; ci3 < 256; ci3++) + if (c[ci3]) + cache[m++] = ci3; + + cache[m] = 0; + } + + void init0x80000000 () + { + uint32 m; + +#ifdef COMPILER_MSVC64 + int data[4]; + __cpuid(data, 0x80000000); + m = data[0]; +#else + __asm + { + mov eax, 0x80000000; + cpuid; + mov m, eax + } +#endif + + if ((m & 0x80000000) != 0) + { + uint32 *d = new uint32[(m - 0x80000000) * 4]; + + for (uint32 i = 0x80000001; i <= m; i++) + { + uint32 *t = d + (i - 0x80000001) * 4; + +#ifdef COMPILER_MSVC64 + __cpuid((int *) (d + (i - 0x80000001) * 4), i); +#else + __asm + { + mov eax, i; + mov esi, t; + cpuid; + mov dword ptr [esi + 0x0], eax; + mov dword ptr [esi + 0x4], ebx; + mov dword ptr [esi + 0x8], ecx; + mov dword ptr [esi + 0xC], edx; + } +#endif + } + + if (m >= 0x80000002) + brand = (tchar *)(d + 4); + + // note the assignment to brand above does a copy, we need to delete + delete[] d; + } + } +}; + +#endif // IA32DETECT_H diff --git a/public/tier0/icommandline.h b/public/tier0/icommandline.h new file mode 100644 index 0000000..d56d5a3 --- /dev/null +++ b/public/tier0/icommandline.h @@ -0,0 +1,65 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef TIER0_ICOMMANDLINE_H +#define TIER0_ICOMMANDLINE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + + +//----------------------------------------------------------------------------- +// Purpose: Interface to engine command line +//----------------------------------------------------------------------------- +abstract_class ICommandLine +{ +public: + virtual void CreateCmdLine( const char *commandline ) = 0; + virtual void CreateCmdLine( int argc, char **argv ) = 0; + virtual const char *GetCmdLine( void ) const = 0; + + // Check whether a particular parameter exists + virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const = 0; + virtual void RemoveParm( const char *parm ) = 0; + virtual void AppendParm( const char *pszParm, const char *pszValues ) = 0; + + // Returns the argument after the one specified, or the default if not found + virtual const char *ParmValue( const char *psz, const char *pDefaultVal = 0 ) const = 0; + virtual int ParmValue( const char *psz, int nDefaultVal ) const = 0; + virtual float ParmValue( const char *psz, float flDefaultVal ) const = 0; + + // Gets at particular parameters + virtual int ParmCount() const = 0; + virtual int FindParm( const char *psz ) const = 0; // Returns 0 if not found. + virtual const char* GetParm( int nIndex ) const = 0; + + // copies the string passwed + virtual void SetParm( int nIndex, char const *pNewParm ) =0; +}; + +//----------------------------------------------------------------------------- +// Gets a singleton to the commandline interface +// NOTE: The #define trickery here is necessary for backwards compat: +// this interface used to lie in the vstdlib library. +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE ICommandLine *CommandLine(); + + +//----------------------------------------------------------------------------- +// Process related functions +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE const tchar *Plat_GetCommandLine(); +#ifndef _WIN32 +// helper function for OS's that don't have a ::GetCommandLine() call +PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine ); +#endif +PLATFORM_INTERFACE const char *Plat_GetCommandLineA(); + + +#endif // TIER0_ICOMMANDLINE_H + diff --git a/public/tier0/l2cache.h b/public/tier0/l2cache.h new file mode 100644 index 0000000..db54695 --- /dev/null +++ b/public/tier0/l2cache.h @@ -0,0 +1,47 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#ifndef CL2CACHE_H +#define CL2CACHE_H +#ifdef _WIN32 +#pragma once +#endif + +class P4Event_BSQ_cache_reference; + +class CL2Cache +{ +public: + + CL2Cache(); + ~CL2Cache(); + + void Start( void ); + void End( void ); + + //------------------------------------------------------------------------- + // GetL2CacheMisses + //------------------------------------------------------------------------- + int GetL2CacheMisses( void ) + { + return m_iL2CacheMissCount; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +private: + + int m_nID; + + P4Event_BSQ_cache_reference *m_pL2CacheEvent; + int64 m_i64Start; + int64 m_i64End; + int m_iL2CacheMissCount; +}; + +#endif // CL2CACHE_H + diff --git a/public/tier0/logging.h b/public/tier0/logging.h new file mode 100644 index 0000000..576a80a --- /dev/null +++ b/public/tier0/logging.h @@ -0,0 +1,746 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +// +// Logging system declarations. +// +// The logging system is a channel-based output mechanism which allows +// subsystems to route their text/diagnostic output to various listeners +// +//=============================================================================== + +#ifndef LOGGING_H +#define LOGGING_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +#include "color.h" +#include "icommandline.h" +#include + +// For XBX_** functions +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#endif + +// Used by CColorizedLoggingListener +#if defined( _WIN32 ) +#include "tier0/win32consoleio.h" +#endif + +/* + ---- Logging System ---- + + The logging system is a channel-based mechanism for all code (engine, + mod, tool) across all platforms to output information, warnings, + errors, etc. + + This system supersedes the existing Msg(), Warning(), Error(), DevMsg(), ConMsg() etc. functions. + There are channels defined in the new system through which all old messages are routed; + see LOG_GENERAL, LOG_CONSOLE, LOG_DEVELOPER, etc. + + To use the system, simply call one of the predefined macros: + + Log_Msg( ChannelID, [Color], Message, ... ) + Log_Warning( ChannelID, [Color], Message, ... ) + Log_Error( ChannelID, [Color], Message, ... ) + + A ChannelID is typically created by defining a logging channel with the + log channel macros: + + DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_ChannelName, "ChannelName", [Flags], [MinimumSeverity], [Color] ); + + or + + BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ChannelName, "ChannelName", [Flags], [MinimumSeverity], [Color] ); + ADD_LOGGING_CHANNEL_TAG( "Tag1" ); + ADD_LOGGING_CHANNEL_TAG( "Tag2" ); + END_DEFINE_LOGGING_CHANNEL(); + + These macros create a global channel ID variable with the name specified + by the first parameter (in this example, LOG_ChannelName). This channel ID + can be used by various LoggingSystem_** functions to manipulate the channel settings. + + The optional [Flags] parameter is an OR'd together set of LoggingChannelFlags_t + values (default: 0). + + The optional [MinimumSeverity] parameter is the lowest threshold + above which messages will be processed (inclusive). The default is LS_MESSAGE, + which results in all messages, warnings, and errors being logged. + Variadic parameters to the Log_** functions will be ignored if a channel + is not enabled for a given severity (for performance reasons). + + Logging channels can have their minimum severity modified by name, ID, or tag. + + Logging channels are not hierarchical since there are situations in which + a channel needs to belong to multiple hierarchies. Use tags to create + categories or shallow hierarchies. + + @TODO (Feature wishlist): + 1) Callstack logging support + 2) Registering dynamic channels and unregistering channels at runtime + 3) Sentient robot to clean up the thousands of places using the old/legacy logging system. +*/ + +////////////////////////////////////////////////////////////////////////// +// Constants, Types, Forward Declares +////////////////////////////////////////////////////////////////////////// + +class CLoggingSystem; +class CThreadFastMutex; + +//----------------------------------------------------------------------------- +// Maximum length of a sprintf'ed logging message. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_MESSAGE_LENGTH = 2048; + +//----------------------------------------------------------------------------- +// Maximum length of a channel or tag name. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_IDENTIFIER_LENGTH = 32; + +//----------------------------------------------------------------------------- +// Maximum number of logging channels. Increase if needed. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_CHANNEL_COUNT = 256; + +//----------------------------------------------------------------------------- +// Maximum number of logging tags across all channels. Increase if needed. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_TAG_COUNT = 1024; + +//----------------------------------------------------------------------------- +// Maximum number of characters across all logging tags. Increase if needed. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_TAG_CHARACTER_COUNT = 8192; + +//----------------------------------------------------------------------------- +// Maximum number of concurrent logging listeners in a given logging state. +//----------------------------------------------------------------------------- +const int MAX_LOGGING_LISTENER_COUNT = 16; + +//----------------------------------------------------------------------------- +// An invalid color set on a channel to imply that it should use +// a device-dependent default color where applicable. +//----------------------------------------------------------------------------- +const Color UNSPECIFIED_LOGGING_COLOR( 0, 0, 0, 0 ); + +//----------------------------------------------------------------------------- +// An ID returned by the logging system to refer to a logging channel. +//----------------------------------------------------------------------------- +typedef int LoggingChannelID_t; + +//----------------------------------------------------------------------------- +// A sentinel value indicating an invalid logging channel ID. +//----------------------------------------------------------------------------- +const LoggingChannelID_t INVALID_LOGGING_CHANNEL_ID = -1; + +//----------------------------------------------------------------------------- +// The severity of a logging operation. +//----------------------------------------------------------------------------- +enum LoggingSeverity_t +{ + //----------------------------------------------------------------------------- + // An informative logging message. + //----------------------------------------------------------------------------- + LS_MESSAGE = 0, + + //----------------------------------------------------------------------------- + // A warning, typically non-fatal + //----------------------------------------------------------------------------- + LS_WARNING = 1, + + //----------------------------------------------------------------------------- + // A message caused by an Assert**() operation. + //----------------------------------------------------------------------------- + LS_ASSERT = 2, + + //----------------------------------------------------------------------------- + // An error, typically fatal/unrecoverable. + //----------------------------------------------------------------------------- + LS_ERROR = 3, + + //----------------------------------------------------------------------------- + // A placeholder level, higher than any legal value. + // Not a real severity value! + //----------------------------------------------------------------------------- + LS_HIGHEST_SEVERITY = 4, +}; + +//----------------------------------------------------------------------------- +// Action which should be taken by logging system as a result of +// a given logged message. +// +// The logging system invokes ILoggingResponsePolicy::OnLog() on +// the specified policy object, which returns a LoggingResponse_t. +//----------------------------------------------------------------------------- +enum LoggingResponse_t +{ + LR_CONTINUE, + LR_DEBUGGER, + LR_ABORT, +}; + +//----------------------------------------------------------------------------- +// Logging channel behavior flags, set on channel creation. +//----------------------------------------------------------------------------- +enum LoggingChannelFlags_t +{ + //----------------------------------------------------------------------------- + // Indicates that the spew is only relevant to interactive consoles. + //----------------------------------------------------------------------------- + LCF_CONSOLE_ONLY = 0x00000001, + + //----------------------------------------------------------------------------- + // Indicates that spew should not be echoed to any output devices. + // A suitable logging listener must be registered which respects this flag + // (e.g. a file logger). + //----------------------------------------------------------------------------- + LCF_DO_NOT_ECHO = 0x00000002, +}; + +//----------------------------------------------------------------------------- +// A callback function used to register tags on a logging channel +// during initialization. +//----------------------------------------------------------------------------- +typedef void ( *RegisterTagsFunc )(); + +//----------------------------------------------------------------------------- +// A context structure passed to logging listeners and response policy classes. +//----------------------------------------------------------------------------- +struct LoggingContext_t +{ + // ID of the channel being logged to. + LoggingChannelID_t m_ChannelID; + // Flags associated with the channel. + LoggingChannelFlags_t m_Flags; + // Severity of the logging event. + LoggingSeverity_t m_Severity; + // Color of logging message if one was specified to Log_****() macro. + // If not specified, falls back to channel color. + // If channel color is not specified, this value is UNSPECIFIED_LOGGING_COLOR + // and indicates that a suitable default should be chosen. + Color m_Color; +}; + +//----------------------------------------------------------------------------- +// Interface for classes to handle logging output. +// +// The Log() function of this class is called synchronously and serially +// by the logging system on all registered instances of ILoggingListener +// in the current "logging state". +// +// Derived classes may do whatever they want with the message (write to disk, +// write to console, send over the network, drop on the floor, etc.). +// +// In general, derived classes should do one, simple thing with the output +// to allow callers to register multiple, orthogonal logging listener classes. +//----------------------------------------------------------------------------- +class ILoggingListener +{ +public: + virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage ) = 0; +}; + +//----------------------------------------------------------------------------- +// Interface for policy classes which determine how to behave when a +// message is logged. +// +// Can return: +// LR_CONTINUE (continue execution) +// LR_DEBUGGER (break into debugger if one is present, otherwise continue) +// LR_ABORT (terminate process immediately with a failure code of 1) +//----------------------------------------------------------------------------- +class ILoggingResponsePolicy +{ +public: + virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext ) = 0; +}; + +////////////////////////////////////////////////////////////////////////// +// Common Logging Listeners & Logging Response Policies +////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// A basic logging listener which prints to stdout and the debug channel. +//----------------------------------------------------------------------------- +class CSimpleLoggingListener : public ILoggingListener +{ +public: + CSimpleLoggingListener( bool bQuietPrintf = false, bool bQuietDebugger = false ) : + m_bQuietPrintf( bQuietPrintf ), + m_bQuietDebugger( bQuietDebugger ) + { + } + + virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage ) + { +#ifdef _X360 + if ( !m_bQuietDebugger && XBX_IsConsoleConnected() ) + { + // send to console + XBX_DebugString( XMAKECOLOR( 0,0,0 ), pMessage ); + } + else +#endif + { +#ifndef _CERT + if ( !m_bQuietPrintf ) + { + _tprintf( _T("%s"), pMessage ); + } +#endif + +#ifdef _WIN32 + if ( !m_bQuietDebugger && Plat_IsInDebugSession() ) + { + Plat_DebugString( pMessage ); + } +#endif + } + } + + // If set to true, does not print anything to stdout. + bool m_bQuietPrintf; + // If set to true, does not print anything to debugger. + bool m_bQuietDebugger; +}; + +//----------------------------------------------------------------------------- +// A basic logging listener for GUI applications +//----------------------------------------------------------------------------- +class CSimpleWindowsLoggingListener : public ILoggingListener +{ +public: + virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage ) + { + if ( Plat_IsInDebugSession() ) + { + Plat_DebugString( pMessage ); + } + if ( pContext->m_Severity == LS_ERROR ) + { + if ( Plat_IsInDebugSession() ) + DebuggerBreak(); + + Plat_MessageBox( "Error", pMessage ); + } + } +}; + + +//----------------------------------------------------------------------------- +// ** NOTE FOR INTEGRATION ** +// This was copied over from source 2 rather than integrated because +// source 2 has more significantly refactored tier0 logging. +// +// A logging listener with Win32 console API color support which which prints +// to stdout and the debug channel. +//----------------------------------------------------------------------------- +#ifndef _X360 +class CColorizedLoggingListener : public CSimpleLoggingListener +{ +public: + CColorizedLoggingListener( bool bQuietPrintf = false, bool bQuietDebugger = false ) : CSimpleLoggingListener( bQuietPrintf, bQuietDebugger ) + { + InitWin32ConsoleColorContext( &m_ColorContext ); + } + + virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage ) + { + if ( !m_bQuietPrintf ) + { + int nPrevColor = -1; + + if ( pContext->m_Color != UNSPECIFIED_LOGGING_COLOR ) + { + nPrevColor = SetWin32ConsoleColor( &m_ColorContext, + pContext->m_Color.r(), pContext->m_Color.g(), pContext->m_Color.b(), + MAX( MAX( pContext->m_Color.r(), pContext->m_Color.g() ), pContext->m_Color.b() ) > 128 ); + } + + _tprintf( _T("%s"), pMessage ); + + if ( nPrevColor >= 0 ) + { + RestoreWin32ConsoleColor( &m_ColorContext, nPrevColor ); + } + } + +#ifdef _WIN32 + if ( !m_bQuietDebugger && Plat_IsInDebugSession() ) + { + Plat_DebugString( pMessage ); + } +#endif + } + + Win32ConsoleColorContext_t m_ColorContext; +}; +#endif // !_X360 + + +//----------------------------------------------------------------------------- +// Default logging response policy used when one is not specified. +//----------------------------------------------------------------------------- +class CDefaultLoggingResponsePolicy : public ILoggingResponsePolicy +{ +public: + virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext ) + { + if ( pContext->m_Severity == LS_ASSERT && !CommandLine()->FindParm( "-noassert" ) ) + { + return LR_DEBUGGER; + } + else if ( pContext->m_Severity == LS_ERROR ) + { + return LR_ABORT; + } + else + { + return LR_CONTINUE; + } + } +}; + +//----------------------------------------------------------------------------- +// A logging response policy which never terminates the process, even on error. +//----------------------------------------------------------------------------- +class CNonFatalLoggingResponsePolicy : public ILoggingResponsePolicy +{ +public: + virtual LoggingResponse_t OnLog( const LoggingContext_t *pContext ) + { + if ( ( pContext->m_Severity == LS_ASSERT && !CommandLine()->FindParm( "-noassert" ) ) || pContext->m_Severity == LS_ERROR ) + { + return LR_DEBUGGER; + } + else + { + return LR_CONTINUE; + } + } +}; + +////////////////////////////////////////////////////////////////////////// +// Central Logging System +////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// The central logging system. +// +// Multiple instances can exist, though all exported tier0 functionality +// specifically works with a single global instance +// (via GetGlobalLoggingSystem()). +//----------------------------------------------------------------------------- +class CLoggingSystem +{ +public: + struct LoggingChannel_t; + + CLoggingSystem(); + ~CLoggingSystem(); + + //----------------------------------------------------------------------------- + // Register a logging channel with the logging system. + // The same channel can be registered multiple times, but the parameters + // in each call to RegisterLoggingChannel must either match across all calls + // or be set to defaults on any given call + // + // This function is not thread-safe and should generally only be called + // by a single thread. Using the logging channel definition macros ensures + // that this is called on the static initialization thread. + //----------------------------------------------------------------------------- + LoggingChannelID_t RegisterLoggingChannel( const char *pChannelName, RegisterTagsFunc registerTagsFunc, int flags = 0, LoggingSeverity_t minimumSeverity = LS_MESSAGE, Color spewColor = UNSPECIFIED_LOGGING_COLOR ); + + //----------------------------------------------------------------------------- + // Gets a channel ID from a string name. + // Performs a simple linear search; cache the value whenever possible + // or re-register the logging channel to get a global ID. + //----------------------------------------------------------------------------- + LoggingChannelID_t FindChannel( const char *pChannelName ) const; + + int GetChannelCount() const { return m_nChannelCount; } + + //----------------------------------------------------------------------------- + // Gets a pointer to the logging channel description. + //----------------------------------------------------------------------------- + LoggingChannel_t *GetChannel( LoggingChannelID_t channelID ); + const LoggingChannel_t *GetChannel( LoggingChannelID_t channelID ) const; + + //----------------------------------------------------------------------------- + // Returns true if the given channel has the specified tag. + //----------------------------------------------------------------------------- + bool HasTag( LoggingChannelID_t channelID, const char *pTag ) const { return GetChannel( channelID )->HasTag( pTag ); } + + //----------------------------------------------------------------------------- + // Returns true if the given channel will spew at the given severity level. + //----------------------------------------------------------------------------- + bool IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity ) const { return GetChannel( channelID )->IsEnabled( severity ); } + + //----------------------------------------------------------------------------- + // Functions to set the spew level of a channel either directly by ID or + // string name, or for all channels with a given tag. + // + // These functions are not technically thread-safe but calling them across + // multiple threads should cause no significant problems + // (the underlying data types being changed are 32-bit/atomic). + //----------------------------------------------------------------------------- + void SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity ); + void SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity ); + void SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity ); + + //----------------------------------------------------------------------------- + // Gets or sets the color of a logging channel. + // (The functions are not thread-safe, but the consequences are not + // significant.) + //----------------------------------------------------------------------------- + Color GetChannelColor( LoggingChannelID_t channelID ) const { return GetChannel( channelID )->m_SpewColor; } + void SetChannelColor( LoggingChannelID_t channelID, Color color ) { GetChannel( channelID )->m_SpewColor = color; } + + //----------------------------------------------------------------------------- + // Gets or sets the flags on a logging channel. + // (The functions are not thread-safe, but the consequences are not + // significant.) + //----------------------------------------------------------------------------- + LoggingChannelFlags_t GetChannelFlags( LoggingChannelID_t channelID ) const { return GetChannel( channelID )->m_Flags; } + void SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags ) { GetChannel( channelID )->m_Flags = flags; } + + //----------------------------------------------------------------------------- + // Adds a string tag to a channel. + // This is not thread-safe and should only be called by a RegisterTagsFunc + // callback passed in to RegisterLoggingChannel (via the + // channel definition macros). + //----------------------------------------------------------------------------- + void AddTagToCurrentChannel( const char *pTagName ); + + //----------------------------------------------------------------------------- + // Functions to save/restore the current logging state. + // Set bThreadLocal to true on a matching Push/Pop call if the intent + // is to override the logging listeners on the current thread only. + // + // Pushing the current logging state onto the state stack results + // in the current state being cleared by default (no listeners, default logging response policy). + // Set bClearState to false to copy the existing listener pointers to the new state. + // + // These functions which mutate logging state ARE thread-safe and are + // guarded by m_StateMutex. + //----------------------------------------------------------------------------- + void PushLoggingState( bool bThreadLocal = false, bool bClearState = true ); + void PopLoggingState( bool bThreadLocal = false ); + + //----------------------------------------------------------------------------- + // Registers a logging listener (a class which handles logged messages). + //----------------------------------------------------------------------------- + void RegisterLoggingListener( ILoggingListener *pListener ); + + //----------------------------------------------------------------------------- + // Returns whether the specified logging listener is registered. + //----------------------------------------------------------------------------- + bool IsListenerRegistered( ILoggingListener *pListener ) const; + + //----------------------------------------------------------------------------- + // Clears out all of the current logging state (removes all listeners, + // sets the response policy to the default). + //----------------------------------------------------------------------------- + void ResetCurrentLoggingState(); + + //----------------------------------------------------------------------------- + // Sets a policy class to decide what should happen when messages of a + // particular severity are logged + // (e.g. exit on error, break into debugger). + // If pLoggingResponse is NULL, uses the default response policy class. + //----------------------------------------------------------------------------- + void SetLoggingResponsePolicy( ILoggingResponsePolicy *pLoggingResponse ); + + //----------------------------------------------------------------------------- + // Logs a message to the specified channel using a given severity and + // spew color. Passing in UNSPECIFIED_LOGGING_COLOR for 'color' allows + // the logging listeners to provide a default. + //----------------------------------------------------------------------------- + LoggingResponse_t LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color color, const tchar *pMessage ); + + // Internal data to represent a logging tag + struct LoggingTag_t + { + const char *m_pTagName; + LoggingTag_t *m_pNextTag; + }; + + // Internal data to represent a logging channel. + struct LoggingChannel_t + { + bool HasTag( const char *pTag ) const + { + LoggingTag_t *pCurrentTag = m_pFirstTag; + while( pCurrentTag != NULL ) + { + if ( stricmp( pCurrentTag->m_pTagName, pTag ) == 0 ) + { + return true; + } + pCurrentTag = pCurrentTag->m_pNextTag; + } + return false; + } + bool IsEnabled( LoggingSeverity_t severity ) const { return severity >= m_MinimumSeverity; } + void SetSpewLevel( LoggingSeverity_t severity ) { m_MinimumSeverity = severity; } + + LoggingChannelID_t m_ID; + LoggingChannelFlags_t m_Flags; // an OR'd combination of LoggingChannelFlags_t + LoggingSeverity_t m_MinimumSeverity; // The minimum severity level required to activate this channel. + Color m_SpewColor; + char m_Name[MAX_LOGGING_IDENTIFIER_LENGTH]; + LoggingTag_t *m_pFirstTag; + }; + +private: + // Represents the current state of the logger (registered listeners, response policy class, etc.) and can + // vary from thread-to-thread. It can also be pushed/popped to save/restore listener/response state. + struct LoggingState_t + { + // Index of the previous entry on the listener set stack. + int m_nPreviousStackEntry; + + // Number of active listeners in this set. Cannot exceed MAX_LOGGING_LISTENER_COUNT. + // If set to -1, implies that this state structure is not in use. + int m_nListenerCount; + // Array of registered logging listener objects. + ILoggingListener *m_RegisteredListeners[MAX_LOGGING_LISTENER_COUNT]; + + // Specific policy class to determine behavior of logging system under specific message types. + ILoggingResponsePolicy *m_pLoggingResponse; + }; + + // These state functions to assume the caller has already grabbed the mutex. + LoggingState_t *GetCurrentState(); + const LoggingState_t *GetCurrentState() const; + + int FindUnusedStateIndex(); + LoggingTag_t *AllocTag( const char *pTagName ); + + int m_nChannelCount; + LoggingChannel_t m_RegisteredChannels[MAX_LOGGING_CHANNEL_COUNT]; + + int m_nChannelTagCount; + LoggingTag_t m_ChannelTags[MAX_LOGGING_TAG_COUNT]; + + // Index to first free character in name pool. + int m_nTagNamePoolIndex; + // Pool of character data used for tag names. + char m_TagNamePool[MAX_LOGGING_TAG_CHARACTER_COUNT]; + + // Protects all data in this class except the registered channels + // (which are supposed to be registered using the macros at static/global init time). + // It is assumed that this mutex is reentrant safe on all platforms. + CThreadFastMutex *m_pStateMutex; + + // The index of the current "global" state of the logging system. By default, all threads use this state + // for logging unless a given thread has pushed the logging state with bThreadLocal == true. + // If a thread-local state has been pushed, g_nThreadLocalStateIndex (a global thread-local integer) will be non-zero. + // By default, g_nThreadLocalStateIndex is 0 for all threads. + int m_nGlobalStateIndex; + + // A pool of logging states used to store a stack (potentially per-thread). + static const int MAX_LOGGING_STATE_COUNT = 16; + LoggingState_t m_LoggingStates[MAX_LOGGING_STATE_COUNT]; + + // Default policy class which determines behavior. + CDefaultLoggingResponsePolicy m_DefaultLoggingResponse; + + // Default spew function. + CSimpleLoggingListener m_DefaultLoggingListener; + +}; + +////////////////////////////////////////////////////////////////////////// +// Logging Macros +////////////////////////////////////////////////////////////////////////// + +// This macro will resolve to the most appropriate overload of LoggingSystem_Log() depending on the number of parameters passed in. +#define InternalMsg( Channel, Severity, /* [Color], Message, */ ... ) do { if ( LoggingSystem_IsChannelEnabled( Channel, Severity ) ) LoggingSystem_Log( Channel, Severity, /* [Color], Message, */ ##__VA_ARGS__ ); } while( 0 ) + +//----------------------------------------------------------------------------- +// New macros, use these! +// +// The macros take an optional Color parameter followed by the message +// and the message formatting. +// We rely on the variadic macro (__VA_ARGS__) operator to paste in the +// extra parameters and resolve to the appropriate overload. +//----------------------------------------------------------------------------- +#define Log_Msg( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_MESSAGE, /* [Color], Message, */ ##__VA_ARGS__ ) +#define Log_Warning( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_WARNING, /* [Color], Message, */ ##__VA_ARGS__ ) +#define Log_Error( Channel, /* [Color], Message, */ ... ) InternalMsg( Channel, LS_ERROR, /* [Color], Message, */ ##__VA_ARGS__ ) +#define Log_Assert( Message, ... ) LoggingSystem_LogAssert( Message, ##__VA_ARGS__ ) + + +#define DECLARE_LOGGING_CHANNEL( Channel ) extern LoggingChannelID_t Channel + +#define DEFINE_LOGGING_CHANNEL_NO_TAGS( Channel, ChannelName, /* [Flags], [Severity], [Color] */ ... ) \ + LoggingChannelID_t Channel = LoggingSystem_RegisterLoggingChannel( ChannelName, NULL, ##__VA_ARGS__ ) + +#define BEGIN_DEFINE_LOGGING_CHANNEL( Channel, ChannelName, /* [Flags], [Severity], [Color] */ ... ) \ + static void Register_##Channel##_Tags(); \ + LoggingChannelID_t Channel = LoggingSystem_RegisterLoggingChannel( ChannelName, Register_##Channel##_Tags, ##__VA_ARGS__ ); \ + void Register_##Channel##_Tags() \ + { + +#define ADD_LOGGING_CHANNEL_TAG( Tag ) LoggingSystem_AddTagToCurrentChannel( Tag ) + +#define END_DEFINE_LOGGING_CHANNEL() \ + } + +////////////////////////////////////////////////////////////////////////// +// DLL Exports +////////////////////////////////////////////////////////////////////////// + +// For documentation on these functions, please look at the corresponding function +// in CLoggingSystem (unless otherwise specified). +PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_RegisterLoggingChannel( const char *pName, RegisterTagsFunc registerTagsFunc, int flags = 0, LoggingSeverity_t severity = LS_MESSAGE, Color color = UNSPECIFIED_LOGGING_COLOR ); + +PLATFORM_INTERFACE void LoggingSystem_RegisterLoggingListener( ILoggingListener *pListener ); +PLATFORM_INTERFACE void LoggingSystem_ResetCurrentLoggingState(); +PLATFORM_INTERFACE void LoggingSystem_SetLoggingResponsePolicy( ILoggingResponsePolicy *pResponsePolicy ); +// NOTE: PushLoggingState() saves the current logging state on a stack and results in a new clear state +// (no listeners, default logging response policy). +PLATFORM_INTERFACE void LoggingSystem_PushLoggingState( bool bThreadLocal = false, bool bClearState = true ); +PLATFORM_INTERFACE void LoggingSystem_PopLoggingState( bool bThreadLocal = false ); + +PLATFORM_INTERFACE void LoggingSystem_AddTagToCurrentChannel( const char *pTagName ); + +// Returns INVALID_LOGGING_CHANNEL_ID if not found +PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_FindChannel( const char *pChannelName ); +PLATFORM_INTERFACE int LoggingSystem_GetChannelCount(); +PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_GetFirstChannelID(); +// Returns INVALID_LOGGING_CHANNEL_ID when there are no channels remaining. +PLATFORM_INTERFACE LoggingChannelID_t LoggingSystem_GetNextChannelID( LoggingChannelID_t channelID ); +PLATFORM_INTERFACE const CLoggingSystem::LoggingChannel_t *LoggingSystem_GetChannel( LoggingChannelID_t channelID ); + +PLATFORM_INTERFACE bool LoggingSystem_HasTag( LoggingChannelID_t channelID, const char *pTag ); + +PLATFORM_INTERFACE bool LoggingSystem_IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity ); +PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity ); +PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity ); +PLATFORM_INTERFACE void LoggingSystem_SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity ); + +// Color is represented as an int32 due to C-linkage restrictions +PLATFORM_INTERFACE int32 LoggingSystem_GetChannelColor( LoggingChannelID_t channelID ); +PLATFORM_INTERFACE void LoggingSystem_SetChannelColor( LoggingChannelID_t channelID, int color ); + +PLATFORM_INTERFACE LoggingChannelFlags_t LoggingSystem_GetChannelFlags( LoggingChannelID_t channelID ); +PLATFORM_INTERFACE void LoggingSystem_SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags ); + +//----------------------------------------------------------------------------- +// Logs a variable-argument to a given channel with the specified severity. +// NOTE: if adding overloads to this function, remember that the Log_*** +// macros simply pass their variadic parameters through to LoggingSystem_Log(). +// Therefore, you need to ensure that the parameters are in the same general +// order and that there are no ambiguities with the overload. +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, const char *pMessageFormat, ... ) FMTFUNCTION( 3, 4 ); +PLATFORM_OVERLOAD LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessageFormat, ... ) FMTFUNCTION( 4, 5 ); + +PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessage ); +PLATFORM_INTERFACE LoggingResponse_t LoggingSystem_LogAssert( const char *pMessageFormat, ... ) FMTFUNCTION( 1, 2 ); + +#endif // LOGGING_H \ No newline at end of file diff --git a/public/tier0/mem.h b/public/tier0/mem.h new file mode 100644 index 0000000..48131f0 --- /dev/null +++ b/public/tier0/mem.h @@ -0,0 +1,53 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Memory allocation! +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TIER0_MEM_H +#define TIER0_MEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#ifdef LINUX +#undef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + +#include "tier0/platform.h" + +#if !defined(STATIC_TIER0) && !defined(_STATIC_LINKED) + +#ifdef TIER0_DLL_EXPORT +# define MEM_INTERFACE DLL_EXPORT +#else +# define MEM_INTERFACE DLL_IMPORT +#endif + +#else // BUILD_AS_DLL + +#define MEM_INTERFACE extern + +#endif // BUILD_AS_DLL + + + +//----------------------------------------------------------------------------- +// DLL-exported methods for particular kinds of memory +//----------------------------------------------------------------------------- +MEM_INTERFACE void *MemAllocScratch( int nMemSize ); +MEM_INTERFACE void MemFreeScratch(); + +#ifdef POSIX +MEM_INTERFACE void ZeroMemory( void *mem, size_t length ); +#endif + +//Only works with USE_MEM_DEBUG and memory allocation call stack tracking enabled. +MEM_INTERFACE int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut ); + + +#endif /* TIER0_MEM_H */ diff --git a/public/tier0/memalloc.h b/public/tier0/memalloc.h new file mode 100644 index 0000000..5965ccf --- /dev/null +++ b/public/tier0/memalloc.h @@ -0,0 +1,574 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This header should never be used directly from leaf code!!! +// Instead, just add the file memoverride.cpp into your project and all this +// will automagically be used +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TIER0_MEMALLOC_H +#define TIER0_MEMALLOC_H + +#ifdef _WIN32 +#pragma once +#endif + +// These memory debugging switches aren't relevant under Linux builds since memoverride.cpp +// isn't built into Linux projects +#ifndef POSIX +// Define this in release to get memory tracking even in release builds +//#define USE_MEM_DEBUG 1 + +// Define this in release to get light memory debugging +//#define USE_LIGHT_MEM_DEBUG + +// Define this to require -uselmd to turn light memory debugging on +//#define LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH +#endif + +#if defined( _MEMTEST ) +#ifdef _WIN32 +#define USE_MEM_DEBUG 1 +#endif +#endif + +// Undefine this if using a compiler lacking threadsafe RTTI (like vc6) +#define MEM_DEBUG_CLASSNAME 1 + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +#include +#ifdef LINUX +#undef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + +#include "tier0/mem.h" + +struct _CrtMemState; + +#define MEMALLOC_VERSION 1 + +typedef size_t (*MemAllocFailHandler_t)( size_t ); + +//----------------------------------------------------------------------------- +// NOTE! This should never be called directly from leaf code +// Just use new,delete,malloc,free etc. They will call into this eventually +//----------------------------------------------------------------------------- +abstract_class IMemAlloc +{ +private: + // Release versions + virtual void *Alloc( size_t nSize ) = 0; +public: + virtual void *Realloc( void *pMem, size_t nSize ) = 0; + + virtual void Free( void *pMem ) = 0; + virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) = 0; + +private: + // Debug versions + virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) = 0; +public: + virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; + virtual void Free( void *pMem, const char *pFileName, int nLine ) = 0; + virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) = 0; + + inline void *IndirectAlloc( size_t nSize ) { return Alloc( nSize ); } + inline void *IndirectAlloc( size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize, pFileName, nLine ); } + + // Returns size of a particular allocation + virtual size_t GetSize( void *pMem ) = 0; + + // Force file + line information for an allocation + virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) = 0; + virtual void PopAllocDbgInfo() = 0; + + // FIXME: Remove when we have our own allocator + // these methods of the Crt debug code is used in our codebase currently + virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc ) = 0; + virtual int CrtSetReportMode( int nReportType, int nReportMode ) = 0; + virtual int CrtIsValidHeapPointer( const void *pMem ) = 0; + virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) = 0; + virtual int CrtCheckMemory( void ) = 0; + virtual int CrtSetDbgFlag( int nNewFlag ) = 0; + virtual void CrtMemCheckpoint( _CrtMemState *pState ) = 0; + + // FIXME: Make a better stats interface + virtual void DumpStats() = 0; + virtual void DumpStatsFileBase( char const *pchFileBase ) = 0; + virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) = 0; + + // FIXME: Remove when we have our own allocator + virtual void* CrtSetReportFile( int nRptType, void* hFile ) = 0; + virtual void* CrtSetReportHook( void* pfnNewHook ) = 0; + virtual int CrtDbgReport( int nRptType, const char * szFile, + int nLine, const char * szModule, const char * pMsg ) = 0; + + virtual int heapchk() = 0; + + virtual bool IsDebugHeap() = 0; + + virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) = 0; + virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) = 0; + virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) = 0; + + virtual int GetVersion() = 0; + + virtual void CompactHeap() = 0; + + // Function called when malloc fails or memory limits hit to attempt to free up memory (can come in any thread) + virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) = 0; + + virtual void DumpBlockStats( void * ) = 0; + +#if defined( _MEMTEST ) + virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) = 0; +#endif + + // Returns 0 if no failure, otherwise the size_t of the last requested chunk + virtual size_t MemoryAllocFailed() = 0; + + virtual void CompactIncremental() = 0; + + virtual void OutOfMemory( size_t nBytesAttempted = 0 ) = 0; + + // Region-based allocations + virtual void *RegionAlloc( int region, size_t nSize ) = 0; + virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) = 0; + + // Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system + virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) = 0; +}; + +//----------------------------------------------------------------------------- +// Singleton interface +//----------------------------------------------------------------------------- +MEM_INTERFACE IMemAlloc *g_pMemAlloc; + +//----------------------------------------------------------------------------- + +#ifdef MEMALLOC_REGIONS +#ifndef MEMALLOC_REGION +#define MEMALLOC_REGION 0 +#endif +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->RegionAlloc( MEMALLOC_REGION, nSize, pFileName, nLine ); +} +#else +#undef MEMALLOC_REGION +inline void *MemAlloc_Alloc( size_t nSize ) +{ + return g_pMemAlloc->IndirectAlloc( nSize ); +} + +inline void *MemAlloc_Alloc( size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->IndirectAlloc( nSize, pFileName, nLine ); +} +#endif + +//----------------------------------------------------------------------------- + +#ifdef MEMALLOC_REGIONS +#else +#endif + + +inline bool ValueIsPowerOfTwo( size_t value ) // don't clash with mathlib definition +{ + return (value & ( value - 1 )) == 0; +} + + +inline void *MemAlloc_AllocAlignedUnattributed( size_t size, size_t align ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +inline void *MemAlloc_AllocAlignedFileLine( size_t size, size_t align, const char *pszFile, int nLine ) +{ + unsigned char *pAlloc, *pResult; + + if (!ValueIsPowerOfTwo(align)) + return NULL; + + align = (align > sizeof(void *) ? align : sizeof(void *)) - 1; + + if ( (pAlloc = (unsigned char*)MemAlloc_Alloc( sizeof(void *) + align + size, pszFile, nLine ) ) == (unsigned char*)NULL) + return NULL; + + pResult = (unsigned char*)( (size_t)(pAlloc + sizeof(void *) + align ) & ~align ); + ((unsigned char**)(pResult))[-1] = pAlloc; + + return (void *)pResult; +} + +#ifdef USE_MEM_DEBUG +#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedFileLine( s, a, __FILE__, __LINE__ ) +#elif defined(USE_LIGHT_MEM_DEBUG) +extern const char *g_pszModule; +#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedFileLine( s, a, g_pszModule, 0 ) +#else +#define MemAlloc_AllocAligned( s, a ) MemAlloc_AllocAlignedUnattributed( s, a ) +#endif + + +inline void *MemAlloc_ReallocAligned( void *ptr, size_t size, size_t align ) +{ + if ( !ValueIsPowerOfTwo( align ) ) + return NULL; + + // Don't change alignment between allocation + reallocation. + if ( ( (size_t)ptr & ( align - 1 ) ) != 0 ) + return NULL; + + if ( !ptr ) + return MemAlloc_AllocAligned( size, align ); + + void *pAlloc, *pResult; + + // Figure out the actual allocation point + pAlloc = ptr; + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + pAlloc = *( (void **)pAlloc ); + + // See if we have enough space + size_t nOffset = (size_t)ptr - (size_t)pAlloc; + size_t nOldSize = g_pMemAlloc->GetSize( pAlloc ); + if ( nOldSize >= size + nOffset ) + return ptr; + + pResult = MemAlloc_AllocAligned( size, align ); + memcpy( pResult, ptr, nOldSize - nOffset ); + g_pMemAlloc->Free( pAlloc ); + return pResult; +} + +inline void MemAlloc_FreeAligned( void *pMemBlock ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *( (void **)pAlloc ); + g_pMemAlloc->Free( pAlloc ); +} + +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile, int nLine ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *( (void **)pAlloc ); + g_pMemAlloc->Free( pAlloc, pszFile, nLine ); +} + +inline size_t MemAlloc_GetSizeAligned( void *pMemBlock ) +{ + void *pAlloc; + + if ( pMemBlock == NULL ) + return 0; + + pAlloc = pMemBlock; + + // pAlloc points to the pointer to starting of the memory block + pAlloc = (void *)(((size_t)pAlloc & ~( sizeof(void *) - 1 ) ) - sizeof(void *)); + + // pAlloc is the pointer to the start of memory block + pAlloc = *((void **)pAlloc ); + return g_pMemAlloc->GetSize( pAlloc ) - ( (byte *)pMemBlock - (byte *)pAlloc ); +} + + +struct aligned_tmp_t +{ + // empty base class +}; + +// template here to allow adding alignment at levels of hierarchy that aren't the base +template< int bytesAlignment = 16, class T = aligned_tmp_t > +class CAlignedNewDelete : public T +{ +public: + void *operator new( size_t nSize ) + { + return MemAlloc_AllocAligned( nSize, bytesAlignment ); + } + + void* operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) + { + return MemAlloc_AllocAlignedFileLine( nSize, bytesAlignment, pFileName, nLine ); + } + + void operator delete(void *pData) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData ); + } + } + + void operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine ) + { + if ( pData ) + { + MemAlloc_FreeAligned( pData, pFileName, nLine ); + } + } +}; + +//----------------------------------------------------------------------------- + +#if (defined(_DEBUG) || defined(USE_MEM_DEBUG)) +#define MEM_ALLOC_CREDIT_(tag) CMemAllocAttributeAlloction memAllocAttributeAlloction( tag, __LINE__ ) +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) g_pMemAlloc->PushAllocDbgInfo( pszFile, line ) +#define MemAlloc_PopAllocDbgInfo() g_pMemAlloc->PopAllocDbgInfo() +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) +#else +#define MEM_ALLOC_CREDIT_(tag) ((void)0) +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) ((void)0) +#define MemAlloc_PopAllocDbgInfo() ((void)0) +#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0) +#endif + +//----------------------------------------------------------------------------- + +class CMemAllocAttributeAlloction +{ +public: + CMemAllocAttributeAlloction( const char *pszFile, int line ) + { + MemAlloc_PushAllocDbgInfo( pszFile, line ); + } + + ~CMemAllocAttributeAlloction() + { + MemAlloc_PopAllocDbgInfo(); + } +}; + +#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) + +//----------------------------------------------------------------------------- + +#if 0 + + #pragma warning(disable:4290) + #pragma warning(push) + + // MEM_DEBUG_CLASSNAME is opt-in. + // Note: typeid().name() is not threadsafe, so if the project needs to access it in multiple threads + // simultaneously, it'll need a mutex. + #if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME) + + template const char *MemAllocClassName( T *p ) + { + static const char *pszName = typeid(*p).name(); // @TODO: support having debug heap ignore certain allocations, and ignore memory allocated here [5/7/2009 tom] + return pszName; + } + + #define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( MemAllocClassName( this ) ) + #define MEM_ALLOC_CLASSNAME(type) (typeid((type*)(0)).name()) + #else + #define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_( __FILE__ ) + #define MEM_ALLOC_CLASSNAME(type) (__FILE__) + #endif + + // MEM_ALLOC_CREDIT_FUNCTION is used when no this pointer is available ( inside 'new' overloads, for example ) + #ifdef _MSC_VER + #define MEM_ALLOC_CREDIT_FUNCTION() MEM_ALLOC_CREDIT_( __FUNCTION__ ) + #else + #define MEM_ALLOC_CREDIT_FUNCTION() (__FILE__) + #endif + + #pragma warning(pop) +#else + #define MEM_ALLOC_CREDIT_CLASS() + #define MEM_ALLOC_CLASSNAME(type) NULL + #define MEM_ALLOC_CREDIT_FUNCTION() +#endif + +//----------------------------------------------------------------------------- + +#if (defined(_DEBUG) || defined(USE_MEM_DEBUG)) +struct MemAllocFileLine_t +{ + const char *pszFile; + int line; +}; + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) \ + static CUtlMap s_##tag##Allocs( DefLessFunc( void *) ); \ + CUtlMap * g_p##tag##Allocs = &s_##tag##Allocs; \ + const char * g_psz##tag##Alloc = strcpy( (char *)MemAlloc_Alloc( strlen( #tag "Alloc" ) + 1, "intentional leak", 0 ), #tag "Alloc" ); + +#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag ) \ + extern CUtlMap * g_p##tag##Allocs; \ + extern const char * g_psz##tag##Alloc; + +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) \ + if ( !p ) \ + ; \ + else \ + { \ + MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \ + g_pMemAlloc->GetActualDbgInfo( fileLine.pszFile, fileLine.line ); \ + if ( fileLine.pszFile != g_psz##tag##Alloc ) \ + { \ + g_p##tag##Allocs->Insert( p, fileLine ); \ + } \ + \ + MemAlloc_RegisterAllocation( fileLine.pszFile, fileLine.line, size, size, 0); \ + } + +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) \ + if ( !p ) \ + ; \ + else \ + { \ + MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \ + CUtlMap::IndexType_t iRecordedFileLine = g_p##tag##Allocs->Find( p ); \ + if ( iRecordedFileLine != g_p##tag##Allocs->InvalidIndex() ) \ + { \ + fileLine = (*g_p##tag##Allocs)[iRecordedFileLine]; \ + g_p##tag##Allocs->RemoveAt( iRecordedFileLine ); \ + } \ + \ + MemAlloc_RegisterDeallocation( fileLine.pszFile, fileLine.line, size, size, 0); \ + } + +#else + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) +#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag ) +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) + +#endif + +//----------------------------------------------------------------------------- + +#endif // !STEAM && !NO_MALLOC_OVERRIDE + +//----------------------------------------------------------------------------- + +#if !defined(STEAM) && defined(NO_MALLOC_OVERRIDE) +#include + +#define MEM_ALLOC_CREDIT_(tag) ((void)0) +#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__) +#define MEM_ALLOC_CREDIT_CLASS() +#define MEM_ALLOC_CLASSNAME(type) NULL + +#define MemAlloc_PushAllocDbgInfo( pszFile, line ) +#define MemAlloc_PopAllocDbgInfo() + +#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) +#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0) +#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0) + +inline void *MemAlloc_AllocAligned( size_t size, size_t align ) +{ + return (void *)_aligned_malloc( size, align ); +} +inline void *MemAlloc_AllocAligned( size_t size, size_t align, const char *pszFile, int nLine ) +{ + pszFile = pszFile; + nLine = nLine; + return (void *)_aligned_malloc( size, align ); +} + +inline void MemAlloc_FreeAligned( void *pMemBlock ) +{ + _aligned_free( pMemBlock ); +} +inline void MemAlloc_FreeAligned( void *pMemBlock, const char *pszFile, int nLine ) +{ + pszFile = pszFile; + nLine = nLine; + _aligned_free( pMemBlock ); +} + +#endif // !STEAM && NO_MALLOC_OVERRIDE + +//----------------------------------------------------------------------------- + + + +// linux memory tracking via hooks. +#ifdef _POSIX +PLATFORM_INTERFACE void MemoryLogMessage( char const *s ); // throw a message into the memory log +PLATFORM_INTERFACE void EnableMemoryLogging( bool bOnOff ); +PLATFORM_INTERFACE void DumpMemoryLog( int nThresh ); +PLATFORM_INTERFACE void DumpMemorySummary( void ); +PLATFORM_INTERFACE void SetMemoryMark( void ); +PLATFORM_INTERFACE void DumpChangedMemory( int nThresh ); + +// ApproximateProcessMemoryUsage returns the approximate memory footprint of this process. +PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage( void ); +#else +inline void MemoryLogMessage( char const * ) +{ +} +inline void EnableMemoryLogging( bool ) +{ +} +inline void DumpMemoryLog( int ) +{ +} +inline void DumpMemorySummary( void ) +{ +} +inline void SetMemoryMark( void ) +{ +} +inline void DumpChangedMemory( int ) +{ +} +inline size_t ApproximateProcessMemoryUsage( void ) +{ + return 0; +} +#endif + + +#endif /* TIER0_MEMALLOC_H */ diff --git a/public/tier0/memdbgoff.h b/public/tier0/memdbgoff.h new file mode 100644 index 0000000..4d2c021 --- /dev/null +++ b/public/tier0/memdbgoff.h @@ -0,0 +1,25 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This header, which must be the final line of a .h file, +// causes all crt methods to stop using debugging versions of the memory allocators. +// NOTE: Use memdbgon.h to re-enable memory debugging. +// +// $NoKeywords: $ +//=============================================================================// + +#ifdef MEM_OVERRIDE_ON + +#undef malloc +#undef realloc +#undef calloc +#undef free +#undef _expand +#undef _msize +#undef new +#undef _aligned_malloc +#undef _aligned_free +#undef _malloc_dbg + +#undef MEM_OVERRIDE_ON + +#endif diff --git a/public/tier0/memdbgon.h b/public/tier0/memdbgon.h new file mode 100644 index 0000000..991667e --- /dev/null +++ b/public/tier0/memdbgon.h @@ -0,0 +1,263 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This header, which must be the final include in a .cpp (or .h) file, +// causes all crt methods to use debugging versions of the memory allocators. +// NOTE: Use memdbgoff.h to disable memory debugging. +// +// $NoKeywords: $ +//=============================================================================// + +// SPECIAL NOTE! This file must *not* use include guards; we need to be able +// to include this potentially multiple times (since we can deactivate debugging +// by including memdbgoff.h) + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) + +// SPECIAL NOTE #2: This must be the final include in a .cpp or .h file!!! + +#if defined(_DEBUG) && !defined(USE_MEM_DEBUG) +#define USE_MEM_DEBUG 1 +#endif + +// If debug build or ndebug and not already included MS custom alloc files, or already included this file +#if (defined(_DEBUG) || !defined(_INC_CRTDBG)) || defined(MEMDBGON_H) + +#include "tier0/basetypes.h" + +#include "tier0/valve_off.h" + #ifdef COMPILER_MSVC + #include + #else + #include + #endif + #include + #include +#include "tier0/valve_on.h" + +#include "commonmacros.h" +#include "memalloc.h" + +#ifdef _WIN32 +#ifndef MEMALLOC_REGION +#define MEMALLOC_REGION 0 +#endif +#else +#undef MEMALLOC_REGION +#endif + +#if defined(USE_MEM_DEBUG) + #if defined(POSIX) + #define _NORMAL_BLOCK 1 + + #include "tier0/valve_off.h" + #include + #include + #include + #include + #if !defined( DID_THE_OPERATOR_NEW ) + #define DID_THE_OPERATOR_NEW + // posix doesn't have a new of this form, so we impl our own + void* operator new( size_t nSize, int blah, const char *pFileName, int nLine ); + void* operator new[]( size_t nSize, int blah, const char *pFileName, int nLine ); + #endif + + #else // defined(POSIX) + + // Include crtdbg.h and make sure _DEBUG is set to 1. + #if !defined(_DEBUG) + #define _DEBUG 1 + #include + #undef _DEBUG + #else + #include + #endif // !defined(_DEBUG) + + #endif // defined(POSIX) +#endif + +#include "tier0/memdbgoff.h" + +// -------------------------------------------------------- +// Debug/non-debug agnostic elements + +#define MEM_OVERRIDE_ON 1 + +#undef malloc +#undef realloc +#undef calloc +#undef _expand +#undef free +#undef _msize +#undef _aligned_malloc +#undef _aligned_free + +#ifndef MEMDBGON_H +inline void *MemAlloc_InlineCallocMemset( void *pMem, size_t nCount, size_t nElementSize) +{ + memset(pMem, 0, nElementSize * nCount); + return pMem; +} +#endif + +#define calloc(c, s) MemAlloc_InlineCallocMemset(malloc(c*s), c, s) +#ifndef USE_LIGHT_MEM_DEBUG +#define free(p) g_pMemAlloc->Free( p ) +#define _aligned_free( p ) MemAlloc_FreeAligned( p ) +#else +extern const char *g_pszModule; +#define free(p) g_pMemAlloc->Free( p, g_pszModule, 0 ) +#define _aligned_free( p ) MemAlloc_FreeAligned( p, g_pszModule, 0 ) +#endif +#define _msize(p) g_pMemAlloc->GetSize( p ) +#define _expand(p, s) _expand_NoLongerSupported(p, s) + +// -------------------------------------------------------- +// Debug path +#if defined(USE_MEM_DEBUG) + +#define malloc(s) MemAlloc_Alloc( s, __FILE__, __LINE__) +#define realloc(p, s) g_pMemAlloc->Realloc( p, s, __FILE__, __LINE__ ) +#define _aligned_malloc( s, a ) MemAlloc_AllocAlignedFileLine( s, a, __FILE__, __LINE__ ) + +#define _malloc_dbg(s, t, f, l) WHYCALLINGTHISDIRECTLY(s) + +#if !defined( LINUX ) +#if defined(__AFX_H__) && defined(DEBUG_NEW) + #define new DEBUG_NEW +#else + #undef new + #define MEMALL_DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new MEMALL_DEBUG_NEW +#endif +#endif + +#undef _strdup +#undef strdup +#undef _wcsdup +#undef wcsdup + +#define _strdup(s) MemAlloc_StrDup(s, __FILE__, __LINE__) +#define strdup(s) MemAlloc_StrDup(s, __FILE__, __LINE__) +#define _wcsdup(s) MemAlloc_WcStrDup(s, __FILE__, __LINE__) +#define wcsdup(s) MemAlloc_WcStrDup(s, __FILE__, __LINE__) + +// Make sure we don't define strdup twice +#if !defined(MEMDBGON_H) + +inline char *MemAlloc_StrDup(const char *pString, const char *pFileName, unsigned nLine) +{ + char *pMemory; + + if (!pString) + return NULL; + + size_t len = strlen(pString) + 1; + if ((pMemory = (char *)MemAlloc_Alloc(len, pFileName, nLine)) != NULL) + { + return strcpy( pMemory, pString ); + } + + return NULL; +} + +inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString, const char *pFileName, unsigned nLine) +{ + wchar_t *pMemory; + + if (!pString) + return NULL; + + size_t len = (wcslen(pString) + 1); + if ((pMemory = (wchar_t *)MemAlloc_Alloc(len * sizeof(wchar_t), pFileName, nLine)) != NULL) + { + return wcscpy( pMemory, pString ); + } + + return NULL; +} + +#endif // DBMEM_DEFINED_STRDUP + +#else +// -------------------------------------------------------- +// Release path + +#ifndef USE_LIGHT_MEM_DEBUG +#define malloc(s) MemAlloc_Alloc( s ) +#define realloc(p, s) g_pMemAlloc->Realloc( p, s ) +#define _aligned_malloc( s, a ) MemAlloc_AllocAligned( s, a ) +#else +#define malloc(s) MemAlloc_Alloc( s, g_pszModule, 0 ) +#define realloc(p, s) g_pMemAlloc->Realloc( p, s, g_pszModule, 0 ) +#define _aligned_malloc( s, a ) MemAlloc_AllocAlignedFileLine( s, a, g_pszModule, 0 ) +#endif + +#ifndef _malloc_dbg +#define _malloc_dbg(s, t, f, l) WHYCALLINGTHISDIRECTLY(s) +#endif + +#undef new + +#undef _strdup +#undef strdup +#undef _wcsdup +#undef wcsdup + +#define _strdup(s) MemAlloc_StrDup(s) +#define strdup(s) MemAlloc_StrDup(s) +#define _wcsdup(s) MemAlloc_WcStrDup(s) +#define wcsdup(s) MemAlloc_WcStrDup(s) + +// Make sure we don't define strdup twice +#if !defined(MEMDBGON_H) + +inline char *MemAlloc_StrDup(const char *pString) +{ + char *pMemory; + + if (!pString) + return NULL; + + size_t len = strlen(pString) + 1; + if ((pMemory = (char *)malloc(len)) != NULL) + { + return strcpy( pMemory, pString ); + } + + return NULL; +} + +inline wchar_t *MemAlloc_WcStrDup(const wchar_t *pString) +{ + wchar_t *pMemory; + + if (!pString) + return NULL; + + size_t len = (wcslen(pString) + 1); + if ((pMemory = (wchar_t *)malloc(len * sizeof(wchar_t))) != NULL) + { + return wcscpy( pMemory, pString ); + } + + return NULL; +} + +#endif // DBMEM_DEFINED_STRDUP + +#endif // USE_MEM_DEBUG + +#define MEMDBGON_H // Defined here so can be used above + +#else + +#if defined(USE_MEM_DEBUG) +#ifndef _STATIC_LINKED +#pragma message ("Note: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build") +#else +#error "Error: file includes crtdbg.h directly, therefore will cannot use memdbgon.h in non-debug build. Not recoverable in static build" +#endif +#endif +#endif // _INC_CRTDBG + +#endif // !STEAM && !NO_MALLOC_OVERRIDE diff --git a/public/tier0/memoverride.cpp b/public/tier0/memoverride.cpp new file mode 100644 index 0000000..a362281 --- /dev/null +++ b/public/tier0/memoverride.cpp @@ -0,0 +1,1385 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Insert this file into all projects using the memory system +// It will cause that project to use the shader memory allocator +// +// $NoKeywords: $ +//=============================================================================// + + +#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE) +#define AVOID_INCLUDING_ALGORITHM + +#undef PROTECTED_THINGS_ENABLE // allow use of _vsnprintf + +#if defined( _WIN32 ) && !defined( _X360 ) +#define WIN_32_LEAN_AND_MEAN +#include +#endif + +#include "tier0/dbg.h" +#include "tier0/memalloc.h" +#include +#include +#include "memdbgoff.h" + +#ifdef _WIN32 +// ARG: crtdbg is necessary for certain definitions below, +// but it also redefines malloc as a macro in release. +// To disable this, we gotta define _DEBUG before including it.. BLEAH! +#define _DEBUG 1 +#include "crtdbg.h" +#ifdef NDEBUG +#undef _DEBUG +#endif + +// Turn this back off in release mode. +#ifdef NDEBUG +#undef _DEBUG +#endif +#elif POSIX +#define __cdecl +#endif + +#if defined(USE_LIGHT_MEM_DEBUG) || defined(USE_MEM_DEBUG) +#pragma optimize( "", off ) +#define inline +#endif + +const char *g_pszModule = MKSTRING( MEMOVERRIDE_MODULE ); +inline void *AllocUnattributed( size_t nSize ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + return MemAlloc_Alloc(nSize); +#else + return MemAlloc_Alloc(nSize, g_pszModule, 0); +#endif +} + +inline void *ReallocUnattributed( void *pMem, size_t nSize ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + return g_pMemAlloc->Realloc(pMem, nSize); +#else + return g_pMemAlloc->Realloc(pMem, nSize, g_pszModule, 0); +#endif +} + +#undef inline + + +//----------------------------------------------------------------------------- +// Standard functions in the CRT that we're going to override to call our allocator +//----------------------------------------------------------------------------- +#if defined(_WIN32) && !defined(_STATIC_LINKED) +// this magic only works under win32 +// under linux this malloc() overrides the libc malloc() and so we +// end up in a recursion (as MemAlloc_Alloc() calls malloc) +#if _MSC_VER >= 1400 +#define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT +#define FREE_CALL _CRTNOALIAS +#else +#define ALLOC_CALL +#define FREE_CALL +#endif + +extern "C" +{ + +ALLOC_CALL void *malloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +FREE_CALL void free( void *pMem ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + g_pMemAlloc->Free(pMem); +#else + g_pMemAlloc->Free(pMem, g_pszModule, 0 ); +#endif +} + +ALLOC_CALL void *realloc( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +ALLOC_CALL void *calloc( size_t nCount, size_t nElementSize ) +{ + void *pMem = AllocUnattributed( nElementSize * nCount ); + memset(pMem, 0, nElementSize * nCount); + return pMem; +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// Non-standard MSVC functions that we're going to override to call our allocator +//----------------------------------------------------------------------------- +extern "C" +{ + +// 64-bit +#ifdef _WIN64 +void* __cdecl _malloc_base( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} +#else +void *_malloc_base( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} +#endif + +void *_calloc_base( size_t nSize ) +{ + void *pMem = AllocUnattributed( nSize ); + memset(pMem, 0, nSize); + return pMem; +} + +void *_realloc_base( void *pMem, size_t nSize ) +{ + return ReallocUnattributed( pMem, nSize ); +} + +void *_recalloc_base( void *pMem, size_t nSize ) +{ + void *pMemOut = ReallocUnattributed( pMem, nSize ); + memset(pMemOut, 0, nSize); + return pMemOut; +} + +void _free_base( void *pMem ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + g_pMemAlloc->Free(pMem); +#else + g_pMemAlloc->Free(pMem, g_pszModule, 0 ); +#endif +} + +void *__cdecl _expand_base( void *pMem, size_t nNewSize, int nBlockUse ) +{ + Assert( 0 ); + return NULL; +} + +// crt +void * __cdecl _malloc_crt(size_t size) +{ + return AllocUnattributed( size ); +} + +void * __cdecl _calloc_crt(size_t count, size_t size) +{ + return _calloc_base( count * size ); +} + +void * __cdecl _realloc_crt(void *ptr, size_t size) +{ + return _realloc_base( ptr, size ); +} + +void * __cdecl _recalloc_crt(void *ptr, size_t count, size_t size) +{ + return _recalloc_base( ptr, size * count ); +} + +ALLOC_CALL void * __cdecl _recalloc ( void * memblock, size_t count, size_t size ) +{ + void *pMem = ReallocUnattributed( memblock, size * count ); + memset( pMem, 0, size * count ); + return pMem; +} + +size_t _msize_base( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +size_t _msize( void *pMem ) +{ + return _msize_base(pMem); +} + +size_t msize( void *pMem ) +{ + return g_pMemAlloc->GetSize(pMem); +} + +void *__cdecl _heap_alloc( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _nh_malloc( size_t nSize, int ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl _expand( void *pMem, size_t nSize ) +{ + Assert( 0 ); + return NULL; +} + +unsigned int _amblksiz = 16; //BYTES_PER_PARA; + +#if _MSC_VER >= 1400 +HANDLE _crtheap = (HANDLE)1; // PatM Can't be 0 or CRT pukes +int __active_heap = 1; +#endif // _MSC_VER >= 1400 + +size_t __cdecl _get_sbh_threshold( void ) +{ + return 0; +} + +int __cdecl _set_sbh_threshold( size_t ) +{ + return 0; +} + +int _heapchk() +{ + return g_pMemAlloc->heapchk(); +} + +int _heapmin() +{ + return 1; +} + +int __cdecl _heapadd( void *, size_t ) +{ + return 0; +} + +int __cdecl _heapset( unsigned int ) +{ + return 0; +} + +size_t __cdecl _heapused( size_t *, size_t * ) +{ + return 0; +} + +#ifdef _WIN32 +#include +int __cdecl _heapwalk( _HEAPINFO * ) +{ + return 0; +} +#endif + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Debugging functions that we're going to override to call our allocator +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- + +extern "C" +{ + +void *malloc_db( size_t nSize, const char *pFileName, int nLine ) +{ + return MemAlloc_Alloc(nSize, pFileName, nLine); +} + +void free_db( void *pMem, const char *pFileName, int nLine ) +{ + g_pMemAlloc->Free(pMem, pFileName, nLine); +} + +void *realloc_db( void *pMem, size_t nSize, const char *pFileName, int nLine ) +{ + return g_pMemAlloc->Realloc(pMem, nSize, pFileName, nLine); +} + +} // end extern "C" + +//----------------------------------------------------------------------------- +// These methods are standard MSVC heap initialization + shutdown methods +//----------------------------------------------------------------------------- +extern "C" +{ + +#if !defined( _X360 ) + int __cdecl _heap_init() + { + return g_pMemAlloc != NULL; + } + + void __cdecl _heap_term() + { + } +#endif + +} +#endif + + +//----------------------------------------------------------------------------- +// Prevents us from using an inappropriate new or delete method, +// ensures they are here even when linking against debug or release static libs +//----------------------------------------------------------------------------- +#ifndef NO_MEMOVERRIDE_NEW_DELETE +void *__cdecl operator new( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return MemAlloc_Alloc(nSize, pFileName, nLine ); +} + +void __cdecl operator delete( void *pMem ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + g_pMemAlloc->Free(pMem); +#else + g_pMemAlloc->Free(pMem, g_pszModule, 0 ); +#endif +} + +void *__cdecl operator new[] ( size_t nSize ) +{ + return AllocUnattributed( nSize ); +} + +void *__cdecl operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) +{ + return MemAlloc_Alloc(nSize, pFileName, nLine); +} + +void __cdecl operator delete[] ( void *pMem ) +{ +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + g_pMemAlloc->Free(pMem); +#else + g_pMemAlloc->Free(pMem, g_pszModule, 0 ); +#endif +} +#endif + + +//----------------------------------------------------------------------------- +// Override some debugging allocation methods in MSVC +// NOTE: These have to be here for release + debug builds in case we +// link to a debug static lib!!! +//----------------------------------------------------------------------------- +#ifndef _STATIC_LINKED +#ifdef _WIN32 + +// This here just hides the internal file names, etc of allocations +// made in the c runtime library +#define CRT_INTERNAL_FILE_NAME "C-runtime internal" + +class CAttibCRT +{ +public: + CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse) + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0); + } + } + + ~CAttibCRT() + { + if (m_nBlockUse == _CRT_BLOCK) + { + g_pMemAlloc->PopAllocDbgInfo(); + } + } + +private: + int m_nBlockUse; +}; + + +#define AttribIfCrt() CAttibCRT _attrib(nBlockUse) +#elif defined(POSIX) +#define AttribIfCrt() +#endif // _WIN32 + + +extern "C" +{ + +void *__cdecl _nh_malloc_dbg( size_t nSize, int nFlag, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return MemAlloc_Alloc(nSize, pFileName, nLine); +} + +void *__cdecl _malloc_dbg( size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return MemAlloc_Alloc(nSize, pFileName, nLine); +} + +#if defined( _X360 ) +void *__cdecl _calloc_dbg_impl( size_t nNum, size_t nSize, int nBlockUse, + const char * szFileName, int nLine, int * errno_tmp ) +{ + return _calloc_dbg( nNum, nSize, nBlockUse, szFileName, nLine ); +} +#endif + +void *__cdecl _calloc_dbg( size_t nNum, size_t nSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + void *pMem = MemAlloc_Alloc(nSize * nNum, pFileName, nLine); + memset(pMem, 0, nSize * nNum); + return pMem; +} + +void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + AttribIfCrt(); + return g_pMemAlloc->Realloc(pMem, nNewSize, pFileName, nLine); +} + +void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse, + const char *pFileName, int nLine ) +{ + Assert( 0 ); + return NULL; +} + +void __cdecl _free_dbg( void *pMem, int nBlockUse ) +{ + AttribIfCrt(); +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + g_pMemAlloc->Free(pMem); +#else + g_pMemAlloc->Free(pMem, g_pszModule, 0 ); +#endif +} + +size_t __cdecl _msize_dbg( void *pMem, int nBlockUse ) +{ +#ifdef _WIN32 + return _msize(pMem); +#elif POSIX + Assert( "_msize_dbg unsupported" ); + return 0; +#endif +} + + +#ifdef _WIN32 + +#if defined(_DEBUG) && _MSC_VER >= 1300 +// X360TBD: aligned and offset allocations may be important on the 360 + +// aligned base +ALLOC_CALL void *__cdecl _aligned_malloc_base( size_t size, size_t align ) +{ + return MemAlloc_AllocAligned( size, align ); +} + +ALLOC_CALL void *__cdecl _aligned_realloc_base( void *ptr, size_t size, size_t align ) +{ + return MemAlloc_ReallocAligned( ptr, size, align ); +} + +ALLOC_CALL void *__cdecl _aligned_recalloc_base( void *ptr, size_t size, size_t align ) +{ + Error( "Unsupported function\n" ); + return NULL; +} + +FREE_CALL void __cdecl _aligned_free_base( void *ptr ) +{ + MemAlloc_FreeAligned( ptr ); +} + +// aligned +ALLOC_CALL void * __cdecl _aligned_malloc( size_t size, size_t align ) +{ + return _aligned_malloc_base(size, align); +} + +ALLOC_CALL void *__cdecl _aligned_realloc(void *memblock, size_t size, size_t align) +{ + return _aligned_realloc_base(memblock, size, align); +} + +ALLOC_CALL void * __cdecl _aligned_recalloc( void * memblock, size_t count, size_t size, size_t align ) +{ + return _aligned_recalloc_base(memblock, count * size, align); +} + +FREE_CALL void __cdecl _aligned_free( void *memblock ) +{ + _aligned_free_base(memblock); +} + +// aligned offset base +ALLOC_CALL void * __cdecl _aligned_offset_malloc_base( size_t size, size_t align, size_t offset ) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +ALLOC_CALL void * __cdecl _aligned_offset_realloc_base( void * memblock, size_t size, size_t align, size_t offset) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +ALLOC_CALL void * __cdecl _aligned_offset_recalloc_base( void * memblock, size_t size, size_t align, size_t offset) +{ + Assert( IsPC() || 0 ); + return NULL; +} + +// aligned offset +ALLOC_CALL void *__cdecl _aligned_offset_malloc(size_t size, size_t align, size_t offset) +{ + return _aligned_offset_malloc_base( size, align, offset ); +} + +ALLOC_CALL void *__cdecl _aligned_offset_realloc(void *memblock, size_t size, size_t align, size_t offset) +{ + return _aligned_offset_realloc_base( memblock, size, align, offset ); +} + +ALLOC_CALL void * __cdecl _aligned_offset_recalloc( void * memblock, size_t count, size_t size, size_t align, size_t offset ) +{ + return _aligned_offset_recalloc_base( memblock, count * size, align, offset ); +} + +#endif // _MSC_VER >= 1400 + +#endif + +} // end extern "C" + + +//----------------------------------------------------------------------------- +// Override some the _CRT debugging allocation methods in MSVC +//----------------------------------------------------------------------------- +#ifdef _WIN32 + +extern "C" +{ + +int _CrtDumpMemoryLeaks(void) +{ + return 0; +} + +_CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient ) +{ + return NULL; +} + +int _CrtSetDbgFlag( int nNewFlag ) +{ + return g_pMemAlloc->CrtSetDbgFlag( nNewFlag ); +} + +// 64-bit port. +#define AFNAME(var) __p_ ## var +#define AFRET(var) &var + +int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF; +int* AFNAME(_crtDbgFlag)(void) +{ + return AFRET(_crtDbgFlag); +} + +long _crtBreakAlloc; /* Break on this allocation */ +long* AFNAME(_crtBreakAlloc) (void) +{ + return AFRET(_crtBreakAlloc); +} + +void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse ) +{ + DebuggerBreak(); +} + +_CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook ) +{ + DebuggerBreak(); + return NULL; +} + +long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc ) +{ + return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc ); +} + +int __cdecl _CrtIsValidHeapPointer( const void *pMem ) +{ + return g_pMemAlloc->CrtIsValidHeapPointer( pMem ); +} + +int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access ) +{ + return g_pMemAlloc->CrtIsValidPointer( pMem, size, access ); +} + +int __cdecl _CrtCheckMemory( void ) +{ + // FIXME: Remove this when we re-implement the heap + return g_pMemAlloc->CrtCheckMemory( ); +} + +int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize, + long *plRequestNumber, char **ppFileName, int *pnLine ) +{ + DebuggerBreak(); + return 1; +} + +int __cdecl _CrtMemDifference( _CrtMemState *pState, const _CrtMemState * oldState, const _CrtMemState * newState ) +{ + DebuggerBreak(); + return FALSE; +} + +void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtMemCheckpoint( _CrtMemState *pState ) +{ + // FIXME: Remove this when we re-implement the heap + g_pMemAlloc->CrtMemCheckpoint( pState ); +} + +void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState ) +{ + DebuggerBreak(); +} + +void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pContext ) +{ + DebuggerBreak(); +} + + +//----------------------------------------------------------------------------- +// Methods in dbgrpt.cpp +//----------------------------------------------------------------------------- +long _crtAssertBusy = -1; + +int __cdecl _CrtSetReportMode( int nReportType, int nReportMode ) +{ + return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode ); +} + +_HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile ) +{ + return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile ); +} + +_CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook ) +{ + return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook ); +} + +int __cdecl _CrtDbgReport( int nRptType, const char * szFile, + int nLine, const char * szModule, const char * szFormat, ... ) +{ + static char output[1024]; + va_list args; + va_start( args, szFormat ); + _vsnprintf( output, sizeof( output )-1, szFormat, args ); + va_end( args ); + + return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output ); +} + +#if _MSC_VER >= 1400 + +#if defined( _DEBUG ) + +// wrapper which passes no debug info; not available in debug +void __cdecl _invalid_parameter_noinfo(void) +{ + Assert(0); +} + +#endif /* defined( _DEBUG ) */ + +#if defined( _DEBUG ) || defined( USE_MEM_DEBUG ) + +int __cdecl __crtMessageWindowW( int nRptType, const wchar_t * szFile, const wchar_t * szLine, + const wchar_t * szModule, const wchar_t * szUserMessage ) +{ + Assert(0); + return 0; +} + +int __cdecl _CrtDbgReportV( int nRptType, const wchar_t *szFile, int nLine, + const wchar_t *szModule, const wchar_t *szFormat, va_list arglist ) +{ + Assert(0); + return 0; +} + +int __cdecl _CrtDbgReportW( int nRptType, const wchar_t *szFile, int nLine, + const wchar_t *szModule, const wchar_t *szFormat, ...) +{ + Assert(0); + return 0; +} + +int __cdecl _VCrtDbgReportA( int nRptType, const wchar_t * szFile, int nLine, + const wchar_t * szModule, const wchar_t * szFormat, va_list arglist ) +{ + Assert(0); + return 0; +} + +int __cdecl _CrtSetReportHook2( int mode, _CRT_REPORT_HOOK pfnNewHook ) +{ + _CrtSetReportHook( pfnNewHook ); + return 0; +} + + +#endif /* defined( _DEBUG ) || defined( USE_MEM_DEBUG ) */ + +extern "C" int __crtDebugCheckCount = FALSE; + +extern "C" int __cdecl _CrtSetCheckCount( int fCheckCount ) +{ + int oldCheckCount = __crtDebugCheckCount; + return oldCheckCount; +} + +extern "C" int __cdecl _CrtGetCheckCount( void ) +{ + return __crtDebugCheckCount; +} + +// aligned offset debug +extern "C" void * __cdecl _aligned_offset_recalloc_dbg( void * memblock, size_t count, size_t size, size_t align, size_t offset, const char * f_name, int line_n ) +{ + Assert( IsPC() || 0 ); + void *pMem = ReallocUnattributed( memblock, size * count ); + memset( pMem, 0, size * count ); + return pMem; +} + +extern "C" void * __cdecl _aligned_recalloc_dbg( void *memblock, size_t count, size_t size, size_t align, const char * f_name, int line_n ) +{ + return _aligned_offset_recalloc_dbg(memblock, count, size, align, 0, f_name, line_n); +} + +extern "C" void * __cdecl _recalloc_dbg ( void * memblock, size_t count, size_t size, int nBlockUse, const char * szFileName, int nLine ) +{ + return _aligned_offset_recalloc_dbg(memblock, count, size, 0, 0, szFileName, nLine); +} + +_CRT_REPORT_HOOK __cdecl _CrtGetReportHook( void ) +{ + return NULL; +} + +#endif +int __cdecl _CrtReportBlockType(const void * pUserData) +{ + return 0; +} + + +} // end extern "C" +#endif // _WIN32 + +// Most files include this file, so when it's used it adds an extra .ValveDbg section, +// to help identify debug binaries. +#ifdef _WIN32 + #ifndef NDEBUG // _DEBUG + #pragma data_seg("ValveDBG") + volatile const char* DBG = "*** DEBUG STUB ***"; + #endif +#endif + +#endif + +// Extras added prevent dbgheap.obj from being included - DAL +#ifdef _WIN32 + +extern "C" +{ +size_t __crtDebugFillThreshold = 0; + +extern "C" void * __cdecl _heap_alloc_base (size_t size) +{ + Assert(0); + return NULL; +} + + +void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFileName, int nLine) +{ + return _heap_alloc(nSize); +} + +// 64-bit +#ifdef _WIN64 +static void * __cdecl realloc_help( void * pUserData, size_t * pnNewSize, int nBlockUse,const char * szFileName, + int nLine, int fRealloc ) +{ + Assert(0); // Shouldn't be needed + return NULL; +} +#else +static void * __cdecl realloc_help( void * pUserData, size_t nNewSize, int nBlockUse, const char * szFileName, + int nLine, int fRealloc) +{ + Assert(0); // Shouldn't be needed + return NULL; +} +#endif + +void __cdecl _free_nolock( void * pUserData) +{ + // I don't think the second param is used in memoverride + _free_dbg(pUserData, 0); +} + +void __cdecl _free_dbg_nolock( void * pUserData, int nBlockUse) +{ + _free_dbg(pUserData, 0); +} + +_CRT_ALLOC_HOOK __cdecl _CrtGetAllocHook ( void) +{ + Assert(0); + return NULL; +} + +static int __cdecl CheckBytes( unsigned char * pb, unsigned char bCheck, size_t nSize) +{ + int bOkay = TRUE; + return bOkay; +} + + +_CRT_DUMP_CLIENT __cdecl _CrtGetDumpClient ( void) +{ + Assert(0); + return NULL; +} + +#if _MSC_VER >= 1400 +static void __cdecl _printMemBlockData( _locale_t plocinfo, _CrtMemBlockHeader * pHead) +{ +} + +static void __cdecl _CrtMemDumpAllObjectsSince_stat( const _CrtMemState * state, _locale_t plocinfo) +{ +} +#endif +void * __cdecl _aligned_malloc_dbg( size_t size, size_t align, const char * f_name, int line_n) +{ + return _aligned_malloc(size, align); +} + +void * __cdecl _aligned_realloc_dbg( void *memblock, size_t size, size_t align, + const char * f_name, int line_n) +{ + return _aligned_realloc(memblock, size, align); +} + +void * __cdecl _aligned_offset_malloc_dbg( size_t size, size_t align, size_t offset, + const char * f_name, int line_n) +{ + return _aligned_offset_malloc(size, align, offset); +} + +void * __cdecl _aligned_offset_realloc_dbg( void * memblock, size_t size, size_t align, + size_t offset, const char * f_name, int line_n) +{ + return _aligned_offset_realloc(memblock, size, align, offset); +} + +void __cdecl _aligned_free_dbg( void * memblock) +{ + _aligned_free(memblock); +} + +size_t __cdecl _CrtSetDebugFillThreshold( size_t _NewDebugFillThreshold) +{ + Assert(0); + return 0; +} + +//=========================================== +// NEW!!! 64-bit + +char * __cdecl _strdup ( const char * string ) +{ + int nSize = strlen(string) + 1; + char *pCopy = (char*)AllocUnattributed( nSize ); + if ( pCopy ) + memcpy( pCopy, string, nSize ); + return pCopy; +} + +#if 0 +_TSCHAR * __cdecl _tfullpath_dbg ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tfullpath ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tgetdcwd_lk_dbg ( int drive, _TSCHAR *pnbuf, int maxlen, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return NULL; +} + +_TSCHAR * __cdecl _tgetdcwd_nolock ( int drive, _TSCHAR *pnbuf, int maxlen ) +{ + Assert(0); + return NULL; +} + +errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname ) +{ + Assert(0); + return 0; +} + +_TSCHAR * __cdecl _ttempnam_dbg ( const _TSCHAR *dir, const _TSCHAR *pfx, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +_TSCHAR * __cdecl _ttempnam ( const _TSCHAR *dir, const _TSCHAR *pfx ) +{ + Assert(0); + return 0; +} +#endif + +wchar_t * __cdecl _wcsdup_dbg ( const wchar_t * string, int nBlockUse, const char * szFileName, int nLine ) +{ + Assert(0); + return 0; +} + +wchar_t * __cdecl _wcsdup ( const wchar_t * string ) +{ + Assert(0); + return 0; +} + +} // end extern "C" + +#if _MSC_VER >= 1400 + +//----------------------------------------------------------------------------- +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +#if defined( _X360 ) +#if defined( _DEBUG ) || defined( USE_MEM_DEBUG ) +#include "utlmap.h" + +MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem ); + +CThreadFastMutex g_XMemAllocMutex; + +void XMemAlloc_RegisterAllocation( void *p, DWORD dwAllocAttributes ) +{ + if ( !g_pMemAlloc ) + { + // core xallocs cannot be journaled until system is ready + return; + } + + AUTO_LOCK_FM( g_XMemAllocMutex ); + int size = XMemSize( p, dwAllocAttributes ); + MemAlloc_RegisterExternalAllocation( XMem, p, size ); +} + +void XMemAlloc_RegisterDeallocation( void *p, DWORD dwAllocAttributes ) +{ + if ( !g_pMemAlloc ) + { + // core xallocs cannot be journaled until system is ready + return; + } + + AUTO_LOCK_FM( g_XMemAllocMutex ); + int size = XMemSize( p, dwAllocAttributes ); + MemAlloc_RegisterExternalDeallocation( XMem, p, size ); +} + +#else + +#define XMemAlloc_RegisterAllocation( p, a ) ((void)0) +#define XMemAlloc_RegisterDeallocation( p, a ) ((void)0) + +#endif + +//----------------------------------------------------------------------------- +// XMemAlloc +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +LPVOID WINAPI XMemAlloc( SIZE_T dwSize, DWORD dwAllocAttributes ) +{ + LPVOID ptr; + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + MEM_ALLOC_CREDIT(); + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + ptr = MemAlloc_Alloc( dwSize ); +#else + ptr = MemAlloc_Alloc(dwSize, g_pszModule, 0); +#endif + break; + case XALLOC_ALIGNMENT_8: + ptr = MemAlloc_AllocAligned( dwSize, 8 ); + break; + case XALLOC_ALIGNMENT_DEFAULT: + case XALLOC_ALIGNMENT_16: + default: + ptr = MemAlloc_AllocAligned( dwSize, 16 ); + break; + } + if ( pAttribs->dwZeroInitialize != 0 ) + { + memset( ptr, 0, XMemSize( ptr, dwAllocAttributes ) ); + } + return ptr; + } + + ptr = XMemAllocDefault( dwSize, dwAllocAttributes ); + if ( ptr ) + { + XMemAlloc_RegisterAllocation( ptr, dwAllocAttributes ); + } + + return ptr; +} + +//----------------------------------------------------------------------------- +// XMemFree +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +VOID WINAPI XMemFree( PVOID pAddress, DWORD dwAllocAttributes ) +{ + if ( !pAddress ) + { + return; + } + + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: +#if !defined(USE_LIGHT_MEM_DEBUG) && !defined(USE_MEM_DEBUG) + return g_pMemAlloc->Free( pAddress ); +#else + return g_pMemAlloc->Free( pAddress, g_pszModule, 0 ); +#endif + default: + return MemAlloc_FreeAligned( pAddress ); + } + return; + } + + XMemAlloc_RegisterDeallocation( pAddress, dwAllocAttributes ); + + XMemFreeDefault( pAddress, dwAllocAttributes ); +} + +//----------------------------------------------------------------------------- +// XMemSize +// +// XBox Memory Allocator Override +//----------------------------------------------------------------------------- +SIZE_T WINAPI XMemSize( PVOID pAddress, DWORD dwAllocAttributes ) +{ + XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes; + bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL ); + + if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI ) + { + switch ( pAttribs->dwAlignment ) + { + case XALLOC_ALIGNMENT_4: + return g_pMemAlloc->GetSize( pAddress ); + default: + return MemAlloc_GetSizeAligned( pAddress ); + } + } + + return XMemSizeDefault( pAddress, dwAllocAttributes ); +} +#endif // _X360 + +#define MAX_LANG_LEN 64 /* max language name length */ +#define MAX_CTRY_LEN 64 /* max country name length */ +#define MAX_MODIFIER_LEN 0 /* max modifier name length - n/a */ +#define MAX_LC_LEN (MAX_LANG_LEN+MAX_CTRY_LEN+MAX_MODIFIER_LEN+3) + +struct _is_ctype_compatible { + unsigned long id; + int is_clike; +}; +typedef struct setloc_struct { + /* getqloc static variables */ + char *pchLanguage; + char *pchCountry; + int iLcidState; + int iPrimaryLen; + BOOL bAbbrevLanguage; + BOOL bAbbrevCountry; + LCID lcidLanguage; + LCID lcidCountry; + /* expand_locale static variables */ + LC_ID _cacheid; + UINT _cachecp; + char _cachein[MAX_LC_LEN]; + char _cacheout[MAX_LC_LEN]; + /* _setlocale_set_cat (LC_CTYPE) static variable */ + struct _is_ctype_compatible _Lcid_c[5]; +} _setloc_struct, *_psetloc_struct; + +struct _tiddata { + unsigned long _tid; /* thread ID */ + + + uintptr_t _thandle; /* thread handle */ + + int _terrno; /* errno value */ + unsigned long _tdoserrno; /* _doserrno value */ + unsigned int _fpds; /* Floating Point data segment */ + unsigned long _holdrand; /* rand() seed value */ + char * _token; /* ptr to strtok() token */ + wchar_t * _wtoken; /* ptr to wcstok() token */ + unsigned char * _mtoken; /* ptr to _mbstok() token */ + + /* following pointers get malloc'd at runtime */ + char * _errmsg; /* ptr to strerror()/_strerror() buff */ + wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */ + char * _namebuf0; /* ptr to tmpnam() buffer */ + wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */ + char * _namebuf1; /* ptr to tmpfile() buffer */ + wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */ + char * _asctimebuf; /* ptr to asctime() buffer */ + wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */ + void * _gmtimebuf; /* ptr to gmtime() structure */ + char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */ + unsigned char _con_ch_buf[MB_LEN_MAX]; + /* ptr to putch() buffer */ + unsigned short _ch_buf_used; /* if the _con_ch_buf is used */ + + /* following fields are needed by _beginthread code */ + void * _initaddr; /* initial user thread address */ + void * _initarg; /* initial user thread argument */ + + /* following three fields are needed to support signal handling and + * runtime errors */ + void * _pxcptacttab; /* ptr to exception-action table */ + void * _tpxcptinfoptrs; /* ptr to exception info pointers */ + int _tfpecode; /* float point exception code */ + + /* pointer to the copy of the multibyte character information used by + * the thread */ + pthreadmbcinfo ptmbcinfo; + + /* pointer to the copy of the locale informaton used by the thead */ + pthreadlocinfo ptlocinfo; + int _ownlocale; /* if 1, this thread owns its own locale */ + + /* following field is needed by NLG routines */ + unsigned long _NLG_dwCode; + + /* + * Per-Thread data needed by C++ Exception Handling + */ + void * _terminate; /* terminate() routine */ + void * _unexpected; /* unexpected() routine */ + void * _translator; /* S.E. translator */ + void * _purecall; /* called when pure virtual happens */ + void * _curexception; /* current exception */ + void * _curcontext; /* current exception context */ + int _ProcessingThrow; /* for uncaught_exception */ + void * _curexcspec; /* for handling exceptions thrown from std::unexpected */ +#if defined (_M_IA64) || defined (_M_AMD64) + void * _pExitContext; + void * _pUnwindContext; + void * _pFrameInfoChain; + unsigned __int64 _ImageBase; +#if defined (_M_IA64) + unsigned __int64 _TargetGp; +#endif /* defined (_M_IA64) */ + unsigned __int64 _ThrowImageBase; + void * _pForeignException; +#elif defined (_M_IX86) + void * _pFrameInfoChain; +#endif /* defined (_M_IX86) */ + _setloc_struct _setloc_data; + + void * _encode_ptr; /* EncodePointer() routine */ + void * _decode_ptr; /* DecodePointer() routine */ + + void * _reserved1; /* nothing */ + void * _reserved2; /* nothing */ + void * _reserved3; /* nothing */ + + int _cxxReThrow; /* Set to True if it's a rethrown C++ Exception */ + + unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed function */ +}; + +typedef struct _tiddata * _ptiddata; + +class _LocaleUpdate +{ + _locale_tstruct localeinfo; + _ptiddata ptd; + bool updated; + public: + _LocaleUpdate(_locale_t plocinfo) + : updated(false) + { + /* + if (plocinfo == NULL) + { + ptd = _getptd(); + localeinfo.locinfo = ptd->ptlocinfo; + localeinfo.mbcinfo = ptd->ptmbcinfo; + + __UPDATE_LOCALE(ptd, localeinfo.locinfo); + __UPDATE_MBCP(ptd, localeinfo.mbcinfo); + if (!(ptd->_ownlocale & _PER_THREAD_LOCALE_BIT)) + { + ptd->_ownlocale |= _PER_THREAD_LOCALE_BIT; + updated = true; + } + } + else + { + localeinfo=*plocinfo; + } + */ + } + ~_LocaleUpdate() + { +// if (updated) +// ptd->_ownlocale = ptd->_ownlocale & ~_PER_THREAD_LOCALE_BIT; + } + _locale_t GetLocaleT() + { + return &localeinfo; + } +}; + + +#pragma warning(push) +#pragma warning(disable: 4483) +#if _MSC_FULL_VER >= 140050415 +#define _NATIVE_STARTUP_NAMESPACE __identifier("") +#else /* _MSC_FULL_VER >= 140050415 */ +#define _NATIVE_STARTUP_NAMESPACE __CrtImplementationDetails +#endif /* _MSC_FULL_VER >= 140050415 */ + +namespace _NATIVE_STARTUP_NAMESPACE +{ + class NativeDll + { + private: + static const unsigned int ProcessDetach = 0; + static const unsigned int ProcessAttach = 1; + static const unsigned int ThreadAttach = 2; + static const unsigned int ThreadDetach = 3; + static const unsigned int ProcessVerifier = 4; + + public: + + inline static bool IsInDllMain() + { + return false; + } + + inline static bool IsInProcessAttach() + { + return false; + } + + inline static bool IsInProcessDetach() + { + return false; + } + + inline static bool IsInVcclrit() + { + return false; + } + + inline static bool IsSafeForManagedCode() + { + if (!IsInDllMain()) + { + return true; + } + + if (IsInVcclrit()) + { + return true; + } + + return !IsInProcessAttach() && !IsInProcessDetach(); + } + }; +} +#pragma warning(pop) + +#endif // _MSC_VER >= 1400 + +#endif // !STEAM && !NO_MALLOC_OVERRIDE + +#endif // _WIN32 diff --git a/public/tier0/minidump.h b/public/tier0/minidump.h new file mode 100644 index 0000000..8f4b7b0 --- /dev/null +++ b/public/tier0/minidump.h @@ -0,0 +1,82 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MINIDUMP_H +#define MINIDUMP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +// writes out a minidump of the current stack trace with a unique filename +PLATFORM_INTERFACE void WriteMiniDump(); + +typedef void (*FnWMain)( int , tchar *[] ); + +#ifdef IS_WINDOWS_PC + +// calls the passed in function pointer and catches any exceptions/crashes thrown by it, and writes a minidump +// use from wmain() to protect the whole program + +PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] ); + +// The ExceptionInfo_t struct is a typeless data struct +// which is OS-dependent and should never be used by external code. +// Just pass it back into MinidumpSetUnhandledExceptionFunction +struct ExceptionInfo_t; + + +// Replaces the current function pointer with the one passed in. +// Returns the previously-set function. +// The function is called internally by WriteMiniDump() and CatchAndWriteMiniDump() +// The default is the built-in function that uses DbgHlp.dll's MiniDumpWriteDump function +typedef void (*FnMiniDump)( unsigned int uStructuredExceptionCode, ExceptionInfo_t * pExceptionInfo ); +PLATFORM_INTERFACE FnMiniDump SetMiniDumpFunction( FnMiniDump pfn ); + +// Use this to write a minidump explicitly. +// Some of the tools choose to catch the minidump themselves instead of using CatchAndWriteMinidump +// so they can show their own dialog. +// +// ptchMinidumpFileNameBuffer if not-NULL should be a writable tchar buffer of length at +// least _MAX_PATH and on return will contain the name of the minidump file written. +// If ptchMinidumpFileNameBuffer is NULL the name of the minidump file written will not +// be available after the function returns. +// + + +// NOTE: Matches windows.h +enum MinidumpType_t +{ + MINIDUMP_Normal = 0x00000000, + MINIDUMP_WithDataSegs = 0x00000001, + MINIDUMP_WithFullMemory = 0x00000002, + MINIDUMP_WithHandleData = 0x00000004, + MINIDUMP_FilterMemory = 0x00000008, + MINIDUMP_ScanMemory = 0x00000010, + MINIDUMP_WithUnloadedModules = 0x00000020, + MINIDUMP_WithIndirectlyReferencedMemory = 0x00000040, + MINIDUMP_FilterModulePaths = 0x00000080, + MINIDUMP_WithProcessThreadData = 0x00000100, + MINIDUMP_WithPrivateReadWriteMemory = 0x00000200, + MINIDUMP_WithoutOptionalData = 0x00000400, + MINIDUMP_WithFullMemoryInfo = 0x00000800, + MINIDUMP_WithThreadInfo = 0x00001000, + MINIDUMP_WithCodeSegs = 0x00002000 +}; + +PLATFORM_INTERFACE bool WriteMiniDumpUsingExceptionInfo( + unsigned int uStructuredExceptionCode, + ExceptionInfo_t *pExceptionInfo, + uint32 nMinidumpTypeFlags, // OR-ed together MinidumpType_t flags + tchar *ptchMinidumpFileNameBuffer = NULL + ); + +PLATFORM_INTERFACE void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn ); + +#endif + +#endif // MINIDUMP_H diff --git a/public/tier0/miniprofiler.h b/public/tier0/miniprofiler.h new file mode 100644 index 0000000..ee8c568 --- /dev/null +++ b/public/tier0/miniprofiler.h @@ -0,0 +1,256 @@ +//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============ +// This will contain things like helper functions to measure tick count of small pieces +// of code precisely; or measure performance counters (L2 misses, mispredictions etc.) +// on different hardware. +//===================================================================================== + +// this class defines a section of code to measure + +#ifndef L4D_TIER0_MINIPROFILER_HDR +#define L4D_TIER0_MINIPROFILER_HDR + +#include + +#ifdef IS_WINDOWS_PC +#include // get __rdtsc +#endif + +#if !defined(_CERT) && ( defined( WIN32 ) || defined( _X360 ) ) // && !defined(_LINUX) +#define ENABLE_HARDWARE_PROFILER 1 +#else +#define ENABLE_HARDWARE_PROFILER 0 +#endif + +#if ENABLE_HARDWARE_PROFILER + +#ifdef _LINUX +inline int GetHardwareClockFast( void ) +{ + unsigned long long int nRet; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (nRet)); + return ( int ) nRet; +} + +#else +inline int GetHardwareClockFast() +{ +#ifdef _X360 + return __mftb32(); +#else + return __rdtsc(); +#endif +} +#endif +#endif + +class CMiniProfiler; +class CLinkedMiniProfiler; + +#ifdef TIER0_DLL_EXPORT +extern "C" +{ + DLL_EXPORT CMiniProfiler *g_pLastMiniProfiler; + DLL_EXPORT uint32 g_nMiniProfilerFrame; +} +#else +DLL_IMPORT void PublishAll(CLinkedMiniProfiler*pList, uint32 nHistoryMax); +#if ENABLE_HARDWARE_PROFILER +DLL_IMPORT CMiniProfiler *g_pLastMiniProfiler; +#endif +#endif + + +class CMiniProfilerSample +{ +#if ENABLE_HARDWARE_PROFILER + int m_nTimeBaseBegin; // time base is kept here instead of using -= and += for better reliability and to avoid a cache miss at the beginning of the profiled section +#endif + +public: + CMiniProfilerSample() + { +#if ENABLE_HARDWARE_PROFILER + m_nTimeBaseBegin = GetHardwareClockFast(); +#endif + } + + int GetElapsed()const + { +#if ENABLE_HARDWARE_PROFILER + return GetHardwareClockFast() - m_nTimeBaseBegin; +#else + return 0; +#endif + } +}; + + + +class CMiniProfiler +{ +public: +#if ENABLE_HARDWARE_PROFILER + // numTicks is + uint32 m_numTimeBaseTicks; // this will be totally screwed between Begin() and End() + uint32 m_numCalls; + uint32 m_numTimeBaseTicksInCallees; // this is the time to subtract from m_numTimeBaseTicks to get the "exclusive" time in this block +#endif +public: + CMiniProfiler() + { +#if ENABLE_HARDWARE_PROFILER + Reset(); +#endif + } + + + void Begin() + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicks -= GetHardwareClockFast(); +#endif + } + + void End() + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicks += GetHardwareClockFast(); + m_numCalls ++; +#endif + } + + + void Add(int numTimeBaseTicks, int numCalls = 1) + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicks += numTimeBaseTicks; + m_numCalls += numCalls; +#endif + } + + void Add(const CMiniProfilerSample &sample) + { + Add(sample.GetElapsed()); + } + + void SubCallee(int numTimeBaseTicks) + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicksInCallees += numTimeBaseTicks; +#endif + } + + + void Reset() + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicks = 0; + m_numCalls = 0; + m_numTimeBaseTicksInCallees = 0; +#endif + } + + void Damp(int shift = 1) + { +#if ENABLE_HARDWARE_PROFILER + m_numTimeBaseTicks >>= shift; + m_numCalls >>= shift; +#endif + } + + DLL_CLASS_EXPORT void Publish(const char *szMessage, ...); +}; + + + +class CMiniProfilerGuard: public CMiniProfilerSample +{ +#if ENABLE_HARDWARE_PROFILER + CMiniProfiler *m_pProfiler, *m_pLastProfiler; + int m_numCallsToAdd; +#endif +public: + CMiniProfilerGuard(CMiniProfiler *pProfiler, int numCallsToAdd = 1) + { +#if ENABLE_HARDWARE_PROFILER + PREFETCH_CACHE_LINE(pProfiler,0); + m_numCallsToAdd = numCallsToAdd; + m_pProfiler = pProfiler; + m_pLastProfiler = g_pLastMiniProfiler; + g_pLastMiniProfiler = pProfiler; +#endif + } + ~CMiniProfilerGuard() + { +#if ENABLE_HARDWARE_PROFILER + int nElapsed = GetElapsed(); + m_pProfiler->Add(nElapsed, m_numCallsToAdd); + m_pLastProfiler->SubCallee(nElapsed); + g_pLastMiniProfiler = m_pLastProfiler; +#endif + } +}; + +class CMiniProfilerAntiGuard: public CMiniProfilerSample +{ +#if ENABLE_HARDWARE_PROFILER + CMiniProfiler *m_pProfiler; +#endif +public: + CMiniProfilerAntiGuard(CMiniProfiler *pProfiler) + { +#if ENABLE_HARDWARE_PROFILER + m_pProfiler = pProfiler; +#endif + } + ~CMiniProfilerAntiGuard() + { +#if ENABLE_HARDWARE_PROFILER + m_pProfiler->Add(-GetElapsed()); +#endif + } +}; + + + +class CLinkedMiniProfiler: public CMiniProfiler +{ +public: +#if ENABLE_HARDWARE_PROFILER + CLinkedMiniProfiler *m_pNext, **m_ppPrev; + const char *m_szName; + uint m_nHistoryMax, m_nHistoryLength; + CMiniProfiler *m_pHistory; + uint32 m_nFrameHistoryBegins; +#endif +public: + CLinkedMiniProfiler(const char *szName, CLinkedMiniProfiler**ppList) + { +#if ENABLE_HARDWARE_PROFILER + m_nFrameHistoryBegins = 0; + m_pNext = *ppList; + m_ppPrev = ppList; + *ppList = this; + m_szName = szName; + m_pHistory = NULL; + m_nHistoryLength = 0; + m_nHistoryMax = 0; +#endif + } + + + ~CLinkedMiniProfiler() + { +#if ENABLE_HARDWARE_PROFILER + // We need to remove miniprofiler from the list properly. This is an issue because we unload DLLs sometimes. + *m_ppPrev = m_pNext; // that's it: we just remove this object from the list by linking previous object with the next + if(m_pNext) + m_pNext->m_ppPrev = m_ppPrev; +#endif + } + + void Publish(uint nHistoryMax); + void PurgeHistory(); +}; + +#endif diff --git a/public/tier0/platform.h b/public/tier0/platform.h new file mode 100644 index 0000000..5055e31 --- /dev/null +++ b/public/tier0/platform.h @@ -0,0 +1,1604 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifdef COMPILER_MSVC +#pragma once +#endif + +#if defined( _X360 ) +#define NO_STEAM +#define NO_VOICE +// for the 360, the ppc platform and the rtos are tightly coupled +// setup the 360 environment here !once! for much less leaf module include wackiness +// these are critical order and purposely appear *before* anything else +#define _XBOX +#include +#include +#include +#include +#include +#include +#undef _XBOX + +#endif + +#include "wchartypes.h" +#include "tier0/valve_off.h" +#include +#include +#include +#include +#include + +#ifdef COMPILER_GCC +#include +#else +#include +#endif + + +//----------------------------------------------------------------------------- +// Old-school defines we don't want to use moving forward +//----------------------------------------------------------------------------- +#if CROSS_PLATFORM_VERSION < 1 + +// feature enables +#define NEW_SOFTWARE_LIGHTING +#if !defined( _X360 ) +#define SUPPORT_PACKED_STORE +#endif +#define BINK_ENABLED_FOR_X360 + + + +// Deprecating, infavor of IsX360() which will revert to IsXbox() +// after confidence of xbox 1 code flush +#define IsXbox() false + +// C functions for external declarations that call the appropriate C++ methods +#ifndef EXPORT + #ifdef _WIN32 + #define EXPORT _declspec( dllexport ) + #else + #define EXPORT /* */ + #endif +#endif + +#ifdef PLATFORM_POSIX +typedef unsigned int DWORD; +typedef unsigned short WORD; +typedef void * HINSTANCE; +#define _MAX_PATH PATH_MAX +#endif + +#endif // CROSS_PLATFORM_VERSION < 1 + + +//----------------------------------------------------------------------------- +// NOTE: All compiler defines are set up in the base VPC scripts +// COMPILER_MSVC, COMPILER_MSVC32, COMPILER_MSVC64, COMPILER_MSVCX360 +// COMPILER_GCC +// The rationale for this is that we need COMPILER_MSVC for the pragma blocks +// #pragma once that occur at the top of all header files, therefore we can't +// place the defines for these in here. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Set up platform defines. +//----------------------------------------------------------------------------- +#ifdef _WIN32 + #define IsPlatformLinux() false + #define IsPlatformPosix() false + #define IsPlatformOSX() false + #define IsPlatformPS3() false + #define IsPlatformWindows() true + #define PLATFORM_WINDOWS 1 + + #ifndef _X360 + #define IsPlatformX360() false + #define IsPlatformWindowsPC() true + #define PLATFORM_WINDOWS_PC 1 + + #ifdef _WIN64 + #define IsPlatformWindowsPC64() true + #define IsPlatformWindowsPC32() false + #define PLATFORM_WINDOWS_PC64 1 + #else + #define IsPlatformWindowsPC64() false + #define IsPlatformWindowsPC32() true + #define PLATFORM_WINDOWS_PC32 1 + #endif + + #else // _X360 + + #define IsPlatformWindowsPC() false + #define IsPlatformWindowsPC64() false + #define IsPlatformWindowsPC32() false + #define IsPlatformX360() true + #define PLATFORM_X360 1 + + #endif // _X360 + +#elif defined(POSIX) + #define IsPlatformX360() false + #define IsPlatformPS3() false + #define IsPlatformWindows() false + #define IsPlatformWindowsPC() false + #define IsPlatformWindowsPC64() false + #define IsPlatformWindowsPC32() false + #define IsPlatformPosix() true + #define PLATFORM_POSIX 1 + + #if defined( LINUX ) + #define IsPlatformLinux() true + #define IsPlatformOSX() false + #define PLATFORM_LINUX 1 + #elif defined ( OSX ) + #define IsPlatformLinux() false + #define IsPlatformOSX() true + #define PLATFORM_OSX 1 + #else + #define IsPlatformLinux() false + #define IsPlatformOSX() false + #endif + +#else + #error +#endif + + +//----------------------------------------------------------------------------- +// Old-school defines we're going to support since much code uses them +//----------------------------------------------------------------------------- +#if CROSS_PLATFORM_VERSION < 2 + +#define IsLinux() IsPlatformLinux() +#define IsOSX() IsPlatformOSX() +#define IsPosix() IsPlatformPosix() +#define IsX360() IsPlatformX360() +#define IsPS3() IsPlatformPS3() + +// Setup platform defines. +#ifdef COMPILER_MSVC +#define MSVC 1 +#endif + +#ifdef COMPILER_GCC +#define GNUC 1 +#endif + +#if defined( _WIN32 ) +#define _WINDOWS 1 +#endif + +#ifdef PLATFORM_WINDOWS_PC +#define IS_WINDOWS_PC 1 +#endif + +#endif // CROSS_PLATFORM_VERSION < 2 + + +//----------------------------------------------------------------------------- +// Set up platform type defines. +//----------------------------------------------------------------------------- +#ifdef PLATFORM_X360 + #ifndef _CONSOLE + #define _CONSOLE + #endif + #define IsPC() false + #define IsConsole() true +#else + #define IsPC() true + #define IsConsole() false +#endif + + + +//----------------------------------------------------------------------------- +// Set up build configuration defines. +//----------------------------------------------------------------------------- +#ifdef _CERT +#define IsCert() true +#else +#define IsCert() false +#endif + +#ifdef _DEBUG +#define IsRelease() false +#define IsDebug() true +#else +#define IsRelease() true +#define IsDebug() false +#endif + +#ifdef _RETAIL +#define IsRetail() true +#else +#define IsRetail() false +#endif + + +//----------------------------------------------------------------------------- +// Portable data types +//----------------------------------------------------------------------------- +typedef unsigned char uint8; +typedef signed char int8; + +#if defined( COMPILER_MSVC ) + + typedef __int16 int16; + typedef unsigned __int16 uint16; + typedef __int32 int32; + typedef unsigned __int32 uint32; + typedef __int64 int64; + typedef unsigned __int64 uint64; + + // intp is an integer that can accomodate a pointer + // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *) + typedef intptr_t intp; + typedef uintptr_t uintp; + + #if defined( COMPILER_MSVCX360 ) + #ifdef __m128 + #undef __m128 + #endif + #define __m128 __vector4 + #endif + +#else // !COMPILER_MSVC + + typedef short int16; + typedef unsigned short uint16; + typedef int int32; + typedef unsigned int uint32; + typedef long long int64; + typedef unsigned long long uint64; + #ifdef PLATFORM_64BITS + typedef long long intp; + typedef unsigned long long uintp; + #else + typedef int intp; + typedef unsigned int uintp; + #endif + typedef void *HWND; + +#endif // else COMPILER_MSVC + +typedef float float32; +typedef double float64; + +// for when we don't care about how many bits we use +typedef unsigned int uint; + + +// Maximum and minimum representable values +#ifndef PLATFORM_OSX + +//#define INT8_MAX SCHAR_MAX +//#define INT16_MAX SHRT_MAX +//#define INT32_MAX LONG_MAX +//#define INT64_MAX (((int64)~0) >> 1) +// +//#define INT8_MIN SCHAR_MIN +//#define INT16_MIN SHRT_MIN +//#define INT32_MIN LONG_MIN +//#define INT64_MIN (((int64)1) << 63) +// +//#define UINT8_MAX ((uint8)~0) +//#define UINT16_MAX ((uint16)~0) +//#define UINT32_MAX ((uint32)~0) +//#define UINT64_MAX ((uint64)~0) +// +//#define UINT8_MIN 0 +//#define UINT16_MIN 0 +//#define UINT32_MIN 0 +//#define UINT64_MIN 0 + +#endif // PLATFORM_OSX + +#ifndef UINT_MIN +#define UINT_MIN UINT32_MIN +#endif + +#define FLOAT32_MAX FLT_MAX +#define FLOAT64_MAX DBL_MAX + +#ifdef GNUC +#undef offsetof +//#define offsetof( type, var ) __builtin_offsetof( type, var ) +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#else +#include +#undef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + + +#define FLOAT32_MIN FLT_MIN +#define FLOAT64_MIN DBL_MIN + + // On OSX, SIGTRAP doesn't really stop the thread cold when debugging. + // So if being debugged, use INT3 which is precise. +#ifdef OSX +#define DebuggerBreak() if ( Plat_IsInDebugSession() ) __asm { int 3 } else { raise(SIGTRAP); } +#else +#endif + +//----------------------------------------------------------------------------- +// Long is evil because it's treated differently by different compilers +// Preventing its use is nasty however. This #define, which should be +// turned on in individual VPC files, causes you to include tier0/valve_off.h +// before standard C + windows headers, and include tier0/valve_on.h after +// standard C + windows headers. So, there's some painful overhead to disabling long +//----------------------------------------------------------------------------- +#ifdef DISALLOW_USE_OF_LONG + #define long long_is_the_devil_stop_using_it_use_int32_or_int64 +#endif + + +//----------------------------------------------------------------------------- +// Various compiler-specific keywords +//----------------------------------------------------------------------------- +#ifdef COMPILER_MSVC + + #ifdef FORCEINLINE + #undef FORCEINLINE + #endif + #define STDCALL __stdcall + #ifndef FASTCALL + #define FASTCALL __fastcall + #endif + #define FORCEINLINE __forceinline + #define FORCEINLINE_TEMPLATE __forceinline + #define NULLTERMINATED __nullterminated + + // This can be used to ensure the size of pointers to members when declaring + // a pointer type for a class that has only been forward declared + #define SINGLE_INHERITANCE __single_inheritance + #define MULTIPLE_INHERITANCE __multiple_inheritance + #define EXPLICIT explicit + #define NO_VTABLE __declspec( novtable ) + + // gcc doesn't allow storage specifiers on explicit template instatiation, but visual studio needs them to avoid link errors. + #define TEMPLATE_STATIC static + + // Used for dll exporting and importing + #define DLL_EXPORT extern "C" __declspec( dllexport ) + #define DLL_IMPORT extern "C" __declspec( dllimport ) + + // Can't use extern "C" when DLL exporting a class + #define DLL_CLASS_EXPORT __declspec( dllexport ) + #define DLL_CLASS_IMPORT __declspec( dllimport ) + + // Can't use extern "C" when DLL exporting a global + #define DLL_GLOBAL_EXPORT extern __declspec( dllexport ) + #define DLL_GLOBAL_IMPORT extern __declspec( dllimport ) + + // Pass hints to the compiler to prevent it from generating unnessecary / stupid code + // in certain situations. Several compilers other than MSVC also have an equivilent + // construct. + // + // Essentially the 'Hint' is that the condition specified is assumed to be true at + // that point in the compilation. If '0' is passed, then the compiler assumes that + // any subsequent code in the same 'basic block' is unreachable, and thus usually + // removed. + #define HINT(THE_HINT) __assume((THE_HINT)) + + // decls for aligning data + #define DECL_ALIGN(x) __declspec( align( x ) ) + + // GCC had a few areas where it didn't construct objects in the same order + // that Windows does. So when CVProfile::CVProfile() would access g_pMemAlloc, + // it would crash because the allocator wasn't initalized yet. + #define CONSTRUCT_EARLY + + #define SELECTANY __declspec(selectany) + + #define RESTRICT __restrict + #define RESTRICT_FUNC __declspec(restrict) + #define FMTFUNCTION( a, b ) + #define NOINLINE + +#if !defined( NO_THREAD_LOCAL ) + #define DECL_THREAD_LOCAL __declspec(thread) +#endif + + #define DISABLE_VC_WARNING( x ) __pragma(warning(disable:4310) ) + #define DEFAULT_VC_WARNING( x ) __pragma(warning(default:4310) ) + + +#elif defined ( COMPILER_GCC ) + + #if (CROSS_PLATFORM_VERSION >= 1) && !defined( PLATFORM_64BITS ) + #define STDCALL __attribute__ ((__stdcall__)) + #else + #define STDCALL + #define __stdcall __attribute__ ((__stdcall__)) + #endif + + #define FASTCALL + #ifdef _LINUX_DEBUGGABLE + #define FORCEINLINE + #else + #define FORCEINLINE inline + #endif + + // GCC 3.4.1 has a bug in supporting forced inline of templated functions + // this macro lets us not force inlining in that case + #define FORCEINLINE_TEMPLATE inline + #define SINGLE_INHERITANCE + #define MULTIPLE_INHERITANCE + #define EXPLICIT + #define NO_VTABLE + + #define NULLTERMINATED + + #define TEMPLATE_STATIC + + // Used for dll exporting and importing + #define DLL_EXPORT extern "C" __attribute__ ((visibility("default"))) + #define DLL_IMPORT extern "C" + + // Can't use extern "C" when DLL exporting a class + #define DLL_CLASS_EXPORT __attribute__ ((visibility("default"))) + #define DLL_CLASS_IMPORT + + // Can't use extern "C" when DLL exporting a global + #define DLL_GLOBAL_EXPORT __attribute__((visibility("default"))) + #define DLL_GLOBAL_IMPORT extern + + #define HINT(THE_HINT) 0 + #define DECL_ALIGN(x) __attribute__( ( aligned( x ) ) ) + #define CONSTRUCT_EARLY __attribute__((init_priority(101))) + #define SELECTANY __attribute__((weak)) + #define RESTRICT + #define RESTRICT_FUNC + #define FMTFUNCTION( fmtargnumber, firstvarargnumber ) __attribute__ (( format( printf, fmtargnumber, firstvarargnumber ))) + #define NOINLINE __attribute__ ((noinline)) + +#if !defined( NO_THREAD_LOCAL ) + #define DECL_THREAD_LOCAL __thread +#endif + + #define DISABLE_VC_WARNING( x ) + #define DEFAULT_VC_WARNING( x ) + +#else + + #define DECL_ALIGN(x) /* */ + #define SELECTANY static + +#endif + +#if defined( GNUC ) +// gnuc has the align decoration at the end +#define ALIGN4 +#define ALIGN8 +#define ALIGN16 +#define ALIGN32 +#define ALIGN128 + +#define ALIGN4_POST DECL_ALIGN(4) +#define ALIGN8_POST DECL_ALIGN(8) +#define ALIGN16_POST DECL_ALIGN(16) +#define ALIGN32_POST DECL_ALIGN(32) +#define ALIGN128_POST DECL_ALIGN(128) +#else +// MSVC has the align at the start of the struct +#define ALIGN4 DECL_ALIGN(4) +#define ALIGN8 DECL_ALIGN(8) +#define ALIGN16 DECL_ALIGN(16) +#define ALIGN32 DECL_ALIGN(32) +#define ALIGN128 DECL_ALIGN(128) + +#define ALIGN4_POST +#define ALIGN8_POST +#define ALIGN16_POST +#define ALIGN32_POST +#define ALIGN128_POST +#endif + + +// This can be used to declare an abstract (interface only) class. +// Classes marked abstract should not be instantiated. If they are, and access violation will occur. +// +// Example of use: +// +// abstract_class CFoo +// { +// ... +// } +// +// MSDN __declspec(novtable) documentation: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_langref_novtable.asp +// +// Note: NJS: This is not enabled for regular PC, due to not knowing the implications of exporting a class with no no vtable. +// It's probable that this shouldn't be an issue, but an experiment should be done to verify this. +// +#ifndef COMPILER_MSVCX360 + #define abstract_class class +#else + #define abstract_class class NO_VTABLE +#endif + + +//----------------------------------------------------------------------------- +// Why do we need this? It would be nice to make it die die die +//----------------------------------------------------------------------------- +// Alloca defined for this platform +#if defined( COMPILER_MSVC ) && !defined( WINDED ) + #if defined(_M_IX86) + #define __i386__ 1 + #endif +#endif + +#if defined __i386__ && !defined __linux__ + #define id386 1 +#else + #define id386 0 +#endif // __i386__ + + +//----------------------------------------------------------------------------- +// Disable annoying unhelpful warnings +//----------------------------------------------------------------------------- +#ifdef COMPILER_MSVC +// Remove warnings from warning level 4. +#pragma warning(disable : 4514) // warning C4514: 'acosl' : unreferenced inline function has been removed +#pragma warning(disable : 4100) // warning C4100: 'hwnd' : unreferenced formal parameter +#pragma warning(disable : 4127) // warning C4127: conditional expression is constant +#pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated +#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable +#pragma warning(disable : 4710) // warning C4710: function 'x' not inlined +#pragma warning(disable : 4702) // warning C4702: unreachable code +#pragma warning(disable : 4505) // unreferenced local function has been removed +#pragma warning(disable : 4239) // nonstandard extension used : 'argument' ( conversion from class Vector to class Vector& ) +#pragma warning(disable : 4097) // typedef-name 'BaseClass' used as synonym for class-name 'CFlexCycler::CBaseFlex' +#pragma warning(disable : 4324) // Padding was added at the end of a structure +#pragma warning(disable : 4244) // type conversion warning. +#pragma warning(disable : 4305) // truncation from 'const double ' to 'float ' +#pragma warning(disable : 4786) // Disable warnings about long symbol names +#pragma warning(disable : 4250) // 'X' : inherits 'Y::Z' via dominance +#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union + +#if _MSC_VER >= 1300 +#pragma warning(disable : 4511) // Disable warnings about private copy constructors +#pragma warning(disable : 4121) // warning C4121: 'symbol' : alignment of a member was sensitive to packing +#pragma warning(disable : 4530) // warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (disabled due to std headers having exception syntax) +#endif + +#if _MSC_VER >= 1400 +#pragma warning(disable : 4996) // functions declared deprecated +#endif + +// When we port to 64 bit, we'll have to resolve the int, ptr vs size_t 32/64 bit problems... +#if !defined( COMPILER_MSVC64 ) +#if ( CROSS_PLATFORM_VERSION < 1 ) +#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data +#pragma warning( disable : 4311 ) // pointer truncation from 'char *' to 'int' +#pragma warning( disable : 4312 ) // conversion from 'unsigned int' to 'memhandle_t' of greater size +#endif +#endif + +#endif + + +//----------------------------------------------------------------------------- +// Stack-based allocation related helpers +//----------------------------------------------------------------------------- +#if defined( COMPILER_GCC ) + + #define stackalloc( _size ) alloca( ALIGN_VALUE( _size, 16 ) ) + + #ifdef PLATFORM_OSX + #define mallocsize( _p ) ( malloc_size( _p ) ) + #else + #define mallocsize( _p ) ( malloc_usable_size( _p ) ) + #endif + +#elif defined ( COMPILER_MSVC ) + + #define stackalloc( _size ) _alloca( ALIGN_VALUE( _size, 16 ) ) + #define mallocsize( _p ) ( _msize( _p ) ) + +#endif + +#define stackfree( _p ) 0 + + +//----------------------------------------------------------------------------- +// Used to break into the debugger +//----------------------------------------------------------------------------- +#ifdef COMPILER_MSVC64 + #define DebuggerBreak() __debugbreak() +#elif COMPILER_MSVC32 + #define DebuggerBreak() __asm { int 3 } +#elif COMPILER_MSVCX360 + #define DebuggerBreak() DebugBreak() +#elif COMPILER_GCC + #if defined( PLATFORM_CYGWIN ) || defined( PLATFORM_POSIX ) + #define DebuggerBreak() __asm__( "int $0x3;") + #else + #define DebuggerBreak() asm( "int3" ) + #endif +#endif + + +//----------------------------------------------------------------------------- +// DLL export for platform utilities +//----------------------------------------------------------------------------- +#ifndef STATIC_TIER0 + +#ifdef TIER0_DLL_EXPORT +#define PLATFORM_INTERFACE DLL_EXPORT +#define PLATFORM_OVERLOAD DLL_GLOBAL_EXPORT +#define PLATFORM_CLASS DLL_CLASS_EXPORT +#else +#define PLATFORM_INTERFACE DLL_IMPORT +#define PLATFORM_OVERLOAD DLL_GLOBAL_IMPORT +#define PLATFORM_CLASS DLL_CLASS_IMPORT +#endif + +#else // BUILD_AS_DLL + +#define PLATFORM_INTERFACE extern +#define PLATFORM_OVERLOAD +#define PLATFORM_CLASS + +#endif // BUILD_AS_DLL + + +//----------------------------------------------------------------------------- +// Returns true if debugger attached, false otherwise +//----------------------------------------------------------------------------- +#if defined( PLATFORM_WINDOWS ) +PLATFORM_INTERFACE void Plat_DebugString( const tchar * ); +#else +#define Plat_DebugString(s) ((void)0) +#endif + +PLATFORM_INTERFACE bool Plat_IsInDebugSession(); + +#define DebuggerBreakIfDebugging() if ( !Plat_IsInDebugSession() ) ; else DebuggerBreak() + + +//----------------------------------------------------------------------------- +// Message Box +//----------------------------------------------------------------------------- +#if defined( PLATFORM_WINDOWS_PC ) +PLATFORM_INTERFACE void Plat_MessageBox( const char *pTitle, const tchar *pMessage ); +#else +#define Plat_MessageBox( t, m ) ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// Posix platform helpers +//----------------------------------------------------------------------------- +#ifdef PLATFORM_POSIX + +// Visual Studio likes to put an underscore in front of anything that looks like a portable function. +#define _strupr strupr +#define _getcwd getcwd +#define _open open +#define _lseek lseek +#define _read read +#define _close close +#define _vsnprintf vsnprintf +#define _stat stat +#define _O_RDONLY O_RDONLY +#define _stricmp strcasecmp +#define _finite finite +#define _unlink unlink +#define _putenv putenv +#define _chdir chdir +#define _access access + +#if !defined( _snprintf ) // some vpc's define this on the command line +#define _snprintf snprintf +#endif + +#include +#include // get unlink +#include + +#endif // PLATFORM_POSIX + + +//----------------------------------------------------------------------------- +// Generally useful platform-independent macros (move to another file?) +//----------------------------------------------------------------------------- + +// need macro for constant expression +#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) ) + +// Force a function call site -not- to inlined. (useful for profiling) +#define DONT_INLINE(a) (((int)(a)+1)?(a):(a)) + +// Marks the codepath from here until the next branch entry point as unreachable, +// and asserts if any attempt is made to execute it. +#define UNREACHABLE() { Assert(0); HINT(0); } + +// In cases where no default is present or appropriate, this causes MSVC to generate +// as little code as possible, and throw an assertion in debug. +#define NO_DEFAULT default: UNREACHABLE(); + +// Defines MAX_PATH +#ifndef MAX_PATH + #define MAX_PATH 260 +#endif + + +//----------------------------------------------------------------------------- +// FP exception handling +//----------------------------------------------------------------------------- +//#define CHECK_FLOAT_EXCEPTIONS 1 +//#define CHECK_FPU_CONTROL_WORD_SET 1 // x360 only + +#if defined( COMPILER_MSVC64 ) + + inline void SetupFPUControlWord() + { + } + +#elif defined ( COMPILER_MSVC32 ) + + inline void SetupFPUControlWordForceExceptions() + { + // use local to get and store control word + uint16 tmpCtrlW; + __asm + { + fnclex /* clear all current exceptions */ + fnstcw word ptr [tmpCtrlW] /* get current control word */ + and [tmpCtrlW], 0FCC0h /* Keep infinity control + rounding control */ + or [tmpCtrlW], 0230h /* set to 53-bit, mask only inexact, underflow */ + fldcw word ptr [tmpCtrlW] /* put new control word in FPU */ + } + } + + #ifdef CHECK_FLOAT_EXCEPTIONS + + inline void SetupFPUControlWord() + { + SetupFPUControlWordForceExceptions(); + } + + #else + + inline void SetupFPUControlWord() + { + // use local to get and store control word + uint16 tmpCtrlW; + __asm + { + fnstcw word ptr [tmpCtrlW] /* get current control word */ + and [tmpCtrlW], 0FCC0h /* Keep infinity control + rounding control */ + or [tmpCtrlW], 023Fh /* set to 53-bit, mask only inexact, underflow */ + fldcw word ptr [tmpCtrlW] /* put new control word in FPU */ + } + } + + #endif + +#elif defined ( COMPILER_GCC ) + + inline void SetupFPUControlWord() + { + __volatile unsigned short int __cw; + __asm __volatile ("fnstcw %0" : "=m" (__cw)); + __cw = __cw & 0x0FCC0; // keep infinity control, keep rounding mode + __cw = __cw | 0x023F; // set 53-bit, no exceptions + __asm __volatile ("fldcw %0" : : "m" (__cw)); + } + +#elif defined( COMPILER_MSVCX360 ) + + #ifdef CHECK_FPU_CONTROL_WORD_SET + FORCEINLINE bool IsFPUControlWordSet() + { + float f = 0.996f; + union + { + double flResult; + int pResult[2]; + }; + flResult = __fctiw( f ); + return ( pResult[1] == 1 ); + } + #else + #define IsFPUControlWordSet() true + #endif + + inline void SetupFPUControlWord() + { + // Set round-to-nearest in FPSCR + // (cannot assemble, must use op-code form) + __emit( 0xFF80010C ); // mtfsfi 7,0 + + // Favour compatibility over speed (make sure the VPU set to Java-compliant mode) + // NOTE: the VPU *always* uses round-to-nearest + __vector4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; + a; // Avoid compiler warning + __asm + { + mtvscr a; // Clear the Vector Status & Control Register to zero + } + } + +#endif // COMPILER_MSVCX360 + + +//----------------------------------------------------------------------------- +// Purpose: Standard functions for handling endian-ness +//----------------------------------------------------------------------------- + +//------------------------------------- +// Basic swaps +//------------------------------------- + +template +inline T WordSwapC( T w ) +{ + uint16 temp; + + temp = ((*((uint16 *)&w) & 0xff00) >> 8); + temp |= ((*((uint16 *)&w) & 0x00ff) << 8); + + return *((T*)&temp); +} + +template +inline T DWordSwapC( T dw ) +{ + uint32 temp; + + temp = *((uint32 *)&dw) >> 24; + temp |= ((*((uint32 *)&dw) & 0x00FF0000) >> 8); + temp |= ((*((uint32 *)&dw) & 0x0000FF00) << 8); + temp |= ((*((uint32 *)&dw) & 0x000000FF) << 24); + + return *((T*)&temp); +} + +//------------------------------------- +// Fast swaps +//------------------------------------- + +#if defined( COMPILER_MSVCX360 ) + + #define WordSwap WordSwap360Intr + #define DWordSwap DWordSwap360Intr + + template + inline T WordSwap360Intr( T w ) + { + T output; + __storeshortbytereverse( w, 0, &output ); + return output; + } + + template + inline T DWordSwap360Intr( T dw ) + { + T output; + __storewordbytereverse( dw, 0, &output ); + return output; + } + +#elif defined( COMPILER_MSVC32 ) + + #define WordSwap WordSwapAsm + #define DWordSwap DWordSwapAsm + + #pragma warning(push) + #pragma warning (disable:4035) // no return value + + template + inline T WordSwapAsm( T w ) + { + __asm + { + mov ax, w + xchg al, ah + } + } + + template + inline T DWordSwapAsm( T dw ) + { + __asm + { + mov eax, dw + bswap eax + } + } + + #pragma warning(pop) + +#else + + #define WordSwap WordSwapC + #define DWordSwap DWordSwapC + +#endif + +//------------------------------------- +// The typically used methods. +//------------------------------------- + +#if defined( _SGI_SOURCE ) || defined( PLATFORM_X360 ) +#define PLAT_BIG_ENDIAN 1 +#else +#define PLAT_LITTLE_ENDIAN 1 +#endif + + +// If a swapped float passes through the fpu, the bytes may get changed. +// Prevent this by swapping floats as DWORDs. +#define SafeSwapFloat( pOut, pIn ) (*((uint*)pOut) = DWordSwap( *((uint*)pIn) )) + +#if defined(PLAT_LITTLE_ENDIAN) +#define BigShort( val ) WordSwap( val ) +#define BigWord( val ) WordSwap( val ) +#define BigLong( val ) DWordSwap( val ) +#define BigDWord( val ) DWordSwap( val ) +#define LittleShort( val ) ( val ) +#define LittleWord( val ) ( val ) +#define LittleLong( val ) ( val ) +#define LittleDWord( val ) ( val ) +#define SwapShort( val ) BigShort( val ) +#define SwapWord( val ) BigWord( val ) +#define SwapLong( val ) BigLong( val ) +#define SwapDWord( val ) BigDWord( val ) + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define LittleFloat( pOut, pIn ) ( *pOut = *pIn ) +#define SwapFloat( pOut, pIn ) BigFloat( pOut, pIn ) + +#elif defined(PLAT_BIG_ENDIAN) + +#define BigShort( val ) ( val ) +#define BigWord( val ) ( val ) +#define BigLong( val ) ( val ) +#define BigDWord( val ) ( val ) +#define LittleShort( val ) WordSwap( val ) +#define LittleWord( val ) WordSwap( val ) +#define LittleLong( val ) DWordSwap( val ) +#define LittleDWord( val ) DWordSwap( val ) +#define SwapShort( val ) LittleShort( val ) +#define SwapWord( val ) LittleWord( val ) +#define SwapLong( val ) LittleLong( val ) +#define SwapDWord( val ) LittleDWord( val ) + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) ( *pOut = *pIn ) +#define LittleFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define SwapFloat( pOut, pIn ) LittleFloat( pOut, pIn ) + +#else + +// @Note (toml 05-02-02): this technique expects the compiler to +// optimize the expression and eliminate the other path. On any new +// platform/compiler this should be tested. +inline short BigShort( short val ) { int test = 1; return ( *(char *)&test == 1 ) ? WordSwap( val ) : val; } +inline uint16 BigWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? WordSwap( val ) : val; } +inline long BigLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } +inline uint32 BigDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? DWordSwap( val ) : val; } +inline short LittleShort( short val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } +inline uint16 LittleWord( uint16 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : WordSwap( val ); } +inline long LittleLong( long val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } +inline uint32 LittleDWord( uint32 val ) { int test = 1; return ( *(char *)&test == 1 ) ? val : DWordSwap( val ); } +inline short SwapShort( short val ) { return WordSwap( val ); } +inline uint16 SwapWord( uint16 val ) { return WordSwap( val ); } +inline long SwapLong( long val ) { return DWordSwap( val ); } +inline uint32 SwapDWord( uint32 val ) { return DWordSwap( val ); } + +// Pass floats by pointer for swapping to avoid truncation in the fpu +inline void BigFloat( float *pOut, const float *pIn ) { int test = 1; ( *(char *)&test == 1 ) ? SafeSwapFloat( pOut, pIn ) : ( *pOut = *pIn ); } +inline void LittleFloat( float *pOut, const float *pIn ) { int test = 1; ( *(char *)&test == 1 ) ? ( *pOut = *pIn ) : SafeSwapFloat( pOut, pIn ); } +inline void SwapFloat( float *pOut, const float *pIn ) { SafeSwapFloat( pOut, pIn ); } + +#endif + +#if COMPILER_MSVCX360 + inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) + { + return __loadwordbytereverse( dwordIndex<<2, base ); + } + + inline void StoreLittleDWord( uint32 *base, unsigned int dwordIndex, uint32 dword ) + { + __storewordbytereverse( dword, dwordIndex<<2, base ); + } + inline uint64 LoadLittleInt64( uint64 *base, unsigned int nWordIndex ) + { + return __loaddoublewordbytereverse( nWordIndex<<2, base ); + } + + inline void StoreLittleInt64( uint64 *base, unsigned int nWordIndex, uint64 nWord ) + { + __storedoublewordbytereverse( nWord, nWordIndex<<2, base ); + } +#else + inline uint32 LoadLittleDWord( uint32 *base, unsigned int dwordIndex ) + { + return LittleDWord( base[dwordIndex] ); + } + + inline void StoreLittleDWord( uint32 *base, unsigned int dwordIndex, uint32 dword ) + { + base[dwordIndex] = LittleDWord(dword); + } +#endif + + +// When in benchmark mode, the timer returns a simple incremented value each time you call it. +// +// It should not be changed after startup unless you really know what you're doing. The only place +// that should do this is the benchmark code itself so it can output a legit duration. +PLATFORM_INTERFACE void Plat_SetBenchmarkMode( bool bBenchmarkMode ); +PLATFORM_INTERFACE bool Plat_IsInBenchmarkMode(); + + +PLATFORM_INTERFACE double Plat_FloatTime(); // Returns time in seconds since the module was loaded. +PLATFORM_INTERFACE uint32 Plat_MSTime(); // Time in milliseconds. +PLATFORM_INTERFACE uint64 Plat_GetClockStart(); // Snapshot of the clock when app started. + +// Get the local calendar time. +// Same as time() followed by localtime(), but non-crash-prone and threadsafe. +PLATFORM_INTERFACE void Plat_GetLocalTime( struct tm *pNow ); + +// Convert a time_t (specified in nTime - seconds since Jan 1, 1970 UTC) to a local calendar time in a threadsafe and non-crash-prone way. +PLATFORM_INTERFACE void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow ); + +// Get a time string (same as ascstring, but threadsafe). +PLATFORM_INTERFACE void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes ); + +// converts a time_t to a struct tm without the local time conversion of ConvertToLocalTime +PLATFORM_INTERFACE void Plat_gmtime( uint64 nTime, struct tm *pTime ); + + +// Get the process' executable filename. +PLATFORM_INTERFACE void Plat_GetModuleFilename( char *pOut, int nMaxBytes ); + +PLATFORM_INTERFACE void Plat_ExitProcess( int nCode ); + +// b/w compatibility +#define Sys_FloatTime Plat_FloatTime + +// Protect against bad auto operator= +#define DISALLOW_OPERATOR_EQUAL( _classname ) \ + private: \ + _classname &operator=( const _classname & ); \ + public: + +// Define a reasonable operator= +#define IMPLEMENT_OPERATOR_EQUAL( _classname ) \ + public: \ + _classname &operator=( const _classname &src ) \ + { \ + memcpy( this, &src, sizeof(_classname) ); \ + return *this; \ + } + +// Processor Information: +struct CPUInformation +{ + int m_Size; // Size of this structure, for forward compatability. + + bool m_bRDTSC : 1, // Is RDTSC supported? + m_bCMOV : 1, // Is CMOV supported? + m_bFCMOV : 1, // Is FCMOV supported? + m_bSSE : 1, // Is SSE supported? + m_bSSE2 : 1, // Is SSE2 Supported? + m_b3DNow : 1, // Is 3DNow! Supported? + m_bMMX : 1, // Is MMX supported? + m_bHT : 1; // Is HyperThreading supported? + + uint8 m_nLogicalProcessors; // Number op logical processors. + uint8 m_nPhysicalProcessors; // Number of physical processors + + bool m_bSSE3 : 1, + m_bSSSE3 : 1, + m_bSSE4a : 1, + m_bSSE41 : 1, + m_bSSE42 : 1; + + int64 m_Speed; // In cycles per second. + + tchar* m_szProcessorID; // Processor vendor Identification. + + CPUInformation(): m_Size(0){} +}; + +PLATFORM_INTERFACE const CPUInformation& GetCPUInformation(); + +PLATFORM_INTERFACE void GetCurrentDate( int *pDay, int *pMonth, int *pYear ); +PLATFORM_INTERFACE void GetCurrentDayOfTheWeek( int *pDay ); // 0 = Sunday +PLATFORM_INTERFACE void GetCurrentDayOfTheYear( int *pDay ); // 0 = Jan 1 + +// ---------------------------------------------------------------------------------- // +// Performance Monitoring Events - L2 stats etc... +// ---------------------------------------------------------------------------------- // +PLATFORM_INTERFACE void InitPME(); +PLATFORM_INTERFACE void ShutdownPME(); + + +//----------------------------------------------------------------------------- +// Security related functions +//----------------------------------------------------------------------------- +// Ensure that the hardware key's drivers have been installed. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKeyDriver(); + +// Ok, so this isn't a very secure way to verify the hardware key for now. It +// is primarially depending on the fact that all the binaries have been wrapped +// with the secure wrapper provided by the hardware keys vendor. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKey(); + +// The same as above, but notifies user with a message box when the key isn't in +// and gives him an opportunity to correct the situation. +PLATFORM_INTERFACE bool Plat_VerifyHardwareKeyPrompt(); + +// Can be called in real time, doesn't perform the verify every frame. Mainly just +// here to allow the game to drop out quickly when the key is removed, rather than +// allowing the wrapper to pop up it's own blocking dialog, which the engine doesn't +// like much. +PLATFORM_INTERFACE bool Plat_FastVerifyHardwareKey(); + +//----------------------------------------------------------------------------- +// Just logs file and line to simple.log +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void* Plat_SimpleLog( const tchar* file, int line ); + + +#if _X360 +#define Plat_FastMemset XMemSet +#define Plat_FastMemcpy XMemCpy +#else +#define Plat_FastMemset memset +#define Plat_FastMemcpy memcpy +#endif + +//----------------------------------------------------------------------------- +// Returns true if running on a 64 bit (windows) OS +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE bool Is64BitOS(); + + +//----------------------------------------------------------------------------- +// XBOX Components valid in PC compilation space +//----------------------------------------------------------------------------- + +#define XBOX_DVD_SECTORSIZE 2048 +#define XBOX_DVD_ECC_SIZE 32768 // driver reads in quantum ECC blocks +#define XBOX_HDD_SECTORSIZE 512 + +// Custom windows messages for Xbox input +#define WM_XREMOTECOMMAND (WM_USER + 100) +#define WM_XCONTROLLER_KEY (WM_USER + 101) +#define WM_SYS_UI (WM_USER + 102) +#define WM_SYS_SIGNINCHANGED (WM_USER + 103) +#define WM_SYS_STORAGEDEVICESCHANGED (WM_USER + 104) +#define WM_SYS_PROFILESETTINGCHANGED (WM_USER + 105) +#define WM_SYS_MUTELISTCHANGED (WM_USER + 106) +#define WM_SYS_INPUTDEVICESCHANGED (WM_USER + 107) +#define WM_SYS_INPUTDEVICECONFIGCHANGED (WM_USER + 108) +#define WM_LIVE_CONNECTIONCHANGED (WM_USER + 109) +#define WM_LIVE_INVITE_ACCEPTED (WM_USER + 110) +#define WM_LIVE_LINK_STATE_CHANGED (WM_USER + 111) +#define WM_LIVE_CONTENT_INSTALLED (WM_USER + 112) +#define WM_LIVE_MEMBERSHIP_PURCHASED (WM_USER + 113) +#define WM_LIVE_VOICECHAT_AWAY (WM_USER + 114) +#define WM_LIVE_PRESENCE_CHANGED (WM_USER + 115) +#define WM_FRIENDS_PRESENCE_CHANGED (WM_USER + 116) +#define WM_FRIENDS_FRIEND_ADDED (WM_USER + 117) +#define WM_FRIENDS_FRIEND_REMOVED (WM_USER + 118) +#define WM_CUSTOM_GAMEBANNERPRESSED (WM_USER + 119) +#define WM_CUSTOM_ACTIONPRESSED (WM_USER + 120) +#define WM_XMP_STATECHANGED (WM_USER + 121) +#define WM_XMP_PLAYBACKBEHAVIORCHANGED (WM_USER + 122) +#define WM_XMP_PLAYBACKCONTROLLERCHANGED (WM_USER + 123) + +inline const char *GetPlatformExt( void ) +{ + return IsPlatformX360() ? ".360" : ""; +} + +// flat view, 6 hw threads +#define XBOX_PROCESSOR_0 ( 1<<0 ) +#define XBOX_PROCESSOR_1 ( 1<<1 ) +#define XBOX_PROCESSOR_2 ( 1<<2 ) +#define XBOX_PROCESSOR_3 ( 1<<3 ) +#define XBOX_PROCESSOR_4 ( 1<<4 ) +#define XBOX_PROCESSOR_5 ( 1<<5 ) + +// core view, 3 cores with 2 hw threads each +#define XBOX_CORE_0_HWTHREAD_0 XBOX_PROCESSOR_0 +#define XBOX_CORE_0_HWTHREAD_1 XBOX_PROCESSOR_1 +#define XBOX_CORE_1_HWTHREAD_0 XBOX_PROCESSOR_2 +#define XBOX_CORE_1_HWTHREAD_1 XBOX_PROCESSOR_3 +#define XBOX_CORE_2_HWTHREAD_0 XBOX_PROCESSOR_4 +#define XBOX_CORE_2_HWTHREAD_1 XBOX_PROCESSOR_5 + +//----------------------------------------------------------------------------- +// Include additional dependant header components. +//----------------------------------------------------------------------------- +#if defined( PLATFORM_X360 ) +#include "xbox/xbox_core.h" +#endif + +//----------------------------------------------------------------------------- +// Methods to invoke the constructor, copy constructor, and destructor +//----------------------------------------------------------------------------- + +template +inline T* Construct( T* pMemory ) +{ + return ::new( pMemory ) T; +} + +template +inline T* Construct( T* pMemory, ARG1 a1 ) +{ + return ::new( pMemory ) T( a1 ); +} + +template +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2 ) +{ + return ::new( pMemory ) T( a1, a2 ); +} + +template +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3 ) +{ + return ::new( pMemory ) T( a1, a2, a3 ); +} + +template +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4 ) +{ + return ::new( pMemory ) T( a1, a2, a3, a4 ); +} + +template +inline T* Construct( T* pMemory, ARG1 a1, ARG2 a2, ARG3 a3, ARG4 a4, ARG5 a5 ) +{ + return ::new( pMemory ) T( a1, a2, a3, a4, a5 ); +} + +template +inline T* CopyConstruct( T* pMemory, T const& src ) +{ + return ::new( pMemory ) T(src); +} + +template +inline void Destruct( T* pMemory ) +{ + pMemory->~T(); + +#ifdef _DEBUG + memset( pMemory, 0xDD, sizeof(T) ); +#endif +} + + +// +// GET_OUTER() +// +// A platform-independent way for a contained class to get a pointer to its +// owner. If you know a class is exclusively used in the context of some +// "outer" class, this is a much more space efficient way to get at the outer +// class than having the inner class store a pointer to it. +// +// class COuter +// { +// class CInner // Note: this does not need to be a nested class to work +// { +// void PrintAddressOfOuter() +// { +// printf( "Outer is at 0x%x\n", GET_OUTER( COuter, m_Inner ) ); +// } +// }; +// +// CInner m_Inner; +// friend class CInner; +// }; + +#define GET_OUTER( OuterType, OuterMember ) \ + ( ( OuterType * ) ( (uint8 *)this - offsetof( OuterType, OuterMember ) ) ) + + +/* TEMPLATE_FUNCTION_TABLE() + + (Note added to platform.h so platforms that correctly support templated + functions can handle portions as templated functions rather than wrapped + functions) + + Helps automate the process of creating an array of function + templates that are all specialized by a single integer. + This sort of thing is often useful in optimization work. + + For example, using TEMPLATE_FUNCTION_TABLE, this: + + TEMPLATE_FUNCTION_TABLE(int, Function, ( int blah, int blah ), 10) + { + return argument * argument; + } + + is equivilent to the following: + + (NOTE: the function has to be wrapped in a class due to code + generation bugs involved with directly specializing a function + based on a constant.) + + template + class FunctionWrapper + { + public: + int Function( int blah, int blah ) + { + return argument*argument; + } + } + + typedef int (*FunctionType)( int blah, int blah ); + + class FunctionName + { + public: + enum { count = 10 }; + FunctionType functions[10]; + }; + + FunctionType FunctionName::functions[] = + { + FunctionWrapper<0>::Function, + FunctionWrapper<1>::Function, + FunctionWrapper<2>::Function, + FunctionWrapper<3>::Function, + FunctionWrapper<4>::Function, + FunctionWrapper<5>::Function, + FunctionWrapper<6>::Function, + FunctionWrapper<7>::Function, + FunctionWrapper<8>::Function, + FunctionWrapper<9>::Function + }; +*/ + +PLATFORM_INTERFACE bool vtune( bool resume ); + + +#define TEMPLATE_FUNCTION_TABLE(RETURN_TYPE, NAME, ARGS, COUNT) \ + \ +typedef RETURN_TYPE (FASTCALL *__Type_##NAME) ARGS; \ + \ +template \ +struct __Function_##NAME \ +{ \ + static RETURN_TYPE FASTCALL Run ARGS; \ +}; \ + \ +template \ +struct __MetaLooper_##NAME : __MetaLooper_##NAME \ +{ \ + __Type_##NAME func; \ + inline __MetaLooper_##NAME() { func = __Function_##NAME::Run; } \ +}; \ + \ +template<> \ +struct __MetaLooper_##NAME<0> \ +{ \ + __Type_##NAME func; \ + inline __MetaLooper_##NAME() { func = __Function_##NAME<0>::Run; } \ +}; \ + \ +class NAME \ +{ \ +private: \ + static const __MetaLooper_##NAME m; \ +public: \ + enum { count = COUNT }; \ + static const __Type_##NAME* functions; \ +}; \ +const __MetaLooper_##NAME NAME::m; \ +const __Type_##NAME* NAME::functions = (__Type_##NAME*)&m; \ +template \ +RETURN_TYPE FASTCALL __Function_##NAME::Run ARGS + + +#define LOOP_INTERCHANGE(BOOLEAN, CODE)\ + if( (BOOLEAN) )\ + {\ + CODE;\ + } else\ + {\ + CODE;\ + } + + +//----------------------------------------------------------------------------- +// Dynamic libs support +//----------------------------------------------------------------------------- +#if defined( PLATFORM_WINDOWS ) + +PLATFORM_INTERFACE void *Plat_GetProcAddress( const char *pszModule, const char *pszName ); + +template +class CDynamicFunction +{ +public: + CDynamicFunction( const char *pszModule, const char *pszName, FUNCPTR_TYPE pfnFallback = NULL ) + { + m_pfn = pfnFallback; + void *pAddr = Plat_GetProcAddress( pszModule, pszName ); + if ( pAddr ) + { + m_pfn = (FUNCPTR_TYPE)pAddr; + } + } + + operator bool() { return m_pfn != NULL; } + bool operator !() { return !m_pfn; } + operator FUNCPTR_TYPE() { return m_pfn; } + +private: + FUNCPTR_TYPE m_pfn; +}; +#endif + + +//----------------------------------------------------------------------------- +// What OS version are we? +//----------------------------------------------------------------------------- +enum PlatOSVersion_t +{ + PLAT_OS_VERSION_UNKNOWN = -1, + + // X360-specific versions + PLAT_OS_VERSION_XBOX360 = 0, + + // PC-specific OS versions + PLAT_OS_VERSION_XP = 5, + PLAT_OS_VERSION_VISTA = 6, +}; + +PLATFORM_INTERFACE PlatOSVersion_t Plat_GetOSVersion(); + + +// Watchdog timer support. Call BeginWatchdogTimer( nn ) to kick the timer off. if you don't call +// EndWatchdogTimer within nn seconds, the program will kick off an exception. This is for making +// sure that hung dedicated servers abort (and restart) instead of staying hung. Calling +// EndWatchdogTimer more than once or when there is no active watchdog is fine. Only does anything +// under linux right now. It should be possible to implement this functionality in windows via a +// thread, if desired. + +#ifdef _POSIX + +PLATFORM_INTERFACE void BeginWatchdogTimer( int nSecs ); +PLATFORM_INTERFACE void EndWatchdogTimer( void ); +PLATFORM_INTERFACE void ResetBaseTime( void ); // reset plat_floattime to 0 for a subprocess +#else +FORCEINLINE void BeginWatchdogTimer( int nSecs ) +{ +} + +FORCEINLINE void EndWatchdogTimer( void ) +{ +} +FORCEINLINE void ResetBaseTime( void ) // reset plat_floattime to 0 for a subprocess +{ +} + +#endif + + +#ifdef COMPILER_MSVC +/* +FORCEINLINE uint8 RotateBitsLeft8( uint8 nValue, int nRotateBits ) +{ + return _rotl8( nValue, nRotateBits ); +} +FORCEINLINE uint16 RotateBitsLeft16( uint16 nValue, int nRotateBits ) +{ + return _rotl( nValue, nRotateBits ); +} +FORCEINLINE uint8 RotateBitsRight8( uint8 nValue, int nRotateBits ) +{ +return _rotr8( nValue, nRotateBits ); +} +FORCEINLINE uint16 RotateBitsRight16( uint16 nValue, int nRotateBits ) +{ +return _rotr16( nValue, nRotateBits ); +} +*/ +FORCEINLINE uint32 RotateBitsLeft32( uint32 nValue, int nRotateBits ) +{ + return _rotl( nValue, nRotateBits ); +} +FORCEINLINE uint64 RotateBitsLeft64( uint64 nValue, int nRotateBits ) +{ + return _rotl64( nValue, nRotateBits ); +} +FORCEINLINE uint32 RotateBitsRight32( uint32 nValue, int nRotateBits ) +{ + return _rotr( nValue, nRotateBits ); +} +FORCEINLINE uint64 RotateBitsRight64( uint64 nValue, int nRotateBits ) +{ + return _rotr64( nValue, nRotateBits ); +} +#else +// GCC should compile this all into single instruction +/* +FORCEINLINE uint8 RotateBitsLeft8( uint8 nValue, int nRotateBits ) +{ + return ( nValue << nRotateBits ) | ( nValue >> ( ( -nRotateBits ) & 7 ) ); +} +FORCEINLINE uint16 RotateBitsLeft16( uint16 nValue, int nRotateBits ) +{ + return ( nValue << nRotateBits ) | ( nValue >> ( ( -nRotateBits ) & 15 ) ); +} +FORCEINLINE uint8 RotateBitsRight8( uint8 nValue, int nRotateBits ) +{ + return ( nValue >> nRotateBits ) | ( nValue << ( ( -nRotateBits ) & 7 ) ); +} +FORCEINLINE uint16 RotateBitsRight16( uint16 nValue, int nRotateBits ) +{ + return ( nValue >> nRotateBits ) | ( nValue << ( ( -nRotateBits ) & 15 ) ); +} +*/ +FORCEINLINE uint32 RotateBitsLeft32( uint32 nValue, int nRotateBits ) +{ + return ( nValue << nRotateBits ) | ( nValue >> ( ( -nRotateBits ) & 31 ) ); +} +FORCEINLINE uint64 RotateBitsLeft64( uint64 nValue, int nRotateBits ) +{ + return ( nValue << nRotateBits ) | ( nValue >> ( ( - nRotateBits ) & 63 ) ); +} +FORCEINLINE uint32 RotateBitsRight32( uint32 nValue, int nRotateBits ) +{ + return ( nValue >> nRotateBits ) | ( nValue << ( ( -nRotateBits ) & 31 ) ); +} +FORCEINLINE uint64 RotateBitsRight64( uint64 nValue, int nRotateBits ) +{ + return ( nValue >> nRotateBits ) | ( nValue << ( ( - nRotateBits ) & 63 ) ); +} +#endif + + +#include "tier0/valve_on.h" + +#if defined(TIER0_DLL_EXPORT) +extern int V_tier0_stricmp(const char *s1, const char *s2 ); +#define stricmp(s1,s2) V_tier0_stricmp( s1, s2 ) +#define strcmpi(s1,s2) V_tier0_stricmp( s1, s2 ) +#else +int _V_stricmp (const char *s1, const char *s2 ); +int V_strncasecmp (const char *s1, const char *s2, int n); + +// A special high-performance case-insensitive compare function that in +// a single call distinguishes between exactly matching strings, +// strings equal in case-insensitive way, and not equal strings: +// returns 0 if strings match exactly +// returns >0 if strings match in a case-insensitive way, but do not match exactly +// returns <0 if strings do not match even in a case-insensitive way +int _V_stricmp_NegativeForUnequal ( const char *s1, const char *s2 ); + +#define stricmp(s1,s2) _V_stricmp(s1, s2) +#define strcmpi(s1,s2) _V_stricmp(s1, s2) +#define strnicmp V_strncasecmp +#endif + +#endif /* PLATFORM_H */ diff --git a/public/tier0/platwindow.h b/public/tier0/platwindow.h new file mode 100644 index 0000000..0bc2f43 --- /dev/null +++ b/public/tier0/platwindow.h @@ -0,0 +1,82 @@ +//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef PLATWINDOW_H +#define PLATWINDOW_H + +#ifdef COMPILER_MSVC32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/basetypes.h" + +//----------------------------------------------------------------------------- +// Window handle +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( PlatWindow_t ); +#define PLAT_WINDOW_INVALID ( (PlatWindow_t)0 ) + + +//----------------------------------------------------------------------------- +// Window creation +//----------------------------------------------------------------------------- +enum WindowCreateFlags_t +{ + WINDOW_CREATE_FULLSCREEN = 0x1, + WINDOW_CREATE_RESIZING = 0x2, + +}; + +PLATFORM_INTERFACE PlatWindow_t Plat_CreateWindow( void *hInstance, const char *pTitle, int nWidth, int nHeight, int nFlags ); + + +//----------------------------------------------------------------------------- +// Window title +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_SetWindowTitle( PlatWindow_t hWindow, const char *pTitle ); + + +//----------------------------------------------------------------------------- +// Window movement +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_SetWindowPos( PlatWindow_t hWindow, int x, int y ); + + +//----------------------------------------------------------------------------- +// Gets the desktop resolution +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_GetDesktopResolution( int *pWidth, int *pHeight ); + + +//----------------------------------------------------------------------------- +// Gets a window size +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_GetWindowClientSize( PlatWindow_t hWindow, int *pWidth, int *pHeight ); + + +//----------------------------------------------------------------------------- +// Is the window minimized? +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE bool Plat_IsWindowMinimized( PlatWindow_t hWindow ); + + +//----------------------------------------------------------------------------- +// Gets the shell window in a console app +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE PlatWindow_t Plat_GetShellWindow( ); + + +//----------------------------------------------------------------------------- +// Convert window -> Screen coordinates and back +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_WindowToScreenCoords( PlatWindow_t hWnd, int &x, int &y ); +PLATFORM_INTERFACE void Plat_ScreenToWindowCoords( PlatWindow_t hWnd, int &x, int &y ); + + +#endif // PLATWINDOW_H diff --git a/public/tier0/pmc360.h b/public/tier0/pmc360.h new file mode 100644 index 0000000..00c1cde --- /dev/null +++ b/public/tier0/pmc360.h @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Analogous to l2cache.h, this class represents information gleaned +// from the 360's Performance Monitor Counters. In particular we +// are interested in l2 cache misses and load-hit-stores. +// +//=============================================================================// +#ifndef CPMCDATA_H +#define CPMCDATA_H +#ifdef _WIN32 +#pragma once +#endif + +#ifndef _X360 +#error This file must only be compiled for XBOX360! +#endif + + +// Warning: +// As written, this class only supports profiling thread 0, processor 0. + +class CPMCData +{ +public: + + CPMCData(); + ~CPMCData() {}; + + void Start( void ); + void End( void ); + + /// This function should be called exactly once during the lifespan of the program; + /// it will set up the counters to record the information we are interested in. + /// This will stomp on whoever else might have set the performance counters elsewhere + /// in the game. + static void InitializeOnceProgramWide( void ); + static bool IsInitialized(); + + //------------------------------------------------------------------------- + // GetL2CacheMisses + //------------------------------------------------------------------------- + uint64 GetL2CacheMisses( void ) const + { + return m_Delta.L2CacheMiss; + } + + uint64 GetLHS( void ) const + { + return m_Delta.LHS; + } + +/* +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE +*/ + +private: + /// represents saved numbers from the counters we are interested in + struct PMCounters + { + uint64 L2CacheMiss; + uint64 LHS; ///< load hit store + + PMCounters(int64 _l2cm, int64 _lhs ) : L2CacheMiss(_l2cm), LHS(_lhs) {}; + PMCounters() : L2CacheMiss(0), LHS(0) {}; + }; + + PMCounters m_OnStart; ///< values when we began the timer + PMCounters m_Delta ; ///< computed total delta between start/stop +}; + +#endif // CPMCDATA_H \ No newline at end of file diff --git a/public/tier0/progressbar.h b/public/tier0/progressbar.h new file mode 100644 index 0000000..865c1ae --- /dev/null +++ b/public/tier0/progressbar.h @@ -0,0 +1,23 @@ +//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============// +// +// Purpose: Provide a shared place for library fucntions to report progress % for display +// +//=============================================================================// + +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H +#ifdef _WIN32 +#pragma once +#endif + + +PLATFORM_INTERFACE void ReportProgress(char const *job_name, int total_units_to_do, + int n_units_completed); + +typedef void (*ProgressReportHandler_t)( char const*, int, int ); + +// install your own handler. returns previous handler +PLATFORM_INTERFACE ProgressReportHandler_t InstallProgressReportHandler( ProgressReportHandler_t pfn); + + +#endif diff --git a/public/tier0/protected_things.h b/public/tier0/protected_things.h new file mode 100644 index 0000000..388e38d --- /dev/null +++ b/public/tier0/protected_things.h @@ -0,0 +1,266 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PROTECTED_THINGS_H +#define PROTECTED_THINGS_H +#ifdef _WIN32 +#pragma once +#endif + +// This header tries to prevent people from using potentially dangerous functions +// (like the notorious non-null-terminating strncpy) and functions that will break +// VCR mode (like time, input, registry, etc). +// +// This header should be included by ALL of our source code. + +// Eventually, ALL of these should be protected, but one man can only accomplish so much in +// one day AND work on features too! +#if defined( PROTECTED_STRINGS_ENABLE ) + + #if defined( printf ) + #undef printf + #endif + #define printf printf__HEY_YOU__USE_VSTDLIB + + #if defined( wprintf ) + #undef wprintf + #endif + #define wprintf wprintf__HEY_YOU__USE_VSTDLIB + + #if defined( strcmp ) + #undef strcmp + #endif + #define strcmp strcmp__HEY_YOU__USE_VSTDLIB + + #if defined( wcscmp ) + #undef wcscmp + #endif + #define wcscmp wcscmp__HEY_YOU__USE_VSTDLIB + + #if defined( strncpy ) + #undef strncpy + #endif + #define strncpy strncpy__HEY_YOU__USE_VSTDLIB + + #if defined( wcsncpy ) + #undef wcsncpy + #endif + #define wcsncpy wcsncpy__HEY_YOU__USE_VSTDLIB + + #if defined( strlen ) + #undef strlen + #endif + #define strlen strlen__HEY_YOU__USE_VSTDLIB + + #if defined( wcslen ) + #undef wcslen + #endif + #define wcslen wcslen__HEY_YOU__USE_VSTDLIB + + #if defined( Q_strlen ) + #undef Q_strlen + #endif + #define Q_strlen Q_strlen__HEY_YOU__USE_VSTDLIB + + #if defined( _snprintf ) + #undef _snprintf + #endif + #define _snprintf snprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _snwprintf ) + #undef _snwprintf + #endif + #define _snwprintf snwprintf__HEY_YOU__USE_VSTDLIB + + #if defined( sprintf ) + #undef sprintf + #endif + #define sprintf sprintf__HEY_YOU__USE_VSTDLIB + + #if defined( swprintf ) + #undef swprintf + #endif + #define swprintf swprintf__HEY_YOU__USE_VSTDLIB + + #if defined( vsprintf ) + #undef vsprintf + #endif + #define vsprintf vsprintf__HEY_YOU__USE_VSTDLIB + + #if defined( vswprintf ) + #undef vswprintf + #endif + #define vswprintf vswprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _vsnprintf ) + #undef _vsnprintf + #endif + #define _vsnprintf vsnprintf__HEY_YOU__USE_VSTDLIB + + #if defined( _vsnwprintf ) + #undef _vsnwprintf + #endif + #define _vsnwprintf vsnwprintf__HEY_YOU__USE_VSTDLIB + + #if defined( strcat ) + #undef strcat + #endif + #define strcat strcat__HEY_YOU__USE_VSTDLIB + + #if defined( wcscat ) + #undef wcscat + #endif + #define wcscat wcscat__HEY_YOU__USE_VSTDLIB + + #if defined( strncat ) + #undef strncat + #endif + #define strncat strncat__HEY_YOU__USE_VSTDLIB + + #if defined( wcsncat ) + #undef wcsncat + #endif + #define wcsncat wcsncat__HEY_YOU__USE_VSTDLIB + +#endif + + +#if defined( PROTECTED_THINGS_ENABLE ) && !defined( _X360 ) + + #if defined( GetTickCount ) + #undef GetTickCount + #endif + #define GetTickCount Use__Plat_MSTime__Instead_of_GetTickCount + + + #if defined( timeGetTime ) + #undef timeGetTime + #endif + #define timeGetTime Use__Plat_MSTime__Instead_of_timeGetTime + #if defined( clock ) + #undef clock + #endif + + + #if defined( GetCursorPos ) + #undef GetCursorPos + #endif + #define GetCursorPos GetCursorPos__USE_VCR_MODE + + + #if defined( ScreenToClient ) + #undef ScreenToClient + #endif + #define ScreenToClient ScreenToClient__USE_VCR_MODE + + +// JAY64: Revisit this, but squelch the warnings for now +#ifndef _WIN64 + #if defined( GetCommandLine ) + #undef GetCommandLine + #endif + #define GetCommandLine GetCommandLine__USE_VCR_MODE + + + #if defined( RegOpenKeyEx ) + #undef RegOpenKeyEx + #endif + #define RegOpenKeyEx RegOpenKeyEx__USE_VCR_MODE + + + #if defined( RegOpenKey ) + #undef RegOpenKey + #endif + #define RegOpenKey RegOpenKey__USE_VCR_MODE + + + #if defined( RegSetValueEx ) + #undef RegSetValueEx + #endif + #define RegSetValueEx RegSetValueEx__USE_VCR_MODE + + + #if defined( RegSetValue ) + #undef RegSetValue + #endif + #define RegSetValue RegSetValue__USE_VCR_MODE + + + #if defined( RegQueryValueEx ) + #undef RegQueryValueEx + #endif + #define RegQueryValueEx RegQueryValueEx__USE_VCR_MODE + + + #if defined( RegQueryValue ) + #undef RegQueryValue + #endif + #define RegQueryValue RegQueryValue__USE_VCR_MODE + + + #if defined( RegCreateKeyEx ) + #undef RegCreateKeyEx + #endif + #define RegCreateKeyEx RegCreateKeyEx__USE_VCR_MODE + + + #if defined( RegCreateKey ) + #undef RegCreateKey + #endif + #define RegCreateKey RegCreateKey__USE_VCR_MODE + + + #if defined( RegCloseKey ) + #undef RegCloseKey + #endif + #define RegCloseKey RegCloseKey__USE_VCR_MODE + + + #if defined( GetNumberOfConsoleInputEvents ) + #undef GetNumberOfConsoleInputEvents + #endif + #define GetNumberOfConsoleInputEvents GetNumberOfConsoleInputEvents__USE_VCR_MODE + + + #if defined( ReadConsoleInput ) + #undef ReadConsoleInput + #endif + #define ReadConsoleInput ReadConsoleInput__USE_VCR_MODE + + + #if defined( GetAsyncKeyState ) + #undef GetAsyncKeyState + #endif + #define GetAsyncKeyState GetAsyncKeyState__USE_VCR_MODE + + + #if defined( GetKeyState ) + #undef GetKeyState + #endif + #define GetKeyState GetKeyState__USE_VCR_MODE +#endif + + + #if defined( CreateThread ) + #undef CreateThread + #endif + #define CreateThread use__ThreadTools__for_thread_functions + + #if defined( WaitForSingleObject ) + #undef WaitForSingleObject + #endif + #define WaitForSingleObject use__ThreadTools__for_thread_functions + + #if defined( EnterCriticalSection ) + #undef EnterCriticalSection + #endif + #define EnterCriticalSection use__ThreadTools__for_thread_functions + +#endif + + +#endif // PROTECTED_THINGS_H diff --git a/public/tier0/systeminformation.h b/public/tier0/systeminformation.h new file mode 100644 index 0000000..c3d4789 --- /dev/null +++ b/public/tier0/systeminformation.h @@ -0,0 +1,58 @@ +//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef SYSTEMINFORMATION_H +#define SYSTEMINFORMATION_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +#ifndef PLATFORM_INTERFACE + #define PLATFORM_INTERFACE +#endif + +// +// Defines a possible outcome of a system call +// +enum SYSTEM_CALL_RESULT_t +{ + SYSCALL_SUCCESS = 0, // System call succeeded + SYSCALL_FAILED = 1, // System call failed + SYSCALL_NOPROC = 2, // Failed to find required system procedure + SYSCALL_NODLL = 3, // Failed to find or load required system module + SYSCALL_UNSUPPORTED = 4, // System call unsupported on the OS +}; + + +// +// Information about paged pool memory +// +struct PAGED_POOL_INFO_t +{ + uint32 numPagesUsed; // Number of Paged Pool pages used + uint32 numPagesFree; // Number of Paged Pool pages free +}; + +// +// Plat_GetMemPageSize +// Returns the size of a memory page in kilobytes. +// +PLATFORM_INTERFACE uint32 Plat_GetMemPageSize(); + +// +// Plat_GetPagedPoolInfo +// Fills in the paged pool info structure if successful. +// +PLATFORM_INTERFACE SYSTEM_CALL_RESULT_t Plat_GetPagedPoolInfo( PAGED_POOL_INFO_t *pPPI ); + + + +#endif // #ifndef SYSTEMINFORMATION_H diff --git a/public/tier0/testthread.h b/public/tier0/testthread.h new file mode 100644 index 0000000..a2936af --- /dev/null +++ b/public/tier0/testthread.h @@ -0,0 +1,60 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: exposes testing thread functions +// +//============================================================================= + +#ifndef TESTTHREAD_H +#define TESTTHREAD_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" + +// test callback +typedef void (STDCALL *TestFunc)(void *pv); + +// runs the test function +PLATFORM_INTERFACE void Test_RunTest(TestFunc func, void *pvArg); + +// call to give the test thread a chance to run +// calling thread will block until the test thread yields +// doesn't do anything if no tests are running +PLATFORM_INTERFACE void Test_RunFrame(); + +// true if any tests are running, or have ran +PLATFORM_INTERFACE bool Test_IsActive(); + +// sets that the test has failed +PLATFORM_INTERFACE void Test_SetFailed(); + +// true if any tests have failed, due to an assert, warning, or explicit fail +PLATFORM_INTERFACE bool Test_HasFailed(); + +// true if any tests have completed +PLATFORM_INTERFACE bool Test_HasFinished(); + +// terminates the test thread +PLATFORM_INTERFACE void Test_TerminateThread(); + +// the following functions should only be called from the test thread + +// yields to the main thread for a single frame +// passing in is a count of the number of frames that have been yielded by this yield macro +// can be used to assert if a test thread is blocked foor +PLATFORM_INTERFACE void TestThread_Yield(); + +// utility functions to pause the test frame until the selected condition is true +#define YIELD_UNTIL(x) { int iYieldCount = 0; while (!(x)) { TestThread_Yield(); iYieldCount++; if ( iYieldCount >= 100 ) { AssertMsg( false, #x ); break; } } } + +// use this like a while(1) loop, with break; to stop yielding +#define YIELD_UNTIL_BREAK() for (; true; TestThread_Yield()) + +// yields for a single frame +#define YIELD_FRAME() { TestThread_Yield(); } +#define YIELD_TWO_FRAMES() { TestThread_Yield(); TestThread_Yield(); } + + + +#endif // TESTTHREAD_H diff --git a/public/tier0/threadtools.h b/public/tier0/threadtools.h new file mode 100644 index 0000000..2a5f261 --- /dev/null +++ b/public/tier0/threadtools.h @@ -0,0 +1,1770 @@ +//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: A collection of utility classes to simplify thread handling, and +// as much as possible contain portability problems. Here avoiding +// including windows.h. +// +//============================================================================= + +#ifndef THREADTOOLS_H +#define THREADTOOLS_H + +#include + +#include "tier0/platform.h" +#include "tier0/dbg.h" + +#ifdef POSIX +#include +#include +#define WAIT_OBJECT_0 0 +#define WAIT_TIMEOUT 0x00000102 +#define WAIT_FAILED -1 +#define THREAD_PRIORITY_HIGHEST 2 +#endif + +#ifdef OSX +// Add some missing defines +#define PTHREAD_MUTEX_TIMED_NP PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE +#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK +#define PTHREAD_MUTEX_ADAPTIVE_NP 3 +#endif + + +#if defined( _WIN32 ) +#pragma once +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#ifdef COMPILER_MSVC64 +#include +#endif + +// #define THREAD_PROFILER 1 + +#define THREAD_MUTEX_TRACING_SUPPORTED +#if defined(_WIN32) && defined(_DEBUG) +#define THREAD_MUTEX_TRACING_ENABLED +#endif + +#ifdef _WIN32 +typedef void *HANDLE; +#endif + +#ifdef _X360 +#define MAX_THREADS_SUPPORTED 16 +#else +#define MAX_THREADS_SUPPORTED 32 +#endif + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +const unsigned TT_INFINITE = 0xffffffff; + +#ifdef PLATFORM_64BITS +typedef uint64 ThreadId_t; +#else +typedef uint32 ThreadId_t; +#endif + +//----------------------------------------------------------------------------- +// +// Simple thread creation. Differs from VCR mode/CreateThread/_beginthreadex +// in that it accepts a standard C function rather than compiler specific one. +// +//----------------------------------------------------------------------------- +FORWARD_DECLARE_HANDLE( ThreadHandle_t ); +typedef uintp (*ThreadFunc_t)( void *pParam ); + +PLATFORM_INTERFACE ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, unsigned stackSize = 0 ); +PLATFORM_INTERFACE bool ReleaseThreadHandle( ThreadHandle_t ); + + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE void ThreadSleep(unsigned duration = 0); +PLATFORM_INTERFACE ThreadId_t ThreadGetCurrentId(); +PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle(); +PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); +PLATFORM_INTERFACE bool ThreadSetPriority( ThreadHandle_t hThread, int priority ); +inline bool ThreadSetPriority( int priority ) { return ThreadSetPriority( NULL, priority ); } +PLATFORM_INTERFACE bool ThreadInMainThread(); +PLATFORM_INTERFACE void DeclareCurrentThreadIsMainThread(); + +// NOTE: ThreadedLoadLibraryFunc_t needs to return the sleep time in milliseconds or TT_INFINITE +typedef int (*ThreadedLoadLibraryFunc_t)(); +PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func ); +PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc(); + +#if defined( PLATFORM_WINDOWS_PC32 ) +DLL_IMPORT unsigned long STDCALL GetCurrentThreadId(); +#define ThreadGetCurrentId GetCurrentThreadId +#endif + +inline void ThreadPause() +{ +#if defined( COMPILER_GCC ) + __asm __volatile( "pause" ); +#elif defined ( COMPILER_MSVC64 ) + _mm_pause(); +#elif defined( COMPILER_MSVC32 ) + __asm pause; +#elif defined( COMPILER_MSVCX360 ) + YieldProcessor(); + __asm { or r0,r0,r0 } + YieldProcessor(); + __asm { or r1,r1,r1 } +#else +#error "implement me" +#endif +} + +PLATFORM_INTERFACE bool ThreadJoin( ThreadHandle_t, unsigned timeout = TT_INFINITE ); + +PLATFORM_INTERFACE void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName ); +inline void ThreadSetDebugName( const char *pszName ) { ThreadSetDebugName( NULL, pszName ); } + +PLATFORM_INTERFACE void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask ); + + +//----------------------------------------------------------------------------- +// +// Interlock methods. These perform very fast atomic thread +// safe operations. These are especially relevant in a multi-core setting. +// +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define NOINLINE +#elif defined(POSIX) +#define NOINLINE __attribute__ ((noinline)) +#endif + +#ifndef _X360 +#define ThreadMemoryBarrier() ((void)0) +#else +#define ThreadMemoryBarrier() __lwsync() +#endif + +#if defined( _LINUX ) || defined( _OSX ) +#define USE_INTRINSIC_INTERLOCKED +// linux implementation +inline int32 ThreadInterlockedIncrement( int32 volatile *p ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, 1 ) + 1; +} + +inline int32 ThreadInterlockedDecrement( int32 volatile *p ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, -1 ) - 1; +} + +inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value ) +{ + Assert( (size_t)p % 4 == 0 ); + int32 nRet; + + // Note: The LOCK instruction prefix is assumed on the XCHG instruction and GCC gets very confused on the Mac when we use it. + __asm __volatile( + "xchgl %2,(%1)" + : "=r" (nRet) + : "r" (p), "0" (value) + : "memory"); + return nRet; +} + +inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, value ); +} +inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_val_compare_and_swap( p, comperand, value ); +} + + +inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_bool_compare_and_swap( p, comperand, value ); +} + +#elif ( defined( COMPILER_MSVC32 ) && ( _MSC_VER >= 1310 ) ) +// windows 32 implemnetation using compiler intrinsics +#define USE_INTRINSIC_INTERLOCKED + +extern "C" +{ + long __cdecl _InterlockedIncrement(volatile long*); + long __cdecl _InterlockedDecrement(volatile long*); + long __cdecl _InterlockedExchange(volatile long*, long); + long __cdecl _InterlockedExchangeAdd(volatile long*, long); + long __cdecl _InterlockedCompareExchange(volatile long*, long, long); +} + +#pragma intrinsic( _InterlockedCompareExchange ) +#pragma intrinsic( _InterlockedDecrement ) +#pragma intrinsic( _InterlockedExchange ) +#pragma intrinsic( _InterlockedExchangeAdd ) +#pragma intrinsic( _InterlockedIncrement ) + +inline int32 ThreadInterlockedIncrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedIncrement( (volatile long*)p ); } +inline int32 ThreadInterlockedDecrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedDecrement( (volatile long*)p ); } +inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchange( (volatile long*)p, value ); } +inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchangeAdd( (volatile long*)p, value ); } +inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedCompareExchange( (volatile long*)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return ( _InterlockedCompareExchange( (volatile long*)p, value, comperand ) == comperand ); } +#else +// non 32-bit windows and 360 implementation +PLATFORM_INTERFACE int32 ThreadInterlockedIncrement( int32 volatile * ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedDecrement( int32 volatile * ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedExchange( int32 volatile *, int32 value ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedExchangeAdd( int32 volatile *, int32 value ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedCompareExchange( int32 volatile *, int32 value, int32 comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf( int32 volatile *, int32 value, int32 comperand ) NOINLINE; +#endif + + +#if defined( USE_INTRINSIC_INTERLOCKED ) && !defined( PLATFORM_64BITS ) +#define TIPTR() +inline void *ThreadInterlockedExchangePointer( void * volatile *p, void *value ) { return (void *)( ( intp )ThreadInterlockedExchange( reinterpret_cast(p), reinterpret_cast(value) ) ); } +inline void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comperand ) { return (void *)( ( intp )ThreadInterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ) ); } +inline bool ThreadInterlockedAssignPointerIf( void * volatile *p, void *value, void *comperand ) { return ( ThreadInterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ) == reinterpret_cast(comperand) ); } +#else +PLATFORM_INTERFACE void *ThreadInterlockedExchangePointer( void * volatile *, void *value ) NOINLINE; +PLATFORM_INTERFACE void *ThreadInterlockedCompareExchangePointer( void * volatile *, void *value, void *comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignPointerIf( void * volatile *, void *value, void *comperand ) NOINLINE; +#endif + + +inline unsigned ThreadInterlockedExchangeSubtract( int32 volatile *p, int32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, -value ); } + +inline void const *ThreadInterlockedExchangePointerToConst( void const * volatile *p, void const *value ) { return ThreadInterlockedExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ) ); } +inline void const *ThreadInterlockedCompareExchangePointerToConst( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedCompareExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } +inline bool ThreadInterlockedAssignPointerToConstIf( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedAssignPointerIf( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } + + +PLATFORM_INTERFACE int64 ThreadInterlockedCompareExchange64( int64 volatile *, int64 value, int64 comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf64( volatile int64 *pDest, int64 value, int64 comperand ) NOINLINE; + +PLATFORM_INTERFACE int64 ThreadInterlockedExchange64( int64 volatile *, int64 value ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile * ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile * ) NOINLINE; +PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *, int64 value ) NOINLINE; + +inline unsigned ThreadInterlockedExchangeSubtract( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } +inline unsigned ThreadInterlockedIncrement( uint32 volatile *p ) { return ThreadInterlockedIncrement( (int32 volatile *)p ); } +inline unsigned ThreadInterlockedDecrement( uint32 volatile *p ) { return ThreadInterlockedDecrement( (int32 volatile *)p ); } +inline unsigned ThreadInterlockedExchange( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchange( (int32 volatile *)p, value ); } +inline unsigned ThreadInterlockedExchangeAdd( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } +inline unsigned ThreadInterlockedCompareExchange( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedCompareExchange( (int32 volatile *)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedAssignIf( (int32 volatile *)p, value, comperand ); } + +//inline int ThreadInterlockedExchangeSubtract( int volatile *p, int value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } +//inline int ThreadInterlockedIncrement( int volatile *p ) { return ThreadInterlockedIncrement( (int32 volatile *)p ); } +//inline int ThreadInterlockedDecrement( int volatile *p ) { return ThreadInterlockedDecrement( (int32 volatile *)p ); } +//inline int ThreadInterlockedExchange( int volatile *p, int value ) { return ThreadInterlockedExchange( (int32 volatile *)p, value ); } +//inline int ThreadInterlockedExchangeAdd( int volatile *p, int value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } +//inline int ThreadInterlockedCompareExchange( int volatile *p, int value, int comperand ) { return ThreadInterlockedCompareExchange( (int32 volatile *)p, value, comperand ); } +//inline bool ThreadInterlockedAssignIf( int volatile *p, int value, int comperand ) { return ThreadInterlockedAssignIf( (int32 volatile *)p, value, comperand ); } + + +//----------------------------------------------------------------------------- +// Access to VTune thread profiling +//----------------------------------------------------------------------------- +#if defined(_WIN32) && defined(THREAD_PROFILER) +PLATFORM_INTERFACE void ThreadNotifySyncPrepare(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncCancel(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncAcquired(void *p); +PLATFORM_INTERFACE void ThreadNotifySyncReleasing(void *p); +#else +#define ThreadNotifySyncPrepare(p) ((void)0) +#define ThreadNotifySyncCancel(p) ((void)0) +#define ThreadNotifySyncAcquired(p) ((void)0) +#define ThreadNotifySyncReleasing(p) ((void)0) +#endif + +//----------------------------------------------------------------------------- +// Encapsulation of a thread local datum (needed because THREAD_LOCAL doesn't +// work in a DLL loaded with LoadLibrary() +//----------------------------------------------------------------------------- + +#ifndef NO_THREAD_LOCAL + +#ifdef _LINUX +// linux totally supports compiler thread locals, even across dll's. +#define PLAT_COMPILER_SUPPORTED_THREADLOCALS 1 +#define CTHREADLOCALINTEGER( typ ) __thread int +#define CTHREADLOCALINT __thread int +#define CTHREADLOCALPTR( typ ) __thread typ * +#define CTHREADLOCAL( typ ) __thread typ +#define GETLOCAL( x ) ( x ) +#endif + +#if defined( _WIN32 ) || defined( OSX ) +#ifndef __AFXTLS_H__ // not compatible with some Windows headers + +#define CTHREADLOCALINT GenericThreadLocals::CThreadLocalInt +#define CTHREADLOCALINTEGER( typ ) GenericThreadLocals::CThreadLocalInt +#define CTHREADLOCALPTR( typ ) GenericThreadLocals::CThreadLocalPtr +#define CTHREADLOCAL( typ ) GenericThreadLocals::CThreadLocal +#define GETLOCAL( x ) ( x.Get() ) + + +namespace GenericThreadLocals +{ + // a (not so efficient) implementation of thread locals for compilers without full support (i.e. visual c). + // don't use this explicity - instead, use the CTHREADxxx macros above. + + class PLATFORM_CLASS CThreadLocalBase + { +public: + CThreadLocalBase(); + ~CThreadLocalBase(); + + void * Get() const; + void Set(void *); + +private: + uint32 m_index; +#if defined(POSIX) + pthread_key_t m_index; +#endif + }; + + //--------------------------------------------------------- + + template + class CThreadLocal : public CThreadLocalBase + { + public: + CThreadLocal() + { +#ifdef PLATFORM_64BITS + COMPILE_TIME_ASSERT( sizeof(T) <= sizeof(void *) ); +#else + COMPILE_TIME_ASSERT( sizeof(T) == sizeof(void *) ); +#endif + } + + void operator=( T i ) { Set( i ); } + + T Get() const + { +#ifdef PLATFORM_64BITS + void *pData = CThreadLocalBase::Get(); + return *reinterpret_cast( &pData ); +#else + #ifdef COMPILER_MSVC + #pragma warning ( disable : 4311 ) + #endif + return reinterpret_cast( CThreadLocalBase::Get() ); + #ifdef COMPILER_MSVC + #pragma warning ( default : 4311 ) + #endif +#endif + } + + void Set(T val) + { +#ifdef PLATFORM_64BITS + void* pData = 0; + *reinterpret_cast( &pData ) = val; + CThreadLocalBase::Set( pData ); +#else + #ifdef COMPILER_MSVC + #pragma warning ( disable : 4312 ) + #endif + CThreadLocalBase::Set( reinterpret_cast(val) ); + #ifdef COMPILER_MSVC + #pragma warning ( default : 4312 ) + #endif +#endif + } + }; + + + //--------------------------------------------------------- + + template + class CThreadLocalInt : public CThreadLocal + { + public: + operator const T() const { return this->Get(); } + int operator=( T i ) { Set( i ); return i; } + + T operator++() { T i = this->Get(); this->Set( ++i ); return i; } + T operator++(int) { T i = this->Get(); this->Set( i + 1 ); return i; } + + T operator--() { T i = this->Get(); this->Set( --i ); return i; } + T operator--(int) { T i = this->Get(); this->Set( i - 1 ); return i; } + + inline CThreadLocalInt( ) { } + inline CThreadLocalInt( const T &initialvalue ) + { + this->Set( initialvalue ); + } + }; + + + //--------------------------------------------------------- + + template + class CThreadLocalPtr : private CThreadLocalBase + { + public: + CThreadLocalPtr() {} + + operator const void *() const { return (const T *)Get(); } + operator void *() { return (T *)Get(); } + + operator const T *() const { return (const T *)Get(); } + operator const T *() { return (const T *)Get(); } + operator T *() { return (T *)Get(); } + + T * operator=( T *p ) { Set( p ); return p; } + + bool operator !() const { return (!Get()); } + bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() != NULL); } + bool operator==( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() == NULL); } + bool operator==( const void *p ) const { return (Get() == p); } + bool operator!=( const void *p ) const { return (Get() != p); } + bool operator==( const T *p ) const { return operator==((const void*)p); } + bool operator!=( const T *p ) const { return operator!=((const void*)p); } + + T * operator->() { return (T *)Get(); } + T & operator *() { return *((T *)Get()); } + + const T * operator->() const { return (const T *)Get(); } + const T & operator *() const { return *((const T *)Get()); } + + const T & operator[]( int i ) const { return *((const T *)Get() + i); } + T & operator[]( int i ) { return *((T *)Get() + i); } + + private: + // Disallowed operations + CThreadLocalPtr( T *pFrom ); + CThreadLocalPtr( const CThreadLocalPtr &from ); + T **operator &(); + T * const *operator &() const; + void operator=( const CThreadLocalPtr &from ); + bool operator==( const CThreadLocalPtr &p ) const; + bool operator!=( const CThreadLocalPtr &p ) const; + }; +} + +#ifdef _OSX +PLATFORM_INTERFACE GenericThreadLocals::CThreadLocalInt g_nThreadID; +#else // _OSX +#ifndef TIER0_DLL_EXPORT +__declspec( dllimport ) GenericThreadLocals::CThreadLocalInt g_nThreadID; +#endif +#endif // _OSX + +#endif /// afx32 +#endif //__win32 + +#endif // NO_THREAD_LOCAL + +//----------------------------------------------------------------------------- +// +// A super-fast thread-safe integer A simple class encapsulating the notion of an +// atomic integer used across threads that uses the built in and faster +// "interlocked" functionality rather than a full-blown mutex. Useful for simple +// things like reference counts, etc. +// +//----------------------------------------------------------------------------- + +template +class CInterlockedIntT +{ +public: + CInterlockedIntT() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(int32) ); } + CInterlockedIntT( T value ) : m_value( value ) {} + + T operator()( void ) const { return m_value; } + operator T() const { return m_value; } + + bool operator!() const { return ( m_value == 0 ); } + bool operator==( T rhs ) const { return ( m_value == rhs ); } + bool operator!=( T rhs ) const { return ( m_value != rhs ); } + + T operator++() { return (T)ThreadInterlockedIncrement( (int32 *)&m_value ); } + T operator++(int) { return operator++() - 1; } + + T operator--() { return (T)ThreadInterlockedDecrement( (int32 *)&m_value ); } + T operator--(int) { return operator--() + 1; } + + bool AssignIf( T conditionValue, T newValue ) { return ThreadInterlockedAssignIf( (int32 *)&m_value, (int32)newValue, (int32)conditionValue ); } + + T operator=( T newValue ) { ThreadInterlockedExchange((int32 *)&m_value, newValue); return m_value; } + + // Atomic add is like += except it returns the previous value as its return value + T AtomicAdd( T add ) { return (T)ThreadInterlockedExchangeAdd( (int32 *)&m_value, (int32)add ); } + + void operator+=( T add ) { ThreadInterlockedExchangeAdd( (int32 *)&m_value, (int32)add ); } + void operator-=( T subtract ) { operator+=( -subtract ); } + void operator*=( T multiplier ) { + T original, result; + do + { + original = m_value; + result = original * multiplier; + } while ( !AssignIf( original, result ) ); + } + void operator/=( T divisor ) { + T original, result; + do + { + original = m_value; + result = original / divisor; + } while ( !AssignIf( original, result ) ); + } + + T operator+( T rhs ) const { return m_value + rhs; } + T operator-( T rhs ) const { return m_value - rhs; } + +private: + volatile T m_value; +}; + +typedef CInterlockedIntT CInterlockedInt; +typedef CInterlockedIntT CInterlockedUInt; + +//----------------------------------------------------------------------------- + +template +class CInterlockedPtr +{ +public: + CInterlockedPtr() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T *) == sizeof(int32) ); /* Will need to rework operator+= for 64 bit */ } + CInterlockedPtr( T *value ) : m_value( value ) {} + + operator T *() const { return m_value; } + + bool operator!() const { return ( m_value == 0 ); } + bool operator==( T *rhs ) const { return ( m_value == rhs ); } + bool operator!=( T *rhs ) const { return ( m_value != rhs ); } + + T *operator++() { return ((T *)ThreadInterlockedExchangeAdd( (int32 *)&m_value, sizeof(T) )) + 1; } + T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd( (int32 *)&m_value, sizeof(T) ); } + + T *operator--() { return ((T *)ThreadInterlockedExchangeAdd( (int32 *)&m_value, -sizeof(T) )) - 1; } + T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd( (int32 *)&m_value, -sizeof(T) ); } + + bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } + + T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } + + void operator+=( int add ) { ThreadInterlockedExchangeAdd( (int32 *)&m_value, add * sizeof(T) ); } + void operator-=( int subtract ) { operator+=( -subtract ); } + + // Atomic add is like += except it returns the previous value as its return value + T *AtomicAdd( int add ) { return ( T * ) ThreadInterlockedExchangeAdd( (int32 *)&m_value, add * sizeof(T) ); } + + T *operator+( int rhs ) const { return m_value + rhs; } + T *operator-( int rhs ) const { return m_value - rhs; } + T *operator+( unsigned rhs ) const { return m_value + rhs; } + T *operator-( unsigned rhs ) const { return m_value - rhs; } + size_t operator-( T *p ) const { return m_value - p; } + size_t operator-( const CInterlockedPtr &p ) const { return m_value - p.m_value; } + +private: + T * volatile m_value; +}; + + + +//----------------------------------------------------------------------------- +// +// Platform independent for critical sections management +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadMutex +{ +public: + CThreadMutex(); + ~CThreadMutex(); + + //------------------------------------------------------ + // Mutex acquisition/release. Const intentionally defeated. + //------------------------------------------------------ + void Lock(); + void Lock() const { (const_cast(this))->Lock(); } + void Unlock(); + void Unlock() const { (const_cast(this))->Unlock(); } + + bool TryLock(); + bool TryLock() const { return (const_cast(this))->TryLock(); } + + void LockSilent(); // A Lock() operation which never spews. Required by the logging system to prevent badness. + void UnlockSilent(); // An Unlock() operation which never spews. Required by the logging system to prevent badness. + + //------------------------------------------------------ + // Use this to make deadlocks easier to track by asserting + // when it is expected that the current thread owns the mutex + //------------------------------------------------------ + bool AssertOwnedByCurrentThread(); + + //------------------------------------------------------ + // Enable tracing to track deadlock problems + //------------------------------------------------------ + void SetTrace( bool ); + +private: + // Disallow copying + CThreadMutex( const CThreadMutex & ); + CThreadMutex &operator=( const CThreadMutex & ); + +#if defined( _WIN32 ) + // Efficient solution to breaking the windows.h dependency, invariant is tested. +#ifdef _WIN64 + #define TT_SIZEOF_CRITICALSECTION 40 +#else +#ifndef _X360 + #define TT_SIZEOF_CRITICALSECTION 24 +#else + #define TT_SIZEOF_CRITICALSECTION 28 +#endif // !_X360 +#endif // _WIN64 + byte m_CriticalSection[TT_SIZEOF_CRITICALSECTION]; +#elif defined(POSIX) + pthread_mutex_t m_Mutex; + pthread_mutexattr_t m_Attr; +#else +#error +#endif + +#ifdef THREAD_MUTEX_TRACING_SUPPORTED + // Debugging (always herge to allow mixed debug/release builds w/o changing size) + uint m_currentOwnerID; + uint16 m_lockCount; + bool m_bTrace; +#endif +}; + +//----------------------------------------------------------------------------- +// +// An alternative mutex that is useful for cases when thread contention is +// rare, but a mutex is required. Instances should be declared volatile. +// Sleep of 0 may not be sufficient to keep high priority threads from starving +// lesser threads. This class is not a suitable replacement for a critical +// section if the resource contention is high. +// +//----------------------------------------------------------------------------- + +#if !defined(THREAD_PROFILER) + +class CThreadFastMutex +{ +public: + CThreadFastMutex() + : m_ownerID( 0 ), + m_depth( 0 ) + { + } + +private: + FORCEINLINE bool TryLockInline( const uint32 threadId ) volatile + { + if ( threadId != m_ownerID && !ThreadInterlockedAssignIf( (volatile int32 *)&m_ownerID, (int32)threadId, 0 ) ) + return false; + + ThreadMemoryBarrier(); + ++m_depth; + return true; + } + + bool TryLock( const uint32 threadId ) volatile + { + return TryLockInline( threadId ); + } + + PLATFORM_CLASS void Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile; + +public: + bool TryLock() volatile + { +#ifdef _DEBUG + if ( m_depth == INT_MAX ) + DebuggerBreak(); + + if ( m_depth < 0 ) + DebuggerBreak(); +#endif + return TryLockInline( ThreadGetCurrentId() ); + } + +#ifndef _DEBUG + FORCEINLINE +#endif + void Lock( unsigned int nSpinSleepTime = 0 ) volatile + { + const uint32 threadId = ThreadGetCurrentId(); + + if ( !TryLockInline( threadId ) ) + { + ThreadPause(); + Lock( threadId, nSpinSleepTime ); + } +#ifdef _DEBUG + if ( m_ownerID != ThreadGetCurrentId() ) + DebuggerBreak(); + + if ( m_depth == INT_MAX ) + DebuggerBreak(); + + if ( m_depth < 0 ) + DebuggerBreak(); +#endif + } + +#ifndef _DEBUG + FORCEINLINE +#endif + void Unlock() volatile + { +#ifdef _DEBUG + if ( m_ownerID != ThreadGetCurrentId() ) + DebuggerBreak(); + + if ( m_depth <= 0 ) + DebuggerBreak(); +#endif + + --m_depth; + if ( !m_depth ) + { + ThreadMemoryBarrier(); + ThreadInterlockedExchange( &m_ownerID, 0 ); + } + } + + bool TryLock() const volatile { return (const_cast(this))->TryLock(); } + void Lock(unsigned nSpinSleepTime = 0 ) const volatile { (const_cast(this))->Lock( nSpinSleepTime ); } + void Unlock() const volatile { (const_cast(this))->Unlock(); } + + // To match regular CThreadMutex: + bool AssertOwnedByCurrentThread() { return true; } + void SetTrace( bool ) {} + + uint32 GetOwnerId() const { return m_ownerID; } + int GetDepth() const { return m_depth; } +private: + volatile uint32 m_ownerID; + int m_depth; +}; + +class ALIGN128 CAlignedThreadFastMutex : public CThreadFastMutex +{ +public: + CAlignedThreadFastMutex() + { + Assert( (size_t)this % 128 == 0 && sizeof(*this) == 128 ); + } + +private: + uint8 pad[128-sizeof(CThreadFastMutex)]; +}; + +#else +typedef CThreadMutex CThreadFastMutex; +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class CThreadNullMutex +{ +public: + static void Lock() {} + static void Unlock() {} + + static bool TryLock() { return true; } + static bool AssertOwnedByCurrentThread() { return true; } + static void SetTrace( bool b ) {} + + static uint32 GetOwnerId() { return 0; } + static int GetDepth() { return 0; } +}; + +//----------------------------------------------------------------------------- +// +// A mutex decorator class used to control the use of a mutex, to make it +// less expensive when not multithreading +// +//----------------------------------------------------------------------------- + +template +class CThreadConditionalMutex : public BaseClass +{ +public: + void Lock() { if ( *pCondition ) BaseClass::Lock(); } + void Lock() const { if ( *pCondition ) BaseClass::Lock(); } + void Unlock() { if ( *pCondition ) BaseClass::Unlock(); } + void Unlock() const { if ( *pCondition ) BaseClass::Unlock(); } + + bool TryLock() { if ( *pCondition ) return BaseClass::TryLock(); else return true; } + bool TryLock() const { if ( *pCondition ) return BaseClass::TryLock(); else return true; } + bool AssertOwnedByCurrentThread() { if ( *pCondition ) return BaseClass::AssertOwnedByCurrentThread(); else return true; } + void SetTrace( bool b ) { if ( *pCondition ) BaseClass::SetTrace( b ); } +}; + +//----------------------------------------------------------------------------- +// Mutex decorator that blows up if another thread enters +//----------------------------------------------------------------------------- + +template +class CThreadTerminalMutex : public BaseClass +{ +public: + bool TryLock() { if ( !BaseClass::TryLock() ) { DebuggerBreak(); return false; } return true; } + bool TryLock() const { if ( !BaseClass::TryLock() ) { DebuggerBreak(); return false; } return true; } + void Lock() { if ( !TryLock() ) BaseClass::Lock(); } + void Lock() const { if ( !TryLock() ) BaseClass::Lock(); } + +}; + +//----------------------------------------------------------------------------- +// +// Class to Lock a critical section, and unlock it automatically +// when the lock goes out of scope +// +//----------------------------------------------------------------------------- + +template +class CAutoLockT +{ +public: + FORCEINLINE CAutoLockT( MUTEX_TYPE &lock) + : m_lock(lock) + { + m_lock.Lock(); + } + + FORCEINLINE CAutoLockT(const MUTEX_TYPE &lock) + : m_lock(const_cast(lock)) + { + m_lock.Lock(); + } + + FORCEINLINE ~CAutoLockT() + { + m_lock.Unlock(); + } + + +private: + MUTEX_TYPE &m_lock; + + // Disallow copying + CAutoLockT( const CAutoLockT & ); + CAutoLockT &operator=( const CAutoLockT & ); +}; + +typedef CAutoLockT CAutoLock; + +//--------------------------------------------------------- + +template struct CAutoLockTypeDeducer {}; +template <> struct CAutoLockTypeDeducer { typedef CThreadMutex Type_t; }; +template <> struct CAutoLockTypeDeducer { typedef CThreadNullMutex Type_t; }; +#if !defined(THREAD_PROFILER) +template <> struct CAutoLockTypeDeducer { typedef CThreadFastMutex Type_t; }; +template <> struct CAutoLockTypeDeducer { typedef CAlignedThreadFastMutex Type_t; }; +#endif + +#define AUTO_LOCK_( type, mutex ) \ + CAutoLockT< type > UNIQUE_ID( static_cast( mutex ) ) + +#define AUTO_LOCK( mutex ) \ + AUTO_LOCK_( CAutoLockTypeDeducer::Type_t, mutex ) + + +#define AUTO_LOCK_FM( mutex ) \ + AUTO_LOCK_( CThreadFastMutex, mutex ) + +#define LOCAL_THREAD_LOCK_( tag ) \ + ; \ + static CThreadFastMutex autoMutex_##tag; \ + AUTO_LOCK( autoMutex_##tag ) + +#define LOCAL_THREAD_LOCK() \ + LOCAL_THREAD_LOCK_(_) + +//----------------------------------------------------------------------------- +// +// Base class for event, semaphore and mutex objects. +// +//----------------------------------------------------------------------------- + +#define TW_TIMEOUT 0xFFFF +#define TW_FAILED 0xFFFE + +class PLATFORM_CLASS CThreadSyncObject +{ +public: + ~CThreadSyncObject(); + + //----------------------------------------------------- + // Query if object is useful + //----------------------------------------------------- + bool operator!() const; + + //----------------------------------------------------- + // Access handle + //----------------------------------------------------- +#ifdef _WIN32 + operator HANDLE() { return GetHandle(); } + const HANDLE GetHandle() const { return m_hSyncObject; } +#endif + //----------------------------------------------------- + // Wait for a signal from the object + //----------------------------------------------------- + bool Wait( uint32 dwTimeout = TT_INFINITE ); + + //----------------------------------------------------- + // Wait for a signal from any of the specified objects. + // + // Returns the index of the object that signaled the event + // or THREADSYNC_TIMEOUT if the timeout was hit before the wait condition was met. + // + // Returns TW_FAILED if an incoming object is invalid. + // + // If bWaitAll=true, then it'll return 0 if all the objects were set. + //----------------------------------------------------- + static uint32 WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + + // This builds a list of pointers and calls straight through to the other WaitForMultiple. + static uint32 WaitForMultiple( int nObjects, CThreadSyncObject *ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + +protected: + CThreadSyncObject(); + void AssertUseable(); + +#ifdef _WIN32 + HANDLE m_hSyncObject; +#elif defined(POSIX) + pthread_mutex_t m_Mutex; + pthread_cond_t m_Condition; + bool m_bInitalized; + CInterlockedInt m_cSet; + bool m_bManualReset; +#else +#error "Implement me" +#endif + +private: + CThreadSyncObject( const CThreadSyncObject & ); + CThreadSyncObject &operator=( const CThreadSyncObject & ); +}; + + +//----------------------------------------------------------------------------- +// +// Wrapper for unnamed event objects +// +//----------------------------------------------------------------------------- + +#if defined( _WIN32 ) + +//----------------------------------------------------------------------------- +// +// CThreadSemaphore +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadSemaphore : public CThreadSyncObject +{ +public: + CThreadSemaphore(int32 initialValue, int32 maxValue); + + //----------------------------------------------------- + // Increases the count of the semaphore object by a specified + // amount. Wait() decreases the count by one on return. + //----------------------------------------------------- + bool Release(int32 releaseCount = 1, int32 * pPreviousCount = NULL ); + +private: + CThreadSemaphore(const CThreadSemaphore &); + CThreadSemaphore &operator=(const CThreadSemaphore &); +}; + + +//----------------------------------------------------------------------------- +// +// A mutex suitable for out-of-process, multi-processor usage +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadFullMutex : public CThreadSyncObject +{ +public: + CThreadFullMutex( bool bEstablishInitialOwnership = false, const char * pszName = NULL ); + + //----------------------------------------------------- + // Release ownership of the mutex + //----------------------------------------------------- + bool Release(); + + // To match regular CThreadMutex: + void Lock() { Wait(); } + void Lock( unsigned timeout ) { Wait( timeout ); } + void Unlock() { Release(); } + bool AssertOwnedByCurrentThread() { return true; } + void SetTrace( bool ) {} + +private: + CThreadFullMutex( const CThreadFullMutex & ); + CThreadFullMutex &operator=( const CThreadFullMutex & ); +}; +#endif + +enum NamedEventResult_t +{ + TT_EventDoesntExist = 0, + TT_EventNotSignaled, + TT_EventSignaled +}; + +class PLATFORM_CLASS CThreadEvent : public CThreadSyncObject +{ +public: + CThreadEvent( bool fManualReset = false ); +#ifdef PLATFORM_WINDOWS + CThreadEvent( const char *name, bool initialState = false, bool bManualReset = false ); + static NamedEventResult_t CheckNamedEvent( const char *name, uint32 dwTimeout = 0 ); +#endif +#ifdef WIN32 + CThreadEvent( HANDLE hHandle ); +#endif + //----------------------------------------------------- + // Set the state to signaled + //----------------------------------------------------- + bool Set(); + + //----------------------------------------------------- + // Set the state to nonsignaled + //----------------------------------------------------- + bool Reset(); + + //----------------------------------------------------- + // Check if the event is signaled + //----------------------------------------------------- + bool Check(); + + bool Wait( uint32 dwTimeout = TT_INFINITE ); + + // See CThreadSyncObject for definitions of these functions. + static uint32 WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + static uint32 WaitForMultiple( int nObjects, CThreadEvent *ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + +private: + CThreadEvent( const CThreadEvent & ); + CThreadEvent &operator=( const CThreadEvent & ); +}; + +// Hard-wired manual event for use in array declarations +class CThreadManualEvent : public CThreadEvent +{ +public: + CThreadManualEvent() + : CThreadEvent( true ) + { + } +}; + + +//----------------------------------------------------------------------------- +// +// CThreadRWLock +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThreadRWLock +{ +public: + CThreadRWLock(); + + void LockForRead(); + void UnlockRead(); + void LockForWrite(); + void UnlockWrite(); + + void LockForRead() const { const_cast(this)->LockForRead(); } + void UnlockRead() const { const_cast(this)->UnlockRead(); } + void LockForWrite() const { const_cast(this)->LockForWrite(); } + void UnlockWrite() const { const_cast(this)->UnlockWrite(); } + +private: + void WaitForRead(); + + CThreadFastMutex m_mutex; + CThreadEvent m_CanWrite; + CThreadEvent m_CanRead; + + int m_nWriters; + int m_nActiveReaders; + int m_nPendingReaders; +}; + +//----------------------------------------------------------------------------- +// +// CThreadSpinRWLock +// +//----------------------------------------------------------------------------- + +class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock +{ +public: + CThreadSpinRWLock() { COMPILE_TIME_ASSERT( sizeof( LockInfo_t ) == sizeof( int64 ) ); Assert( (intp)this % 8 == 0 ); memset( this, 0, sizeof( *this ) ); } + + bool TryLockForWrite(); + bool TryLockForRead(); + + void LockForRead(); + void UnlockRead(); + void LockForWrite(); + void UnlockWrite(); + + bool TryLockForWrite() const { return const_cast(this)->TryLockForWrite(); } + bool TryLockForRead() const { return const_cast(this)->TryLockForRead(); } + void LockForRead() const { const_cast(this)->LockForRead(); } + void UnlockRead() const { const_cast(this)->UnlockRead(); } + void LockForWrite() const { const_cast(this)->LockForWrite(); } + void UnlockWrite() const { const_cast(this)->UnlockWrite(); } + +private: + // This structure is used as an atomic & exchangeable 64-bit value. It would probably be better to just have one 64-bit value + // and accessor functions that make/break it, but at this late stage of development, I'm just wrapping it into union + // Beware of endianness: on Xbox/PowerPC m_writerId is high-word of m_i64; on PC, it's low-dword of m_i64 + union LockInfo_t + { + struct + { + uint32 m_writerId; + int m_nReaders; + }; + int64 m_i64; + }; + + bool AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ); + bool TryLockForWrite( const uint32 threadId ); + void SpinLockForWrite( const uint32 threadId ); + + volatile LockInfo_t m_lockInfo; + CInterlockedInt m_nWriters; +} ALIGN8_POST; + +//----------------------------------------------------------------------------- +// +// A thread wrapper similar to a Java thread. +// +//----------------------------------------------------------------------------- + +class PLATFORM_CLASS CThread +{ +public: + CThread(); + virtual ~CThread(); + + //----------------------------------------------------- + + const char *GetName(); + void SetName( const char * ); + + size_t CalcStackDepth( void *pStackVariable ) { return ((byte *)m_pStackBase - (byte *)pStackVariable); } + + //----------------------------------------------------- + // Functions for the other threads + //----------------------------------------------------- + + // Start thread running - error if already running + virtual bool Start( unsigned nBytesStack = 0 ); + + // Returns true if thread has been created and hasn't yet exited + bool IsAlive(); + + // This method causes the current thread to wait until this thread + // is no longer alive. + bool Join( unsigned timeout = TT_INFINITE ); + + // Access the thread handle directly + ThreadHandle_t GetThreadHandle(); + +#ifdef _WIN32 + uint GetThreadId(); +#endif + + //----------------------------------------------------- + + int GetResult(); + + //----------------------------------------------------- + // Functions for both this, and maybe, and other threads + //----------------------------------------------------- + + // Forcibly, abnormally, but relatively cleanly stop the thread + void Stop( int exitCode = 0 ); + + // Get the priority + int GetPriority() const; + + // Set the priority + bool SetPriority( int ); + + // Suspend a thread + unsigned Suspend(); + + // Resume a suspended thread + unsigned Resume(); + + // Force hard-termination of thread. Used for critical failures. + bool Terminate( int exitCode = 0 ); + + //----------------------------------------------------- + // Global methods + //----------------------------------------------------- + + // Get the Thread object that represents the current thread, if any. + // Can return NULL if the current thread was not created using + // CThread + static CThread *GetCurrentCThread(); + + // Offer a context switch. Under Win32, equivalent to Sleep(0) +#ifdef Yield +#undef Yield +#endif + static void Yield(); + + // This method causes the current thread to yield and not to be + // scheduled for further execution until a certain amount of real + // time has elapsed, more or less. Duration is in milliseconds + static void Sleep( unsigned duration ); + +protected: + + // Optional pre-run call, with ability to fail-create. Note Init() + // is forced synchronous with Start() + virtual bool Init(); + + // Thread will run this function on startup, must be supplied by + // derived class, performs the intended action of the thread. + virtual int Run() = 0; + + // Called when the thread exits + virtual void OnExit(); + +#ifdef _WIN32 + // Allow for custom start waiting + virtual bool WaitForCreateComplete( CThreadEvent *pEvent ); +#endif + + CThreadMutex m_Lock; + CThreadEvent m_ExitEvent; // Set right before the thread's function exits. + +private: + enum Flags + { + SUPPORT_STOP_PROTOCOL = 1 << 0 + }; + + // Thread initially runs this. param is actually 'this'. function + // just gets this and calls ThreadProc + struct ThreadInit_t + { + CThread * pThread; +#ifdef _WIN32 + CThreadEvent *pInitCompleteEvent; +#endif + bool * pfInitSuccess; + }; + +#ifdef PLATFORM_WINDOWS + static unsigned long __stdcall ThreadProc( void * pv ); +#else + static void* ThreadProc( void * pv ); +#endif + + // make copy constructor and assignment operator inaccessible + CThread( const CThread & ); + CThread &operator=( const CThread & ); + +#ifdef _WIN32 + HANDLE m_hThread; +#elif defined(_POSIX) + pthread_t m_threadId; + CInterlockedInt m_nSuspendCount; +#endif + int m_result; + char m_szName[32]; + void * m_pStackBase; + unsigned m_flags; +}; + +//----------------------------------------------------------------------------- +// Simple thread class encompasses the notion of a worker thread, handing +// synchronized communication. +//----------------------------------------------------------------------------- + +// These are internal reserved error results from a call attempt +enum WTCallResult_t +{ + WTCR_FAIL = -1, + WTCR_TIMEOUT = -2, + WTCR_THREAD_GONE = -3, +}; + +class PLATFORM_CLASS CWorkerThread : public CThread +{ +public: + CWorkerThread(); + + //----------------------------------------------------- + // + // Inter-thread communication + // + // Calls in either direction take place on the same "channel." + // Seperate functions are specified to make identities obvious + // + //----------------------------------------------------- + + // Master: Signal the thread, and block for a response + int CallWorker( unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true ); + + // Worker: Signal the thread, and block for a response + int CallMaster( unsigned, unsigned timeout = TT_INFINITE ); + + // Wait for the next request + bool WaitForCall( unsigned dwTimeout, unsigned *pResult = NULL ); + bool WaitForCall( unsigned *pResult = NULL ); + + // Is there a request? + bool PeekCall( unsigned *pParam = NULL ); + + // Reply to the request + void Reply( unsigned ); + + // Wait for a reply in the case when CallWorker() with timeout != TT_INFINITE + int WaitForReply( unsigned timeout = TT_INFINITE ); + + // If you want to do WaitForMultipleObjects you'll need to include + // this handle in your wait list or you won't be responsive + CThreadEvent& GetCallHandle(); // (returns m_EventSend) + + // Find out what the request was + unsigned GetCallParam() const; + + // Boost the worker thread to the master thread, if worker thread is lesser, return old priority + int BoostPriority(); + +protected: + int Call( unsigned, unsigned timeout, bool fBoost ); + +private: + CWorkerThread( const CWorkerThread & ); + CWorkerThread &operator=( const CWorkerThread & ); + + CThreadEvent m_EventSend; + CThreadEvent m_EventComplete; + + unsigned m_Param; + int m_ReturnVal; +}; + + +// a unidirectional message queue. A queue of type T. Not especially high speed since each message +// is malloced/freed. Note that if your message class has destructors/constructors, they MUST be +// thread safe! +template class CMessageQueue +{ + CThreadEvent SignalEvent; // signals presence of data + CThreadMutex QueueAccessMutex; + + // the parts protected by the mutex + struct MsgNode + { + MsgNode *Next; + T Data; + }; + + MsgNode *Head; + MsgNode *Tail; + +public: + CMessageQueue( void ) + { + Head = Tail = NULL; + } + + // check for a message. not 100% reliable - someone could grab the message first + bool MessageWaiting( void ) + { + return ( Head != NULL ); + } + + void WaitMessage( T *pMsg ) + { + for(;;) + { + while( ! MessageWaiting() ) + SignalEvent.Wait(); + QueueAccessMutex.Lock(); + if (! Head ) + { + // multiple readers could make this null + QueueAccessMutex.Unlock(); + continue; + } + *( pMsg ) = Head->Data; + MsgNode *remove_this = Head; + Head = Head->Next; + if (! Head) // if empty, fix tail ptr + Tail = NULL; + QueueAccessMutex.Unlock(); + delete remove_this; + break; + } + } + + void QueueMessage( T const &Msg) + { + MsgNode *new1=new MsgNode; + new1->Data=Msg; + new1->Next=NULL; + QueueAccessMutex.Lock(); + if ( Tail ) + { + Tail->Next=new1; + Tail = new1; + } + else + { + Head = new1; + Tail = new1; + } + SignalEvent.Set(); + QueueAccessMutex.Unlock(); + } +}; + + +//----------------------------------------------------------------------------- +// +// CThreadMutex. Inlining to reduce overhead and to allow client code +// to decide debug status (tracing) +// +//----------------------------------------------------------------------------- + +#ifdef MSVC +typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; +typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; + +#ifndef _X360 +extern "C" +{ + void __declspec(dllimport) __stdcall InitializeCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall EnterCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall LeaveCriticalSection(CRITICAL_SECTION *); + void __declspec(dllimport) __stdcall DeleteCriticalSection(CRITICAL_SECTION *); +}; +#endif + +//--------------------------------------------------------- + +inline void CThreadMutex::Lock() +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + uint thisThreadID = ThreadGetCurrentId(); + if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) ) + Msg( _T( "Thread %u about to wait for lock %x owned by %u\n" ), ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); +#endif + + LockSilent(); + +#ifdef THREAD_MUTEX_TRACING_ENABLED + if (m_lockCount == 0) + { + // we now own it for the first time. Set owner information + m_currentOwnerID = thisThreadID; + if ( m_bTrace ) + Msg( _T( "Thread %u now owns lock 0x%x\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + } + m_lockCount++; +#endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Unlock() +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + AssertMsg( m_lockCount >= 1, "Invalid unlock of thread lock" ); + m_lockCount--; + if (m_lockCount == 0) + { + if ( m_bTrace ) + Msg( _T( "Thread %u releasing lock 0x%x\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + m_currentOwnerID = 0; + } +#endif + UnlockSilent(); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::LockSilent() +{ + EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::UnlockSilent() +{ + LeaveCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); +} + +//--------------------------------------------------------- + +inline bool CThreadMutex::AssertOwnedByCurrentThread() +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + if (ThreadGetCurrentId() == m_currentOwnerID) + return true; + AssertMsg3( 0, "Expected thread %u as owner of lock 0x%x, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); + return false; +#else + return true; +#endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::SetTrace( bool bTrace ) +{ +#ifdef THREAD_MUTEX_TRACING_ENABLED + m_bTrace = bTrace; +#endif +} + +//--------------------------------------------------------- + +#elif defined(POSIX) + +inline CThreadMutex::CThreadMutex() +{ + // enable recursive locks as we need them + pthread_mutexattr_init( &m_Attr ); + pthread_mutexattr_settype( &m_Attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( &m_Mutex, &m_Attr ); +} + +//--------------------------------------------------------- + +inline CThreadMutex::~CThreadMutex() +{ + pthread_mutex_destroy( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Lock() +{ + pthread_mutex_lock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::Unlock() +{ + pthread_mutex_unlock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::LockSilent() +{ + pthread_mutex_lock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::UnlockSilent() +{ + pthread_mutex_unlock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline bool CThreadMutex::AssertOwnedByCurrentThread() +{ + return true; +} + +//--------------------------------------------------------- + +inline void CThreadMutex::SetTrace(bool fTrace) +{ +} + +#endif // POSIX + +//----------------------------------------------------------------------------- +// +// CThreadRWLock inline functions +// +//----------------------------------------------------------------------------- + +inline CThreadRWLock::CThreadRWLock() +: m_CanRead( true ), + m_nWriters( 0 ), + m_nActiveReaders( 0 ), + m_nPendingReaders( 0 ) +{ +} + +inline void CThreadRWLock::LockForRead() +{ + m_mutex.Lock(); + if ( m_nWriters) + { + WaitForRead(); + } + m_nActiveReaders++; + m_mutex.Unlock(); +} + +inline void CThreadRWLock::UnlockRead() +{ + m_mutex.Lock(); + m_nActiveReaders--; + if ( m_nActiveReaders == 0 && m_nWriters != 0 ) + { + m_CanWrite.Set(); + } + m_mutex.Unlock(); +} + + +//----------------------------------------------------------------------------- +// +// CThreadSpinRWLock inline functions +// +//----------------------------------------------------------------------------- + +inline bool CThreadSpinRWLock::AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ) +{ + // Note: using unions guarantees no aliasing bugs. Casting structures through *(int64*)& + // may create hard-to-catch bugs because when you do that, compiler doesn't know that the newly computed pointer + // is actually aliased with LockInfo_t structure. It's rarely a problem in practice, but when it is, it's a royal pain to debug. + return ThreadInterlockedAssignIf64( &m_lockInfo.m_i64, newValue.m_i64, comperand.m_i64 ); +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForWrite( const uint32 threadId ) +{ + // In order to grab a write lock, there can be no readers and no owners of the write lock + if ( m_lockInfo.m_nReaders > 0 || ( m_lockInfo.m_writerId && m_lockInfo.m_writerId != threadId ) ) + { + return false; + } + + static const LockInfo_t oldValue = { 0, 0 }; + LockInfo_t newValue = { threadId, 0 }; + if ( AssignIf( newValue, oldValue ) ) + { + ThreadMemoryBarrier(); + return true; + } + return false; +} + +inline bool CThreadSpinRWLock::TryLockForWrite() +{ + m_nWriters++; + if ( !TryLockForWrite( ThreadGetCurrentId() ) ) + { + m_nWriters--; + return false; + } + return true; +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForRead() +{ + if ( m_nWriters != 0 ) + { + return false; + } + // In order to grab a write lock, the number of readers must not change and no thread can own the write + LockInfo_t oldValue; + LockInfo_t newValue; + + if( IsX360() ) + { + // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360 + // WARNING: This code assumes BIG Endian CPU + oldValue.m_i64 = uint32( m_lockInfo.m_nReaders ); + newValue.m_i64 = oldValue.m_i64 + 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code + } + else + { + // this is the original code that worked here for a while + oldValue.m_nReaders = m_lockInfo.m_nReaders; + oldValue.m_writerId = 0; + newValue.m_nReaders = oldValue.m_nReaders + 1; + newValue.m_writerId = 0; + } + + if ( AssignIf( newValue, oldValue ) ) + { + ThreadMemoryBarrier(); + return true; + } + return false; +} + +inline void CThreadSpinRWLock::LockForWrite() +{ + const uint32 threadId = ThreadGetCurrentId(); + + m_nWriters++; + + if ( !TryLockForWrite( threadId ) ) + { + ThreadPause(); + SpinLockForWrite( threadId ); + } +} + +// read data from a memory address +template FORCEINLINE T ReadVolatileMemory( T const *pPtr ) +{ + volatile const T * pVolatilePtr = ( volatile const T * ) pPtr; + return *pVolatilePtr; +} + + +//----------------------------------------------------------------------------- + +#ifdef _LINUX +DLL_GLOBAL_IMPORT __thread int g_nThreadID; +#endif + +#if defined( _WIN32 ) +#pragma warning(pop) +#endif + +#endif // THREADTOOLS_H diff --git a/public/tier0/tslist.h b/public/tier0/tslist.h new file mode 100644 index 0000000..adc0843 --- /dev/null +++ b/public/tier0/tslist.h @@ -0,0 +1,808 @@ +//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +// LIFO from disassembly of Windows API and http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf +// FIFO from http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf +// +//============================================================================= + +#ifndef TSLIST_H +#define TSLIST_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#if ( defined( PLATFORM_X360 ) || defined( PLATFORM_WINDOWS_PC64 ) ) +#define USE_NATIVE_SLIST +#endif + +#if defined( USE_NATIVE_SLIST ) && !defined( _X360 ) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include "tier0/dbg.h" +#include "tier0/threadtools.h" + +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- + +#if defined(PLATFORM_WINDOWS_PC64) +#define TSLIST_HEAD_ALIGNMENT 16 //MEMORY_ALLOCATION_ALIGNMENT +#define TSLIST_NODE_ALIGNMENT 16 //MEMORY_ALLOCATION_ALIGNMENT +#else +#define TSLIST_HEAD_ALIGNMENT 8 +#define TSLIST_NODE_ALIGNMENT 8 +#endif + +#define TSLIST_HEAD_ALIGN DECL_ALIGN(TSLIST_HEAD_ALIGNMENT) +#define TSLIST_NODE_ALIGN DECL_ALIGN(TSLIST_NODE_ALIGNMENT) + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE bool RunTSQueueTests( int nListSize = 10000, int nTests = 1 ); +PLATFORM_INTERFACE bool RunTSListTests( int nListSize = 10000, int nTests = 1 ); + +//----------------------------------------------------------------------------- +// Lock free list. +//----------------------------------------------------------------------------- +//#define USE_NATIVE_SLIST + +#ifdef USE_NATIVE_SLIST +typedef SLIST_ENTRY TSLNodeBase_t; +typedef SLIST_HEADER TSLHead_t; +#else +struct TSLIST_NODE_ALIGN TSLNodeBase_t +{ + TSLNodeBase_t *Next; // name to match Windows +}; + +union TSLHead_t +{ + struct Value_t + { + TSLNodeBase_t *Next; + int16 Depth; + int16 Sequence; + } value; + + int64 value64; +}; +#endif + +//------------------------------------- + +class TSLIST_HEAD_ALIGN CTSListBase +{ +public: + CTSListBase() + { + if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( _T( "CTSListBase: Misaligned list\n" ) ); + DebuggerBreak(); + } + +#ifdef USE_NATIVE_SLIST + InitializeSListHead( &m_Head ); +#else + m_Head.value64 = (int64)0; +#endif + } + + ~CTSListBase() + { + Detach(); + } + + TSLNodeBase_t *Push( TSLNodeBase_t *pNode ) + { +#ifdef _DEBUG + if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 ) + { + Error( _T( "CTSListBase: Misaligned node\n" ) ); + DebuggerBreak(); + } +#endif + +#ifdef USE_NATIVE_SLIST +#ifdef _X360 + // integrated write-release barrier + return (TSLNodeBase_t *)InterlockedPushEntrySListRelease( &m_Head, pNode ); +#else + return (TSLNodeBase_t *)InterlockedPushEntrySList( &m_Head, pNode ); +#endif +#else + TSLHead_t oldHead; + TSLHead_t newHead; + + for (;;) + { + oldHead.value64 = m_Head.value64; + pNode->Next = oldHead.value.Next; + newHead.value.Next = pNode; + *((uint32 *)&newHead.value.Depth) = *((uint32 *)&oldHead.value.Depth) + 0x10001; + + if ( ThreadInterlockedAssignIf64( &m_Head.value64, newHead.value64, oldHead.value64 ) ) + { + break; + } + ThreadPause(); + }; + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLNodeBase_t *Pop() + { +#ifdef USE_NATIVE_SLIST +#ifdef _X360 + // integrated read-acquire barrier + TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySListAcquire( &m_Head ); +#else + TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySList( &m_Head ); +#endif + return pNode; +#else + TSLHead_t oldHead; + TSLHead_t newHead; + + for (;;) + { + oldHead.value64 = m_Head.value64; + if ( !oldHead.value.Next ) + return NULL; + + newHead.value.Next = oldHead.value.Next->Next; + *((uint32 *)&newHead.value.Depth) = *((uint32 *)&oldHead.value.Depth) - 1; + + if ( ThreadInterlockedAssignIf64( &m_Head.value64, newHead.value64, oldHead.value64 ) ) + { + break; + } + ThreadPause(); + }; + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLNodeBase_t *Detach() + { +#ifdef USE_NATIVE_SLIST + TSLNodeBase_t *pBase = (TSLNodeBase_t *)InterlockedFlushSList( &m_Head ); +#ifdef _X360 + __lwsync(); // read-acquire barrier +#endif + return pBase; +#else + TSLHead_t oldHead; + TSLHead_t newHead; + + do + { + ThreadPause(); + + oldHead.value64 = m_Head.value64; + if ( !oldHead.value.Next ) + return NULL; + + newHead.value.Next = NULL; + *((uint32 *)&newHead.value.Depth) = *((uint32 *)&oldHead.value.Depth) & 0xffff0000; + + } while( !ThreadInterlockedAssignIf64( &m_Head.value64, newHead.value64, oldHead.value64 ) ); + + return (TSLNodeBase_t *)oldHead.value.Next; +#endif + } + + TSLHead_t *AccessUnprotected() + { + return &m_Head; + } + + int Count() const + { +#ifdef USE_NATIVE_SLIST + return QueryDepthSList( const_cast( &m_Head ) ); +#else + return m_Head.value.Depth; +#endif + } + +private: + TSLHead_t m_Head; +}; + +//------------------------------------- + +template +class TSLIST_HEAD_ALIGN CTSSimpleList : public CTSListBase +{ +public: + void Push( T *pNode ) + { + Assert( sizeof(T) >= sizeof(TSLNodeBase_t) ); + CTSListBase::Push( (TSLNodeBase_t *)pNode ); + } + + T *Pop() + { + return (T *)CTSListBase::Pop(); + } +}; + +//------------------------------------- +// this is a replacement for CTSList<> and CObjectPool<> that does not +// have a per-item, per-alloc new/delete overhead +// similar to CTSSimpleList except that it allocates it's own pool objects +// and frees them on destruct. Also it does not overlay the TSNodeBase_t memory +// on T's memory +template< class T > +class TSLIST_HEAD_ALIGN CTSPool : public CTSListBase +{ + // packs the node and the item (T) into a single struct and pools those + struct TSLIST_NODE_ALIGN simpleTSPoolStruct_t : public TSLNodeBase_t + { + T elem; + }; + +public: + + ~CTSPool() + { + simpleTSPoolStruct_t *pNode = NULL; + while ( 1 ) + { + pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop(); + if ( !pNode ) + break; + delete pNode; + } + } + + void PutObject( T *pInfo ) + { + char *pElem = (char *)pInfo; + pElem -= offsetof(simpleTSPoolStruct_t,elem); + simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)pElem; + + CTSListBase::Push( pNode ); + } + + T *GetObject() + { + simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop(); + if ( !pNode ) + { + pNode = new simpleTSPoolStruct_t; + } + return &pNode->elem; + } + + // omg windows sdk - why do you #define GetObject()? + FORCEINLINE T *Get() + { + return GetObject(); + } + +}; +//------------------------------------- + +template +class TSLIST_HEAD_ALIGN CTSList : public CTSListBase +{ +public: + struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t + { + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + + T elem; + + }; + + ~CTSList() + { + Purge(); + } + + void Purge() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + } + + void RemoveAll() + { + Purge(); + } + + Node_t *Push( Node_t *pNode ) + { + return (Node_t *)CTSListBase::Push( pNode ); + } + + Node_t *Pop() + { + return (Node_t *)CTSListBase::Pop(); + } + + void PushItem( const T &init ) + { + Push( new Node_t( init ) ); + } + + bool PopItem( T *pResult) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + *pResult = pNode->elem; + delete pNode; + return true; + } + + Node_t *Detach() + { + return (Node_t *)CTSListBase::Detach(); + } + +}; + +//------------------------------------- + +template +class TSLIST_HEAD_ALIGN CTSListWithFreeList : public CTSListBase +{ +public: + struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t + { + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + + T elem; + }; + + ~CTSListWithFreeList() + { + Purge(); + } + + void Purge() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + pCurrent = (Node_t *)m_FreeList.Detach(); + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + delete pCurrent; + pCurrent = pNext; + } + } + + void RemoveAll() + { + Node_t *pCurrent = Detach(); + Node_t *pNext; + while ( pCurrent ) + { + pNext = (Node_t *)pCurrent->Next; + m_FreeList.Push( pCurrent ); + pCurrent = pNext; + } + } + + Node_t *Push( Node_t *pNode ) + { + return (Node_t *)CTSListBase::Push( pNode ); + } + + Node_t *Pop() + { + return (Node_t *)CTSListBase::Pop(); + } + + void PushItem( const T &init ) + { + Node_t *pNode = (Node_t *)m_FreeList.Pop(); + if ( !pNode ) + { + pNode = new Node_t; + } + pNode->elem = init; + Push( pNode ); + } + + bool PopItem( T *pResult) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + *pResult = pNode->elem; + m_FreeList.Push( pNode ); + return true; + } + + Node_t *Detach() + { + return (Node_t *)CTSListBase::Detach(); + } + + void FreeNode( Node_t *pNode ) + { + m_FreeList.Push( pNode ); + } + +private: + CTSListBase m_FreeList; +}; + +//----------------------------------------------------------------------------- +// Lock free queue +// +// A special consideration: the element type should be simple. This code +// actually dereferences freed nodes as part of pop, but later detects +// that. If the item in the queue is a complex type, only bad things can +// come of that. Also, therefore, if you're using Push/Pop instead of +// push item, be aware that the node memory cannot be freed until +// all threads that might have been popping have completed the pop. +// The PushItem()/PopItem() for handles this by keeping a persistent +// free list. Dont mix Push/PushItem. Note also nodes will be freed at the end, +// and are expected to have been allocated with operator new. +//----------------------------------------------------------------------------- + +template +class TSLIST_HEAD_ALIGN CTSQueue +{ +public: + struct TSLIST_NODE_ALIGN Node_t + { + Node_t() {} + Node_t( const T &init ) : elem( init ) {} + + Node_t *pNext; + T elem; + }; + + union TSLIST_HEAD_ALIGN NodeLink_t + { + struct Value_t + { + Node_t *pNode; + int32 sequence; + } value; + + int64 value64; + }; + + CTSQueue() + { + COMPILE_TIME_ASSERT( sizeof(Node_t) >= sizeof(TSLNodeBase_t) ); + if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( "CTSQueue: Misaligned queue\n" ); + DebuggerBreak(); + } + if ( ((size_t)&m_Tail) % TSLIST_HEAD_ALIGNMENT != 0 ) + { + Error( "CTSQueue: Misaligned queue\n" ); + DebuggerBreak(); + } + m_Count = 0; + m_Head.value.sequence = m_Tail.value.sequence = 0; + m_Head.value.pNode = m_Tail.value.pNode = new Node_t; // list always contains a dummy node + m_Head.value.pNode->pNext = End(); + } + + ~CTSQueue() + { + Purge(); + Assert( m_Count == 0 ); + Assert( m_Head.value.pNode == m_Tail.value.pNode ); + Assert( m_Head.value.pNode->pNext == End() ); + delete m_Head.value.pNode; + } + + // Note: Purge, RemoveAll, and Validate are *not* threadsafe + void Purge() + { + if ( IsDebug() ) + { + Validate(); + } + + Node_t *pNode; + while ( ( pNode = Pop() ) != NULL ) + { + delete pNode; + } + + while ( ( pNode = (Node_t *)m_FreeNodes.Pop() ) != NULL ) + { + delete pNode; + } + + Assert( m_Count == 0 ); + Assert( m_Head.value.pNode == m_Tail.value.pNode ); + Assert( m_Head.value.pNode->pNext == End() ); + + m_Head.value.sequence = m_Tail.value.sequence = 0; + } + + void RemoveAll() + { + if ( IsDebug() ) + { + Validate(); + } + + Node_t *pNode; + while ( ( pNode = Pop() ) != NULL ) + { + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + } + } + + bool Validate() + { + bool bResult = true; + int nNodes = 0; + if ( m_Tail.value.pNode->pNext != End() ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + + if ( m_Count == 0 ) + { + if ( m_Head.value.pNode != m_Tail.value.pNode ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + } + + Node_t *pNode = m_Head.value.pNode; + while ( pNode != End() ) + { + nNodes++; + pNode = pNode->pNext; + } + + nNodes--;// skip dummy node + + if ( nNodes != m_Count ) + { + DebuggerBreakIfDebugging(); + bResult = false; + } + + if ( !bResult ) + { + Msg( "Corrupt CTSQueueDetected" ); + } + + return bResult; + } + + void FinishPush( Node_t *pNode, const NodeLink_t &oldTail ) + { + NodeLink_t newTail; + + newTail.value.pNode = pNode; + newTail.value.sequence = oldTail.value.sequence + 1; + +#ifdef _X360 + __lwsync(); // write-release barrier +#endif + InterlockedCompareExchangeNodeLink( &m_Tail, newTail, oldTail ); + } + + Node_t *Push( Node_t *pNode ) + { +#ifdef _DEBUG + if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 ) + { + Error( "CTSListBase: Misaligned node\n" ); + DebuggerBreak(); + } +#endif + + NodeLink_t oldTail; + + pNode->pNext = End(); + + for (;;) + { + oldTail = m_Tail; + if ( InterlockedCompareExchangeNode( &(oldTail.value.pNode->pNext), pNode, End() ) == End() ) + { + break; + } + else + { + // Another thread is trying to push, help it along + FinishPush( oldTail.value.pNode->pNext, oldTail ); + } + } + + FinishPush( pNode, oldTail ); + + m_Count++; + + return oldTail.value.pNode; + } + + Node_t *Pop() + { + #define TSQUEUE_BAD_NODE_LINK ((Node_t *)0xdeadbeefULL) + NodeLink_t * volatile pHead = &m_Head; + NodeLink_t * volatile pTail = &m_Tail; + Node_t * volatile * pHeadNode = &m_Head.value.pNode; + volatile int * volatile pHeadSequence = &m_Head.value.sequence; + Node_t * volatile * pTailNode = &pTail->value.pNode; + + NodeLink_t head; + NodeLink_t newHead; + Node_t *pNext; + int tailSequence; + T elem; + + for (;;) + { + head.value.sequence = *pHeadSequence; // must grab sequence first, which allows condition below to ensure pNext is valid +#ifdef _X360 + __lwsync(); // 360 needs a barrier to prevent reordering of these assignments +#endif + head.value.pNode = *pHeadNode; + tailSequence = pTail->value.sequence; + pNext = head.value.pNode->pNext; + + if ( pNext && head.value.sequence == *pHeadSequence ) // Checking pNext only to force optimizer to not reorder the assignment to pNext and the compare of the sequence + { + if ( bTestOptimizer ) + { + if ( pNext == TSQUEUE_BAD_NODE_LINK ) + { + Msg( "Bad node link detected\n" ); + continue; + } + } + if ( head.value.pNode == *pTailNode ) + { + if ( pNext == End() ) + { + return NULL; + } + + // Another thread is trying to push, help it along + NodeLink_t &oldTail = head; // just reuse local memory for head to build old tail + oldTail.value.sequence = tailSequence; // reuse head pNode + FinishPush( pNext, oldTail ); + } + else if ( pNext != End() ) + { + elem = pNext->elem; // NOTE: next could be a freed node here, by design + newHead.value.pNode = pNext; + newHead.value.sequence = head.value.sequence + 1; + if ( InterlockedCompareExchangeNodeLink( pHead, newHead, head ) ) + { +#ifdef _X360 + __lwsync(); // read-acquire barrier +#endif + if ( bTestOptimizer ) + { + head.value.pNode->pNext = TSQUEUE_BAD_NODE_LINK; + } + break; + } + } + } + } + + m_Count--; + head.value.pNode->elem = elem; + return head.value.pNode; + } + + void FreeNode( Node_t *pNode ) + { + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + } + + void PushItem( const T &init ) + { + Node_t *pNode = (Node_t *)m_FreeNodes.Pop(); + if ( pNode ) + { + pNode->elem = init; + } + else + { + pNode = new Node_t( init ); + } + Push( pNode ); + } + + bool PopItem( T *pResult) + { + Node_t *pNode = Pop(); + if ( !pNode ) + return false; + *pResult = pNode->elem; + m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); + return true; + } + + int Count() + { + return m_Count; + } + +private: + Node_t *End() { return (Node_t *)this; } // just need a unique signifier + +#ifndef _WIN64 + Node_t *InterlockedCompareExchangeNode( Node_t * volatile *ppNode, Node_t *value, Node_t *comperand ) + { + return (Node_t *)::ThreadInterlockedCompareExchangePointer( (void **)ppNode, value, comperand ); + } + + bool InterlockedCompareExchangeNodeLink( NodeLink_t volatile *pLink, const NodeLink_t &value, const NodeLink_t &comperand ) + { + return ThreadInterlockedAssignIf64( (int64 *)pLink, value.value64, comperand.value64 ); + } + +#else + Node_t *InterlockedCompareExchangeNode( Node_t * volatile *ppNode, Node_t *value, Node_t *comperand ) + { + AUTO_LOCK( m_ExchangeMutex ); + Node_t *retVal = *ppNode; + if ( *ppNode == comperand ) + *ppNode = value; + return retVal; + } + + bool InterlockedCompareExchangeNodeLink( NodeLink_t volatile *pLink, const NodeLink_t &value, const NodeLink_t &comperand ) + { + AUTO_LOCK( m_ExchangeMutex ); + if ( pLink->value64 == comperand.value64 ) + { + pLink->value64 = value.value64; + return true; + } + return false; + } + + CThreadFastMutex m_ExchangeMutex; +#endif + + NodeLink_t m_Head; + NodeLink_t m_Tail; + + CInterlockedInt m_Count; + + CTSListBase m_FreeNodes; +}; + +#include "tier0/memdbgoff.h" + +#endif // TSLIST_H diff --git a/public/tier0/validator.h b/public/tier0/validator.h new file mode 100644 index 0000000..a3b336e --- /dev/null +++ b/public/tier0/validator.h @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include "valobject.h" + +#ifndef VALIDATOR_H +#define VALIDATOR_H + +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef DBGFLAG_VALIDATE + + +class CValidator +{ +public: + // Constructors & destructors + CValidator( void ); + ~CValidator( void ); + + // Call this each time we enter a new Validate function + void Push( tchar *pchType, void *pvObj, tchar *pchName ); + + // Call this each time we exit a Validate function + void Pop( void ); + + // Claim ownership of a memory block + void ClaimMemory( void *pvMem ); + + // Finish performing a check and perform necessary computations + void Finalize( void ); + + // Render our results to the console + void RenderObjects( int cubThreshold ); // Render all reported objects + void RenderLeaks( void ); // Render all memory leaks + + // List manipulation functions: + CValObject *FindObject( void *pvObj ); // Returns CValObject containing pvObj, or NULL. + void DiffAgainst( CValidator *pOtherValidator ); // Removes any entries from this validator that are also present in the other. + + // Accessors + bool BMemLeaks( void ) { return m_bMemLeaks; }; + CValObject *PValObjectFirst( void ) { return m_pValObjectFirst; }; + + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures + + +private: + CValObject *m_pValObjectFirst; // Linked list of all ValObjects + CValObject *m_pValObjectLast; // Last ValObject on the linked list + + CValObject *m_pValObjectCur; // Object we're current processing + + int m_cpvOwned; // Total # of blocks owned + + int m_cpubLeaked; // # of leaked memory blocks + int m_cubLeaked; // Amount of leaked memory + bool m_bMemLeaks; // Has any memory leaked? +}; + + +#endif // DBGFLAG_VALIDATE + + +#endif // VALIDATOR_H diff --git a/public/tier0/valobject.h b/public/tier0/valobject.h new file mode 100644 index 0000000..8a2cbf0 --- /dev/null +++ b/public/tier0/valobject.h @@ -0,0 +1,72 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: CValObject is used for tracking individual objects that report +// in to CValidator. Whenever a new object reports in (via CValidator::Push), +// we create a new CValObject to aggregate stats for it. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VALOBJECT_H +#define VALOBJECT_H +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef DBGFLAG_VALIDATE +class CValObject +{ +public: + // Constructors & destructors + CValObject( void ) { }; + ~CValObject( void ); + + void Init( tchar *pchType, void *pvObj, tchar *pchName, CValObject *pValObjectParent, + CValObject *pValObjectPrev ); + + // Our object has claimed ownership of a memory block + void ClaimMemoryBlock( void *pvMem ); + + // A child of ours has claimed ownership of a memory block + void ClaimChildMemoryBlock( int cubUser ); + + // Accessors + tchar *PchType( void ) { return m_rgchType; }; + void *PvObj( void ) { return m_pvObj; }; + tchar *PchName( void ) { return m_rgchName; }; + CValObject *PValObjectParent( void ) { return m_pValObjectParent; }; + int NLevel( void ) { return m_nLevel; }; + CValObject *PValObjectNext( void ) { return m_pValObjectNext; }; + int CpubMemSelf( void ) { return m_cpubMemSelf; }; + int CubMemSelf( void ) { return m_cubMemSelf; }; + int CpubMemTree( void ) { return m_cpubMemTree; }; + int CubMemTree( void ) { return m_cubMemTree; }; + int NUser( void ) { return m_nUser; }; + void SetNUser( int nUser ) { m_nUser = nUser; }; + void SetBNewSinceSnapshot( bool bNewSinceSnapshot ) { m_bNewSinceSnapshot = bNewSinceSnapshot; } + bool BNewSinceSnapshot( void ) { return m_bNewSinceSnapshot; } + +private: + bool m_bNewSinceSnapshot; // If this block is new since the snapshot. + tchar m_rgchType[64]; // Type of the object we represent + tchar m_rgchName[64]; // Name of this particular object + void *m_pvObj; // Pointer to the object we represent + + CValObject *m_pValObjectParent; // Our parent object in the tree. + int m_nLevel; // Our depth in the tree + + CValObject *m_pValObjectNext; // Next ValObject in the linked list + + int m_cpubMemSelf; // # of memory blocks we own directly + int m_cubMemSelf; // Total size of the memory blocks we own directly + + int m_cpubMemTree; // # of memory blocks owned by us and our children + int m_cubMemTree; // Total size of the memory blocks owned by us and our children + + int m_nUser; // Field provided for use by our users +}; +#endif // DBGFLAG_VALIDATE + + +#endif // VALOBJECT_H diff --git a/public/tier0/valve_off.h b/public/tier0/valve_off.h new file mode 100644 index 0000000..954a140 --- /dev/null +++ b/public/tier0/valve_off.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This turns off all Valve-specific #defines. Because we sometimes +// call external include files from inside .cpp files, we need to +// wrap those includes like this: +// #include "tier0/valve_off.h" +// #include +// #include "tier0/valve_on.h" +// +// $NoKeywords: $ +//=============================================================================// + + +#ifdef STEAM + +//----------------------------------------------------------------------------- +// Unicode-related #defines (see wchartypes.h) +//----------------------------------------------------------------------------- +#undef char + + +//----------------------------------------------------------------------------- +// Memory-related #defines +//----------------------------------------------------------------------------- +#undef malloc +#undef realloc +#undef _expand +#undef free + +#endif // STEAM + +// Allow long to be used in 3rd-party headers +#undef long \ No newline at end of file diff --git a/public/tier0/valve_on.h b/public/tier0/valve_on.h new file mode 100644 index 0000000..03ab0a7 --- /dev/null +++ b/public/tier0/valve_on.h @@ -0,0 +1,37 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: This turns on all Valve-specific #defines. Because we sometimes +// call external include files from inside .cpp files, we need to +// wrap those includes like this: +// #include "tier0/valve_off.h" +// #include +// #include "tier0/valve_on.h" +// +// $NoKeywords: $ +//=============================================================================// + + +#ifdef STEAM +//----------------------------------------------------------------------------- +// Unicode-related #defines (see wchartypes.h) +//----------------------------------------------------------------------------- +#ifdef ENFORCE_WCHAR +#define char DontUseChar_SeeWcharOn.h +#endif + + +//----------------------------------------------------------------------------- +// Memory-related #defines +//----------------------------------------------------------------------------- +#define malloc( cub ) HEY_DONT_USE_MALLOC_USE_PVALLOC +#define realloc( pvOld, cub ) HEY_DONT_USE_REALLOC_USE_PVREALLOC +#define _expand( pvOld, cub ) HEY_DONT_USE_EXPAND_USE_PVEXPAND +#define free( pv ) HEY_DONT_USE_FREE_USE_FREEPV + +#endif + + +// Long is evil because it's treated differently by different compilers +#ifdef DISALLOW_USE_OF_LONG + #define long long_is_the_devil_stop_using_it_use_int32_or_int64 +#endif diff --git a/public/tier0/vprof.h b/public/tier0/vprof.h new file mode 100644 index 0000000..d05f2fe --- /dev/null +++ b/public/tier0/vprof.h @@ -0,0 +1,1398 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Real-Time Hierarchical Profiling +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VPROF_H +#define VPROF_H + +#include "tier0/dbg.h" +#include "tier0/fasttimer.h" +#include "tier0/l2cache.h" +#include "tier0/threadtools.h" + +// VProf is enabled by default in all configurations -except- X360 Retail. +#ifndef _LINUX +#if !( defined( _X360 ) && defined( _CERT ) ) +#define VPROF_ENABLED +#endif +#endif + +#if defined(_X360) && defined(VPROF_ENABLED) +#include "tier0/pmc360.h" +#ifndef USE_PIX +#define VPROF_UNDO_PIX +#undef _PIX_H_ +#undef PIXBeginNamedEvent +#undef PIXEndNamedEvent +#undef PIXSetMarker +#undef PIXNameThread +#define USE_PIX +#include +#undef USE_PIX +#else +#include +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +// enable this to get detailed nodes beneath budget +// #define VPROF_LEVEL 1 + +// enable this to use pix (360 only) +// #define VPROF_PIX 1 + +#if defined(VPROF_PIX) +#pragma comment( lib, "Xapilibi" ) +#endif + +//----------------------------------------------------------------------------- +// +// Profiling instrumentation macros +// + +#define MAXCOUNTERS 256 + + +#ifdef VPROF_ENABLED + +#define VPROF_VTUNE_GROUP + +#define VPROF( name ) VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0) +#define VPROF_ASSERT_ACCOUNTED( name ) VPROF_(name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, true, 0) +#define VPROF_( name, detail, group, bAssertAccounted, budgetFlags ) VPROF_##detail(name,group, bAssertAccounted, budgetFlags) + +#define VPROF_BUDGET( name, group ) VPROF_BUDGET_FLAGS(name, group, BUDGETFLAG_OTHER) +#define VPROF_BUDGET_FLAGS( name, group, flags ) VPROF_(name, 0, group, false, flags) + +#define VPROF_SCOPE_BEGIN( tag ) do { VPROF( tag ) +#define VPROF_SCOPE_END() } while (0) + +#define VPROF_ONLY( expression ) expression + +#define VPROF_ENTER_SCOPE( name ) g_VProfCurrentProfile.EnterScope( name, 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false, 0 ) +#define VPROF_EXIT_SCOPE() g_VProfCurrentProfile.ExitScope() + +#define VPROF_BUDGET_GROUP_ID_UNACCOUNTED 0 + + +// Budgetgroup flags. These are used with VPROF_BUDGET_FLAGS. +// These control which budget panels the groups show up in. +// If a budget group uses VPROF_BUDGET, it gets the default +// which is BUDGETFLAG_OTHER. +#define BUDGETFLAG_CLIENT (1<<0) // Shows up in the client panel. +#define BUDGETFLAG_SERVER (1<<1) // Shows up in the server panel. +#define BUDGETFLAG_OTHER (1<<2) // Unclassified (the client shows these but the dedicated server doesn't). +#define BUDGETFLAG_HIDDEN (1<<15) +#define BUDGETFLAG_ALL 0xFFFF + + +// NOTE: You can use strings instead of these defines. . they are defined here and added +// in vprof.cpp so that they are always in the same order. +#define VPROF_BUDGETGROUP_OTHER_UNACCOUNTED _T("Unaccounted") +#define VPROF_BUDGETGROUP_WORLD_RENDERING _T("World Rendering") +#define VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING _T("Displacement_Rendering") +#define VPROF_BUDGETGROUP_GAME _T("Game") +#define VPROF_BUDGETGROUP_NPCS _T("NPCs") +#define VPROF_BUDGETGROUP_SERVER_ANIM _T("Server Animation") +#define VPROF_BUDGETGROUP_PHYSICS _T("Physics") +#define VPROF_BUDGETGROUP_STATICPROP_RENDERING _T("Static_Prop_Rendering") +#define VPROF_BUDGETGROUP_MODEL_RENDERING _T("Other_Model_Rendering") +#define VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING _T("Fast Path Model Rendering") +#define VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING _T("Brush_Model_Rendering") +#define VPROF_BUDGETGROUP_SHADOW_RENDERING _T("Shadow_Rendering") +#define VPROF_BUDGETGROUP_DETAILPROP_RENDERING _T("Detail_Prop_Rendering") +#define VPROF_BUDGETGROUP_PARTICLE_RENDERING _T("Particle/Effect_Rendering") +#define VPROF_BUDGETGROUP_ROPES _T("Ropes") +#define VPROF_BUDGETGROUP_DLIGHT_RENDERING _T("Dynamic_Light_Rendering") +#define VPROF_BUDGETGROUP_OTHER_NETWORKING _T("Networking") +#define VPROF_BUDGETGROUP_CLIENT_ANIMATION _T("Client_Animation") +#define VPROF_BUDGETGROUP_OTHER_SOUND _T("Sound") +#define VPROF_BUDGETGROUP_OTHER_VGUI _T("VGUI") +#define VPROF_BUDGETGROUP_OTHER_FILESYSTEM _T("FileSystem") +#define VPROF_BUDGETGROUP_PREDICTION _T("Prediction") +#define VPROF_BUDGETGROUP_INTERPOLATION _T("Interpolation") +#define VPROF_BUDGETGROUP_SWAP_BUFFERS _T("Swap_Buffers") +#define VPROF_BUDGETGROUP_PLAYER _T("Player") +#define VPROF_BUDGETGROUP_OCCLUSION _T("Occlusion") +#define VPROF_BUDGETGROUP_OVERLAYS _T("Overlays") +#define VPROF_BUDGETGROUP_TOOLS _T("Tools") +#define VPROF_BUDGETGROUP_LIGHTCACHE _T("Light_Cache") +#define VPROF_BUDGETGROUP_DISP_HULLTRACES _T("Displacement_Hull_Traces") +#define VPROF_BUDGETGROUP_TEXTURE_CACHE _T("Texture_Cache") +#define VPROF_BUDGETGROUP_PARTICLE_SIMULATION _T("Particle Simulation") +#define VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING _T("Flashlight Shadows") +#define VPROF_BUDGETGROUP_CLIENT_SIM _T("Client Simulation") // think functions, tempents, etc. +#define VPROF_BUDGETGROUP_STEAM _T("Steam") +#define VPROF_BUDGETGROUP_CVAR_FIND _T("Cvar_Find") +#define VPROF_BUDGETGROUP_CLIENTLEAFSYSTEM _T("ClientLeafSystem") +#define VPROF_BUDGETGROUP_JOBS_COROUTINES _T("Jobs/Coroutines") + +#ifdef _X360 +// update flags +#define VPROF_UPDATE_BUDGET 0x01 // send budget data every frame +#define VPROF_UPDATE_TEXTURE_GLOBAL 0x02 // send global texture data every frame +#define VPROF_UPDATE_TEXTURE_PERFRAME 0x04 // send perframe texture data every frame +#endif + +//------------------------------------- + +#ifndef VPROF_LEVEL +#define VPROF_LEVEL 0 +#endif + +#define VPROF_0(name,group,assertAccounted,budgetFlags) CVProfScope VProf_(name, 0, group, assertAccounted, budgetFlags); + +#if VPROF_LEVEL > 0 +#define VPROF_1(name,group,assertAccounted,budgetFlags) CVProfScope VProf_(name, 1, group, assertAccounted, budgetFlags); +#else +#define VPROF_1(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 1 +#define VPROF_2(name,group,assertAccounted,budgetFlags) CVProfScope VProf_(name, 2, group, assertAccounted, budgetFlags); +#else +#define VPROF_2(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 2 +#define VPROF_3(name,group,assertAccounted,budgetFlags) CVProfScope VProf_(name, 3, group, assertAccounted, budgetFlags); +#else +#define VPROF_3(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +#if VPROF_LEVEL > 3 +#define VPROF_4(name,group,assertAccounted,budgetFlags) CVProfScope VProf_(name, 4, group, assertAccounted, budgetFlags); +#else +#define VPROF_4(name,group,assertAccounted,budgetFlags) ((void)0) +#endif + +//------------------------------------- + +#ifdef _MSC_VER +#define VProfCode( code ) \ + if ( 0 ) \ + ; \ + else \ + { \ + VPROF( __FUNCTION__ ": " #code ); \ + code; \ + } +#else +#define VProfCode( code ) \ + if ( 0 ) \ + ; \ + else \ + { \ + VPROF( #code ); \ + code; \ + } +#endif + + +//------------------------------------- + +#define VPROF_INCREMENT_COUNTER(name,amount) do { static CVProfCounter _counter( name ); _counter.Increment( amount ); } while( 0 ) +#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount) do { static CVProfCounter _counter( name, group ); _counter.Increment( amount ); } while( 0 ) + +#else + +#define VPROF( name ) ((void)0) +#define VPROF_ASSERT_ACCOUNTED( name ) ((void)0) +#define VPROF_( name, detail, group, bAssertAccounted, budgetFlags ) ((void)0) +#define VPROF_BUDGET( name, group ) ((void)0) +#define VPROF_BUDGET_FLAGS( name, group, flags ) ((void)0) + +#define VPROF_SCOPE_BEGIN( tag ) do { +#define VPROF_SCOPE_END() } while (0) + +#define VPROF_ONLY( expression ) ((void)0) + +#define VPROF_ENTER_SCOPE( name ) +#define VPROF_EXIT_SCOPE() + +#define VPROF_INCREMENT_COUNTER(name,amount) ((void)0) +#define VPROF_INCREMENT_GROUP_COUNTER(name,group,amount) ((void)0) + +#define VPROF_TEST_SPIKE( msec ) ((void)0) + +#define VProfCode( code ) code + +#endif + +//----------------------------------------------------------------------------- + +#ifdef VPROF_ENABLED + +//----------------------------------------------------------------------------- +// +// A node in the call graph hierarchy +// + +class PLATFORM_CLASS CVProfNode +{ +friend class CVProfRecorder; +friend class CVProfile; + +public: + CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags ); + ~CVProfNode(); + + CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, int budgetFlags ); + CVProfNode *GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName ); + CVProfNode *GetParent(); + CVProfNode *GetSibling(); + CVProfNode *GetPrevSibling(); + CVProfNode *GetChild(); + + void MarkFrame(); + void ResetPeak(); + + void Pause(); + void Resume(); + void Reset(); + + void EnterScope(); + bool ExitScope(); + + const tchar *GetName(); + + int GetBudgetGroupID() + { + return m_BudgetGroupID; + } + + // Only used by the record/playback stuff. + void SetBudgetGroupID( int id ) + { + m_BudgetGroupID = id; + } + + int GetCurCalls(); + double GetCurTime(); + int GetPrevCalls(); + double GetPrevTime(); + int GetTotalCalls(); + double GetTotalTime(); + double GetPeakTime(); + + double GetCurTimeLessChildren(); + double GetPrevTimeLessChildren(); + double GetTotalTimeLessChildren(); + + int GetPrevL2CacheMissLessChildren(); + int GetPrevLoadHitStoreLessChildren(); + + void ClearPrevTime(); + + int GetL2CacheMisses(); + + // Not used in the common case... + void SetCurFrameTime( unsigned long milliseconds ); + + void SetClientData( int iClientData ) { m_iClientData = iClientData; } + int GetClientData() const { return m_iClientData; } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + + +// Used by vprof record/playback. +private: + + void SetUniqueNodeID( int id ) + { + m_iUniqueNodeID = id; + } + + int GetUniqueNodeID() const + { + return m_iUniqueNodeID; + } + + static int s_iCurrentUniqueNodeID; + + +private: + const tchar *m_pszName; + CFastTimer m_Timer; + + // L2 Cache data. + int m_iPrevL2CacheMiss; + int m_iCurL2CacheMiss; + int m_iTotalL2CacheMiss; + +#ifndef _X360 + // L2 Cache data. + CL2Cache m_L2Cache; +#else // 360: + + unsigned int m_iBitFlags; // see enum below for settings + CPMCData m_PMCData; + int m_iPrevLoadHitStores; + int m_iCurLoadHitStores; + int m_iTotalLoadHitStores; + + public: + enum FlagBits + { + kRecordL2 = 0x01, + kCPUTrace = 0x02, ///< cause a PIX trace inside this node. + }; + // call w/ true to enable L2 and LHS recording; false to turn it off + inline void EnableL2andLHS(bool enable) + { + if (enable) + m_iBitFlags |= kRecordL2; + else + m_iBitFlags &= (~kRecordL2); + } + + inline bool IsL2andLHSEnabled( void ) + { + return (m_iBitFlags & kRecordL2) != 0; + } + + int GetLoadHitStores(); + + private: + +#endif + + int m_nRecursions; + + unsigned m_nCurFrameCalls; + CCycleCount m_CurFrameTime; + + unsigned m_nPrevFrameCalls; + CCycleCount m_PrevFrameTime; + + unsigned m_nTotalCalls; + CCycleCount m_TotalTime; + + CCycleCount m_PeakTime; + + CVProfNode *m_pParent; + CVProfNode *m_pChild; + CVProfNode *m_pSibling; + + int m_BudgetGroupID; + + int m_iClientData; + int m_iUniqueNodeID; +}; + +//----------------------------------------------------------------------------- +// +// Coordinator and root node of the profile hierarchy tree +// + +enum VProfReportType_t +{ + VPRT_SUMMARY = ( 1 << 0 ), + VPRT_HIERARCHY = ( 1 << 1 ), + VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY = ( 1 << 2 ), + VPRT_LIST_BY_TIME = ( 1 << 3 ), + VPRT_LIST_BY_TIME_LESS_CHILDREN = ( 1 << 4 ), + VPRT_LIST_BY_AVG_TIME = ( 1 << 5 ), + VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN = ( 1 << 6 ), + VPRT_LIST_BY_PEAK_TIME = ( 1 << 7 ), + VPRT_LIST_BY_PEAK_OVER_AVERAGE = ( 1 << 8 ), + VPRT_LIST_TOP_ITEMS_ONLY = ( 1 << 9 ), + + VPRT_FULL = (0xffffffff & ~(VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY|VPRT_LIST_TOP_ITEMS_ONLY)), +}; + +enum CounterGroup_t +{ + COUNTER_GROUP_DEFAULT=0, + COUNTER_GROUP_NO_RESET, // The engine doesn't reset these counters. Usually, they are used + // like global variables that can be accessed across modules. + COUNTER_GROUP_TEXTURE_GLOBAL, // Global texture usage counters (totals for what is currently in memory). + COUNTER_GROUP_TEXTURE_PER_FRAME // Per-frame texture usage counters. +}; + +class PLATFORM_CLASS CVProfile +{ +public: + CVProfile(); + ~CVProfile(); + + void Term(); + + // + // Runtime operations + // + + void Start(); + void Stop(); + + void SetTargetThreadId( unsigned id ) { m_TargetThreadId = id; } + unsigned GetTargetThreadId() { return m_TargetThreadId; } + bool InTargetThread() { return ( m_TargetThreadId == ThreadGetCurrentId() ); } + +#ifdef _X360 + enum VXConsoleReportMode_t + { + VXCONSOLE_REPORT_TIME = 0, + VXCONSOLE_REPORT_L2CACHE_MISSES, + VXCONSOLE_REPORT_LOAD_HIT_STORE, + VXCONSOLE_REPORT_COUNT, + }; + + void VXProfileStart(); + void VXProfileUpdate(); + void VXEnableUpdateMode( int event, bool bEnable ); + void VXSendNodes( void ); + + void PMCDisableAllNodes(CVProfNode *pStartNode = NULL); ///< turn off l2 and lhs recording for everywhere + bool PMCEnableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node + bool PMCDisableL2Upon(const tchar *pszNodeName, bool bRecursive = false); ///< enable l2 and lhs recording for one given node + + void DumpEnabledPMCNodes( void ); + + void VXConsoleReportMode( VXConsoleReportMode_t mode ); + void VXConsoleReportScale( VXConsoleReportMode_t mode, float flScale ); + + // the CPU trace mode is actually a small state machine; it can be off, primed for + // single capture, primed for everything-in-a-frame capture, or currently in everything-in-a-frame + // capture. + enum CPUTraceState + { + kDisabled, + kFirstHitNode, // record from the first time we hit the node until that node ends + kAllNodesInFrame_WaitingForMark, // we're going to record all the times a node is hit in a frame, but are waiting for the frame to start + kAllNodesInFrame_Recording, // we're recording all hits on a node this frame. + + // Same as above, but going to record for > 1 frame + kAllNodesInFrame_WaitingForMarkMultiFrame, // we're going to record all the times a node is hit in a frame, but are waiting for the frame to start + kAllNodesInFrame_RecordingMultiFrame, + }; + + // Global switch to turn CPU tracing on or off at all. The idea is you set up a node first, + // then trigger tracing by throwing this to true. It'll reset back to false after the trace + // happens. + inline CPUTraceState GetCPUTraceMode(); + inline void SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent = false, int nNumFrames = -1 ); + inline void IncrementMultiTraceIndex(); // tick up the counter that gets appended to the multi-per-frame traces + inline unsigned int GetMultiTraceIndex(); // return the counter + void CPUTraceDisableAllNodes( CVProfNode *pStartNode = NULL ); // disable the cpu trace flag wherever it may be + CVProfNode *CPUTraceEnableForNode( const tchar *pszNodeName ); // enable cpu trace on this node only, disabling it wherever else it may be on. + CVProfNode *CPUTraceGetEnabledNode( CVProfNode *pStartNode = NULL ); // return the node enabled for CPU tracing, or NULL. + const char *GetCPUTraceFilename(); // get the filename the trace should write into. + const char *SetCPUTraceFilename( const char *filename ); // set the filename the trace should write into. (don't specify the extension; I'll do that.) + inline bool TraceCompleteEvent( void ); + +#ifdef _X360 + void LatchMultiFrame( int64 cycles ); + void SpewWorstMultiFrame(); +#endif + +#endif + + void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted ); + void EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ); + void ExitScope(); + + void MarkFrame(); + void ResetPeaks(); + + void Pause(); + void Resume(); + void Reset(); + + bool IsEnabled() const; + int GetDetailLevel() const; + + bool AtRoot() const; + + // + // Queries + // + +#ifdef VPROF_VTUNE_GROUP +# define MAX_GROUP_STACK_DEPTH 1024 + + void EnableVTuneGroup( const tchar *pGroupName ) + { + m_nVTuneGroupID = BudgetGroupNameToBudgetGroupID( pGroupName ); + m_bVTuneGroupEnabled = true; + } + void DisableVTuneGroup( void ) + { + m_bVTuneGroupEnabled = false; + } + + inline void PushGroup( int nGroupID ); + inline void PopGroup( void ); +#endif + + int NumFramesSampled() { return m_nFrames; } + double GetPeakFrameTime(); + double GetTotalTimeSampled(); + double GetTimeLastFrame(); + + CVProfNode *GetRoot(); + CVProfNode *FindNode( CVProfNode *pStartNode, const tchar *pszNode ); + + void OutputReport( int type = VPRT_FULL, const tchar *pszStartNode = NULL, int budgetGroupID = -1 ); + + const tchar *GetBudgetGroupName( int budgetGroupID ); + int GetBudgetGroupFlags( int budgetGroupID ) const; // Returns a combination of BUDGETFLAG_ defines. + int GetNumBudgetGroups( void ); + void GetBudgetGroupColor( int budgetGroupID, int &r, int &g, int &b, int &a ); + int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName ); + int BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName, int budgetFlagsToORIn ); + void RegisterNumBudgetGroupsChangedCallBack( void (*pCallBack)(void) ); + + int BudgetGroupNameToBudgetGroupIDNoCreate( const tchar *pBudgetGroupName ) { return FindBudgetGroupName( pBudgetGroupName ); } + + void HideBudgetGroup( int budgetGroupID, bool bHide = true ); + void HideBudgetGroup( const tchar *pszName, bool bHide = true ) { HideBudgetGroup( BudgetGroupNameToBudgetGroupID( pszName), bHide ); } + + int *FindOrCreateCounter( const tchar *pName, CounterGroup_t eCounterGroup=COUNTER_GROUP_DEFAULT ); + void ResetCounters( CounterGroup_t eCounterGroup ); + + int GetNumCounters( void ) const; + + const tchar *GetCounterName( int index ) const; + int GetCounterValue( int index ) const; + const tchar *GetCounterNameAndValue( int index, int &val ) const; + CounterGroup_t GetCounterGroup( int index ) const; + + // Performance monitoring events. + void PMEInitialized( bool bInit ) { m_bPMEInit = bInit; } + void PMEEnable( bool bEnable ) { m_bPMEEnabled = bEnable; } + +#ifndef _X360 + bool UsePME( void ) { return ( m_bPMEInit && m_bPMEEnabled ); } +#else + bool UsePME( void ) { return ( CPMCData::IsInitialized() && m_bPMEEnabled ); } +#endif + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, tchar *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +protected: + + void FreeNodes_R( CVProfNode *pNode ); + +#ifdef VPROF_VTUNE_GROUP + bool VTuneGroupEnabled() + { + return m_bVTuneGroupEnabled; + } + int VTuneGroupID() + { + return m_nVTuneGroupID; + } +#endif + + void SumTimes( const tchar *pszStartNode, int budgetGroupID ); + void SumTimes( CVProfNode *pNode, int budgetGroupID ); + void DumpNodes( CVProfNode *pNode, int indent, bool bAverageAndCountOnly ); + int FindBudgetGroupName( const tchar *pBudgetGroupName ); + int AddBudgetGroupName( const tchar *pBudgetGroupName, int budgetFlags ); + +#ifdef VPROF_VTUNE_GROUP + bool m_bVTuneGroupEnabled; + int m_nVTuneGroupID; + int m_GroupIDStack[MAX_GROUP_STACK_DEPTH]; + int m_GroupIDStackDepth; +#endif + int m_enabled; + bool m_fAtRoot; // tracked for efficiency of the "not profiling" case + CVProfNode *m_pCurNode; + CVProfNode m_Root; + int m_nFrames; + int m_ProfileDetailLevel; + int m_pausedEnabledDepth; + + class CBudgetGroup + { + public: + tchar *m_pName; + int m_BudgetFlags; + }; + + CBudgetGroup *m_pBudgetGroups; + int m_nBudgetGroupNamesAllocated; + int m_nBudgetGroupNames; + void (*m_pNumBudgetGroupsChangedCallBack)(void); + + // Performance monitoring events. + bool m_bPMEInit; + bool m_bPMEEnabled; + + int m_Counters[MAXCOUNTERS]; + char m_CounterGroups[MAXCOUNTERS]; // (These are CounterGroup_t's). + tchar *m_CounterNames[MAXCOUNTERS]; + int m_NumCounters; + +#ifdef _X360 + int m_UpdateMode; + CPUTraceState m_iCPUTraceEnabled; + int m_nFramesRemaining; + int m_nFrameCount; + int64 m_WorstCycles; + char m_WorstTraceFilename[128]; + char m_CPUTraceFilename[128]; + unsigned int m_iSuccessiveTraceIndex; + VXConsoleReportMode_t m_ReportMode; + float m_pReportScale[VXCONSOLE_REPORT_COUNT]; + bool m_bTraceCompleteEvent; +#endif + + unsigned m_TargetThreadId; +}; + +//------------------------------------- + +PLATFORM_INTERFACE CVProfile g_VProfCurrentProfile; + + +//----------------------------------------------------------------------------- + +PLATFORM_INTERFACE bool g_VProfSignalSpike; + +class CVProfSpikeDetector +{ +public: + CVProfSpikeDetector( float spike ) : + m_timeLast( GetTimeLast() ) + { + m_spike = spike; + m_Timer.Start(); + } + + ~CVProfSpikeDetector() + { + m_Timer.End(); + if ( Plat_FloatTime() - m_timeLast > 2.0 ) + { + m_timeLast = Plat_FloatTime(); + if ( m_Timer.GetDuration().GetMillisecondsF() > m_spike ) + { + g_VProfSignalSpike = true; + } + } + } + +private: + static float &GetTimeLast() { static float timeLast = 0; return timeLast; } + CFastTimer m_Timer; + float m_spike; + float &m_timeLast; +}; + + +// Macro to signal a local spike. Meant as temporary instrumentation, do not leave in code +#define VPROF_TEST_SPIKE( msec ) CVProfSpikeDetector UNIQUE_ID( msec ) + +//----------------------------------------------------------------------------- + +#ifdef VPROF_VTUNE_GROUP +inline void CVProfile::PushGroup( int nGroupID ) +{ + // There is always at least one item on the stack since we force + // the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED. + Assert( m_GroupIDStackDepth > 0 ); + Assert( m_GroupIDStackDepth < MAX_GROUP_STACK_DEPTH ); + m_GroupIDStack[m_GroupIDStackDepth] = nGroupID; + m_GroupIDStackDepth++; + if( m_GroupIDStack[m_GroupIDStackDepth-2] != nGroupID && + VTuneGroupEnabled() && + nGroupID == VTuneGroupID() ) + { + vtune( true ); + } +} +#endif // VPROF_VTUNE_GROUP + +#ifdef VPROF_VTUNE_GROUP +inline void CVProfile::PopGroup( void ) +{ + m_GroupIDStackDepth--; + // There is always at least one item on the stack since we force + // the first element to be VPROF_BUDGETGROUP_OTHER_UNACCOUNTED. + Assert( m_GroupIDStackDepth > 0 ); + if( m_GroupIDStack[m_GroupIDStackDepth] != m_GroupIDStack[m_GroupIDStackDepth+1] && + VTuneGroupEnabled() && + m_GroupIDStack[m_GroupIDStackDepth+1] == VTuneGroupID() ) + { + vtune( false ); + } +} +#endif // VPROF_VTUNE_GROUP + +//----------------------------------------------------------------------------- + +class CVProfScope +{ +public: + CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ); + ~CVProfScope(); +}; + +//----------------------------------------------------------------------------- +// +// CVProfNode, inline methods +// + +inline CVProfNode::CVProfNode( const tchar * pszName, int detailLevel, CVProfNode *pParent, const tchar *pBudgetGroupName, int budgetFlags ) + : m_pszName( pszName ), + m_nCurFrameCalls( 0 ), + m_nPrevFrameCalls( 0 ), + m_nRecursions( 0 ), + m_pParent( pParent ), + m_pChild( NULL ), + m_pSibling( NULL ), + m_iClientData( -1 ) +#ifdef _X360 + , m_iBitFlags( 0 ) +#endif +{ + m_iUniqueNodeID = s_iCurrentUniqueNodeID++; + + if ( m_iUniqueNodeID > 0 ) + { + m_BudgetGroupID = g_VProfCurrentProfile.BudgetGroupNameToBudgetGroupID( pBudgetGroupName, budgetFlags ); + } + else + { + m_BudgetGroupID = 0; // "m_Root" can't call BudgetGroupNameToBudgetGroupID because g_VProfCurrentProfile not yet initialized + } + + Reset(); + + if( m_pParent && ( m_BudgetGroupID == VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) ) + { + m_BudgetGroupID = m_pParent->GetBudgetGroupID(); + } +} + + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetParent() +{ + Assert( m_pParent ); + return m_pParent; +} + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetSibling() +{ + return m_pSibling; +} + +//------------------------------------- +// Hacky way to the previous sibling, only used from vprof panel at the moment, +// so it didn't seem like it was worth the memory waste to add the reverse +// link per node. + +inline CVProfNode *CVProfNode::GetPrevSibling() +{ + CVProfNode* p = GetParent(); + + if(!p) + return NULL; + + CVProfNode* s; + for( s = p->GetChild(); + s && ( s->GetSibling() != this ); + s = s->GetSibling() ) + ; + + return s; +} + +//------------------------------------- + +inline CVProfNode *CVProfNode::GetChild() +{ + return m_pChild; +} + +//------------------------------------- + +inline const tchar *CVProfNode::GetName() +{ + Assert( m_pszName ); + return m_pszName; +} + +//------------------------------------- + +inline int CVProfNode::GetTotalCalls() +{ + return m_nTotalCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetTotalTime() +{ + return m_TotalTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline int CVProfNode::GetCurCalls() +{ + return m_nCurFrameCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetCurTime() +{ + return m_CurFrameTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline int CVProfNode::GetPrevCalls() +{ + return m_nPrevFrameCalls; +} + +//------------------------------------- + +inline double CVProfNode::GetPrevTime() +{ + return m_PrevFrameTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline double CVProfNode::GetPeakTime() +{ + return m_PeakTime.GetMillisecondsF(); +} + +//------------------------------------- + +inline double CVProfNode::GetTotalTimeLessChildren() +{ + double result = GetTotalTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetTotalTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +//------------------------------------- + +inline double CVProfNode::GetCurTimeLessChildren() +{ + double result = GetCurTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetCurTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +inline double CVProfNode::GetPrevTimeLessChildren() +{ + double result = GetPrevTime(); + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->GetPrevTime(); + pChild = pChild->GetSibling(); + } + return result; +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetPrevL2CacheMissLessChildren() +{ + int result = m_iPrevL2CacheMiss; + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->m_iPrevL2CacheMiss; + pChild = pChild->GetSibling(); + } + return result; +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetPrevLoadHitStoreLessChildren() +{ +#ifndef _X360 + return 0; +#else + int result = m_iPrevLoadHitStores; + CVProfNode *pChild = GetChild(); + while ( pChild ) + { + result -= pChild->m_iPrevLoadHitStores; + pChild = pChild->GetSibling(); + } + return result; +#endif +} + + +//----------------------------------------------------------------------------- +inline void CVProfNode::ClearPrevTime() +{ + m_PrevFrameTime.Init(); +} + +//----------------------------------------------------------------------------- +inline int CVProfNode::GetL2CacheMisses( void ) +{ +#ifndef _X360 + return m_L2Cache.GetL2CacheMisses(); +#else + return m_iTotalL2CacheMiss; +#endif +} + +#ifdef _X360 +inline int CVProfNode::GetLoadHitStores( void ) +{ + return m_iTotalLoadHitStores; +} +#endif + +//----------------------------------------------------------------------------- +// +// CVProfile, inline methods +// + +//------------------------------------- + +inline bool CVProfile::IsEnabled() const +{ + return ( m_enabled != 0 ); +} + +//------------------------------------- + +inline int CVProfile::GetDetailLevel() const +{ + return m_ProfileDetailLevel; +} + + +//------------------------------------- + +inline bool CVProfile::AtRoot() const +{ + return m_fAtRoot; +} + +//------------------------------------- + +inline void CVProfile::Start() +{ + if ( ++m_enabled == 1 ) + { + m_Root.EnterScope(); +#ifdef _X360 + VXProfileStart(); + CPMCData::InitializeOnceProgramWide(); +#endif + } +} + +//------------------------------------- + +inline void CVProfile::Stop() +{ + if ( --m_enabled == 0 ) + m_Root.ExitScope(); +} + +//------------------------------------- + +inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ) +{ + if ( ( m_enabled != 0 || !m_fAtRoot ) && InTargetThread() ) // if became disabled, need to unwind back to root before stopping + { + // Only account for vprof stuff on the primary thread. + //if( !Plat_IsPrimaryThread() ) + // return; + + if ( pszName != m_pCurNode->GetName() ) + { + m_pCurNode = m_pCurNode->GetSubNode( pszName, detailLevel, pBudgetGroupName, budgetFlags ); + } + m_pBudgetGroups[m_pCurNode->GetBudgetGroupID()].m_BudgetFlags |= budgetFlags; + +#if defined( _DEBUG ) && !defined( _X360 ) + // 360 doesn't want this to allow tier0 debug/release .def files to match + if ( bAssertAccounted ) + { + // FIXME + AssertOnce( m_pCurNode->GetBudgetGroupID() != 0 ); + } +#endif + m_pCurNode->EnterScope(); + m_fAtRoot = false; + } +#if defined(_X360) && defined(VPROF_PIX) + if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) + PIXBeginNamedEvent( 0, pszName ); +#endif +} + +inline void CVProfile::EnterScope( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted ) +{ + EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, BUDGETFLAG_OTHER ); +} + +//------------------------------------- + +inline void CVProfile::ExitScope() +{ +#if defined(_X360) && defined(VPROF_PIX) +#ifdef PIXBeginNamedEvent +#error +#endif + if ( m_pCurNode->GetBudgetGroupID() != VPROF_BUDGET_GROUP_ID_UNACCOUNTED ) + PIXEndNamedEvent(); +#endif + if ( ( !m_fAtRoot || m_enabled != 0 ) && InTargetThread() ) + { + // Only account for vprof stuff on the primary thread. + //if( !Plat_IsPrimaryThread() ) + // return; + + // ExitScope will indicate whether we should back up to our parent (we may + // be profiling a recursive function) + if (m_pCurNode->ExitScope()) + { + m_pCurNode = m_pCurNode->GetParent(); + } + m_fAtRoot = ( m_pCurNode == &m_Root ); + } +} + +//------------------------------------- + +inline void CVProfile::Pause() +{ + m_pausedEnabledDepth = m_enabled; + m_enabled = 0; + if ( !AtRoot() ) + m_Root.Pause(); +} + +//------------------------------------- + +inline void CVProfile::Resume() +{ + m_enabled = m_pausedEnabledDepth; + if ( !AtRoot() ) + m_Root.Resume(); +} + +//------------------------------------- + +inline void CVProfile::Reset() +{ + m_Root.Reset(); + m_nFrames = 0; +} + +//------------------------------------- + +inline void CVProfile::ResetPeaks() +{ + m_Root.ResetPeak(); +} + +//------------------------------------- + +inline void CVProfile::MarkFrame() +{ + if ( m_enabled ) + { + ++m_nFrames; + m_Root.ExitScope(); + m_Root.MarkFrame(); + m_Root.EnterScope(); + +#ifdef _X360 + // update the CPU trace state machine if enabled + switch ( GetCPUTraceMode() ) + { + case kAllNodesInFrame_WaitingForMark: + // mark! Start recording a zillion traces. + m_iCPUTraceEnabled = kAllNodesInFrame_Recording; + break; + case kAllNodesInFrame_WaitingForMarkMultiFrame: + m_iCPUTraceEnabled = kAllNodesInFrame_RecordingMultiFrame; + break; + case kAllNodesInFrame_Recording: + // end of frame. stop recording if no more frames needed + m_iCPUTraceEnabled = kDisabled; + Msg("Frame ended. Recording no more CPU traces\n"); + + break; + case kAllNodesInFrame_RecordingMultiFrame: + // end of frame. stop recording if no more frames needed + if ( --m_nFramesRemaining == 0 ) + { + m_iCPUTraceEnabled = kDisabled; + Msg("Frames ended. Recording no more CPU traces\n"); + + SpewWorstMultiFrame(); + } + + ++m_nFrameCount; + + break; + default: + // no default + break; + } +#endif + } +} + +//------------------------------------- + +inline double CVProfile::GetTotalTimeSampled() +{ + return m_Root.GetTotalTime(); +} + +//------------------------------------- + +inline double CVProfile::GetPeakFrameTime() +{ + return m_Root.GetPeakTime(); +} + +//------------------------------------- + +inline double CVProfile::GetTimeLastFrame() +{ + return m_Root.GetCurTime(); +} + +//------------------------------------- + +inline CVProfNode *CVProfile::GetRoot() +{ + return &m_Root; +} + + +inline const tchar *CVProfile::GetBudgetGroupName( int budgetGroupID ) +{ + Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames ); + return m_pBudgetGroups[budgetGroupID].m_pName; +} + +inline int CVProfile::GetBudgetGroupFlags( int budgetGroupID ) const +{ + Assert( budgetGroupID >= 0 && budgetGroupID < m_nBudgetGroupNames ); + return m_pBudgetGroups[budgetGroupID].m_BudgetFlags; +} + +#ifdef _X360 + +inline CVProfile::CPUTraceState CVProfile::GetCPUTraceMode() +{ + return m_iCPUTraceEnabled; +} + +inline void CVProfile::SetCPUTraceEnabled( CPUTraceState enabled, bool bTraceCompleteEvent /*=true*/, int nNumFrames /*= -1*/ ) +{ + m_iCPUTraceEnabled = enabled; + m_bTraceCompleteEvent = bTraceCompleteEvent; + if ( nNumFrames != -1 ) + { + m_nFramesRemaining = nNumFrames; + m_nFrameCount = 0; + m_WorstCycles = 0; + m_WorstTraceFilename[ 0 ] = 0; + } +} + +inline void CVProfile::IncrementMultiTraceIndex() +{ + ++m_iSuccessiveTraceIndex; +} + +inline unsigned int CVProfile::GetMultiTraceIndex() +{ + return m_iSuccessiveTraceIndex; +} + +#endif + + +//----------------------------------------------------------------------------- + +inline CVProfScope::CVProfScope( const tchar * pszName, int detailLevel, const tchar *pBudgetGroupName, bool bAssertAccounted, int budgetFlags ) +{ + g_VProfCurrentProfile.EnterScope( pszName, detailLevel, pBudgetGroupName, bAssertAccounted, budgetFlags ); +} + +//------------------------------------- + +inline CVProfScope::~CVProfScope() +{ + g_VProfCurrentProfile.ExitScope(); +} + +class CVProfCounter +{ +public: + CVProfCounter( const tchar *pName, CounterGroup_t group=COUNTER_GROUP_DEFAULT ) + { + m_pCounter = g_VProfCurrentProfile.FindOrCreateCounter( pName, group ); + Assert( m_pCounter ); + } + ~CVProfCounter() + { + } + void Increment( int val ) + { + Assert( m_pCounter ); + *m_pCounter += val; + } +private: + int *m_pCounter; +}; + +#endif + +#ifdef _X360 + +#include "xbox/xbox_console.h" +#include "tracerecording.h" +#include "tier1/fmtstr.h" +#pragma comment( lib, "tracerecording.lib" ) +#pragma comment( lib, "xbdm.lib" ) + +class CPIXRecorder +{ +public: + CPIXRecorder() : m_bActive( false ) {} + ~CPIXRecorder() { Stop(); } + + void Start( const char *pszFilename = "capture" ) + { + if ( !m_bActive ) + { + if ( !XTraceStartRecording( CFmtStr( "e:\\%s.pix2", pszFilename ) ) ) + { + Msg( "XTraceStartRecording failed, error code %d\n", GetLastError() ); + } + else + { + m_bActive = true; + } + } + } + + void Stop() + { + if ( m_bActive ) + { + m_bActive = false; + if ( XTraceStopRecording() ) + { + Msg( "CPU trace finished.\n" ); + // signal VXConsole that trace is completed + XBX_rTraceComplete(); + } + } + } + +private: + bool m_bActive; +}; + +#define VPROF_BEGIN_PIX_BLOCK( convar ) \ + { \ + bool bRunPix = 0; \ + static CFastTimer PIXTimer; \ + extern ConVar convar; \ + ConVar &PIXConvar = convar; \ + CPIXRecorder PIXRecorder; \ + { \ + PIXLabel: \ + if ( bRunPix ) \ + { \ + PIXRecorder.Start(); \ + } \ + else \ + { \ + if ( PIXConvar.GetBool() ) \ + { \ + PIXTimer.Start(); \ + } \ + } \ + { + + +#define VPROF_END_PIX_BLOCK() \ + } \ + \ + if ( !bRunPix ) \ + { \ + if ( PIXConvar.GetBool() ) \ + { \ + PIXTimer.End(); \ + if ( PIXTimer.GetDuration().GetMillisecondsF() > PIXConvar.GetFloat() ) \ + { \ + PIXConvar.SetValue( 0 ); \ + bRunPix = true; \ + goto PIXLabel; \ + } \ + } \ + } \ + else \ + { \ + PIXRecorder.Stop(); \ + } \ + } \ + } +#else +#define VPROF_BEGIN_PIX_BLOCK( PIXConvar ) { +#define VPROF_END_PIX_BLOCK() } +#endif + + +#ifdef VPROF_UNDO_PIX +#undef USE_PIX +#undef _PIX_H_ +#undef PIXBeginNamedEvent +#undef PIXEndNamedEvent +#undef PIXSetMarker +#undef PIXNameThread +#include +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +//============================================================================= diff --git a/public/tier0/wchartypes.h b/public/tier0/wchartypes.h new file mode 100644 index 0000000..9469c0f --- /dev/null +++ b/public/tier0/wchartypes.h @@ -0,0 +1,101 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: All of our code is completely Unicode. Instead of char, you should +// use wchar, uint8, or char8, as explained below. +// +// $NoKeywords: $ +//=============================================================================// + + +#ifndef WCHARTYPES_H +#define WCHARTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#ifdef _INC_TCHAR +#error ("Must include tier0 type headers before tchar.h") +#endif + +// Temporarily turn off Valve defines +#include "tier0/valve_off.h" + +#if !defined(_WCHAR_T_DEFINED) && !defined( __WCHAR_TYPE__ ) && !defined(GNUC) +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif + +// char8 +// char8 is equivalent to char, and should be used when you really need a char +// (for example, when calling an external function that's declared to take +// chars). +typedef char char8; + +// uint8 +// uint8 is equivalent to byte (but is preferred over byte for clarity). Use this +// whenever you mean a byte (for example, one byte of a network packet). +typedef unsigned char uint8; +typedef unsigned char BYTE; +typedef unsigned char byte; + +// wchar +// wchar is a single character of text (currently 16 bits, as all of our text is +// Unicode). Use this whenever you mean a piece of text (for example, in a string). +typedef wchar_t wchar; +//typedef char wchar; + +// __WFILE__ +// This is a Unicode version of __FILE__ +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define __WFILE__ WIDEN(__FILE__) + +#ifdef STEAM +#ifndef _UNICODE +#define FORCED_UNICODE +#endif +#define _UNICODE +#endif + +#if defined( POSIX ) +#define _tcsstr strstr +#define _tcsicmp stricmp +#define _tcscmp strcmp +#define _tcscpy strcpy +#define _tcsncpy strncpy +#define _tcsrchr strrchr +#define _tcslen strlen +#define _tfopen fopen +#define _stprintf sprintf +#define _ftprintf fprintf +#define _vsntprintf _vsnprintf +#define _tprintf printf +#define _sntprintf _snprintf +#define _T(s) s +#else +#include +#endif + +#if defined(_UNICODE) +typedef wchar tchar; +#define tstring wstring +#define __TFILE__ __WFILE__ +#define TCHAR_IS_WCHAR +#else +typedef char tchar; +#define tstring string +#define __TFILE__ __FILE__ +#define TCHAR_IS_CHAR +#endif + +#ifdef FORCED_UNICODE +#undef _UNICODE +#endif + +// Turn valve defines back on +#include "tier0/valve_on.h" + + +#endif // WCHARTYPES + + diff --git a/public/tier0/win32consoleio.h b/public/tier0/win32consoleio.h new file mode 100644 index 0000000..92b3914 --- /dev/null +++ b/public/tier0/win32consoleio.h @@ -0,0 +1,32 @@ +//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ====== +// +// Purpose: Win32 Console API helpers +// +//============================================================================= +#ifndef WIN32_CONSOLE_IO_H +#define WIN32_CONSOLE_IO_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +// Function to attach a console for I/O to a Win32 GUI application in a reasonably smart fashion. +PLATFORM_INTERFACE bool SetupWin32ConsoleIO(); + +// Win32 Console Color API Helpers, originally from cmdlib. + +struct Win32ConsoleColorContext_t +{ + int m_InitialColor; + uint16 m_LastColor; + uint16 m_BadColor; + uint16 m_BackgroundFlags; +}; + +PLATFORM_INTERFACE void InitWin32ConsoleColorContext( Win32ConsoleColorContext_t *pContext ); + +PLATFORM_INTERFACE uint16 SetWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, int nRed, int nGreen, int nBlue, int nIntensity ); + +PLATFORM_INTERFACE void RestoreWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, uint16 prevColor ); + +#endif diff --git a/public/tier0/xbox_codeline_defines.h b/public/tier0/xbox_codeline_defines.h new file mode 100644 index 0000000..3f7b544 --- /dev/null +++ b/public/tier0/xbox_codeline_defines.h @@ -0,0 +1,16 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef XBOX_CODELINE_DEFINES_H +#define XBOX_CODELINE_DEFINES_H + + +// In the regular src_main codeline, we leave this out. +//#define IN_XBOX_CODELINE + + +#endif // XBOX_CODELINE_DEFINES_H diff --git a/public/tier1/CommandBuffer.h b/public/tier1/CommandBuffer.h new file mode 100644 index 0000000..a918963 --- /dev/null +++ b/public/tier1/CommandBuffer.h @@ -0,0 +1,160 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + + +#ifndef COMMANDBUFFER_H +#define COMMANDBUFFER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utllinkedlist.h" +#include "tier1/convar.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; + + +//----------------------------------------------------------------------------- +// Invalid command handle +//----------------------------------------------------------------------------- +typedef int CommandHandle_t; +enum +{ + COMMAND_BUFFER_INVALID_COMMAND_HANDLE = 0 +}; + + +//----------------------------------------------------------------------------- +// A command buffer class- a queue of argc/argv based commands associated +// with a particular time +//----------------------------------------------------------------------------- +class CCommandBuffer +{ +public: + // Constructor, destructor + CCommandBuffer( ); + ~CCommandBuffer(); + + // Inserts text into the command buffer + bool AddText( const char *pText, int nTickDelay = 0 ); + + // Used to iterate over all commands appropriate for the current time + void BeginProcessingCommands( int nDeltaTicks ); + bool DequeueNextCommand( ); + int DequeueNextCommand( const char **& ppArgv ); + int ArgC() const; + const char **ArgV() const; + const char *ArgS() const; // All args that occur after the 0th arg, in string form + const char *GetCommandString() const; // The entire command in string form, including the 0th arg + const CCommand& GetCommand() const; + void EndProcessingCommands(); + + // Are we in the middle of processing commands? + bool IsProcessingCommands(); + + // Delays all queued commands to execute at a later time + void DelayAllQueuedCommands( int nTickDelay ); + + // Indicates how long to delay when encoutering a 'wait' command + void SetWaitDelayTime( int nTickDelay ); + + // Returns a handle to the next command to process + // (useful when inserting commands into the buffer during processing + // of commands to force immediate execution of those commands, + // most relevantly, to implement a feature where you stream a file + // worth of commands into the buffer, where the file size is too large + // to entirely contain in the buffer). + CommandHandle_t GetNextCommandHandle(); + + // Specifies a max limit of the args buffer. For unittesting. Size == 0 means use default + void LimitArgumentBufferSize( int nSize ); + +private: + enum + { + ARGS_BUFFER_LENGTH = 8192, + }; + + struct Command_t + { + int m_nTick; + int m_nFirstArgS; + int m_nBufferSize; + }; + + // Insert a command into the command queue at the appropriate time + void InsertCommandAtAppropriateTime( int hCommand ); + + // Insert a command into the command queue + // Only happens if it's inserted while processing other commands + void InsertImmediateCommand( int hCommand ); + + // Insert a command into the command queue + bool InsertCommand( const char *pArgS, int nCommandSize, int nTick ); + + // Returns the length of the next command, as well as the offset to the next command + void GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset ); + + // Compacts the command buffer + void Compact(); + + // Parses argv0 out of the buffer + bool ParseArgV0( CUtlBuffer &buf, char *pArgv0, int nMaxLen, const char **pArgs ); + + char m_pArgSBuffer[ ARGS_BUFFER_LENGTH ]; + int m_nLastUsedArgSSize; + int m_nArgSBufferSize; + CUtlFixedLinkedList< Command_t > m_Commands; + int m_nCurrentTick; + int m_nLastTickToProcess; + int m_nWaitDelayTicks; + int m_hNextCommand; + int m_nMaxArgSBufferLength; + bool m_bIsProcessingCommands; + + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + CCommand m_CurrentCommand; +}; + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +inline int CCommandBuffer::ArgC() const +{ + return m_CurrentCommand.ArgC(); +} + +inline const char **CCommandBuffer::ArgV() const +{ + return m_CurrentCommand.ArgV(); +} + +inline const char *CCommandBuffer::ArgS() const +{ + return m_CurrentCommand.ArgS(); +} + +inline const char *CCommandBuffer::GetCommandString() const +{ + return m_CurrentCommand.GetCommandString(); +} + +inline const CCommand& CCommandBuffer::GetCommand() const +{ + return m_CurrentCommand; +} + +#endif // COMMANDBUFFER_H diff --git a/public/tier1/KeyValues.h b/public/tier1/KeyValues.h new file mode 100644 index 0000000..745d8d3 --- /dev/null +++ b/public/tier1/KeyValues.h @@ -0,0 +1,474 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef KEYVALUES_H +#define KEYVALUES_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#include "utlvector.h" +#include "color.h" +#include "exprevaluator.h" + +class IBaseFileSystem; +class CUtlBuffer; +class Color; +class KeyValues; +class IKeyValuesDumpContext; +typedef void * FileHandle_t; + +// single byte identifies a xbox kv file in binary format +// strings are pooled from a searchpath/zip mounted symbol table +#define KV_BINARY_POOLED_FORMAT 0xAA + + +#define FOR_EACH_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextKey() ) + +#define FOR_EACH_TRUE_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstTrueSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextTrueSubKey() ) + +#define FOR_EACH_VALUE( kvRoot, kvValue ) \ + for ( KeyValues * kvValue = kvRoot->GetFirstValue(); kvValue != NULL; kvValue = kvValue->GetNextValue() ) + + +//----------------------------------------------------------------------------- +// Purpose: Simple recursive data access class +// Used in vgui for message parameters and resource files +// Destructor deletes all child KeyValues nodes +// Data is stored in key (string names) - (string/int/float)value pairs called nodes. +// +// About KeyValues Text File Format: + +// It has 3 control characters '{', '}' and '"'. Names and values may be quoted or +// not. The quote '"' charater must not be used within name or values, only for +// quoting whole tokens. You may use escape sequences wile parsing and add within a +// quoted token a \" to add quotes within your name or token. When using Escape +// Sequence the parser must now that by setting KeyValues::UsesEscapeSequences( true ), +// which it's off by default. Non-quoted tokens ends with a whitespace, '{', '}' and '"'. +// So you may use '{' and '}' within quoted tokens, but not for non-quoted tokens. +// An open bracket '{' after a key name indicates a list of subkeys which is finished +// with a closing bracket '}'. Subkeys use the same definitions recursively. +// Whitespaces are space, return, newline and tabulator. Allowed Escape sequences +// are \n, \t, \\, \n and \". The number character '#' is used for macro purposes +// (eg #include), don't use it as first charater in key names. +//----------------------------------------------------------------------------- +class KeyValues +{ +public: + KeyValues( const char *setName ); + + // + // AutoDelete class to automatically free the keyvalues. + // Simply construct it with the keyvalues you allocated and it will free them when falls out of scope. + // When you decide that keyvalues shouldn't be deleted call Assign(NULL) on it. + // If you constructed AutoDelete(NULL) you can later assign the keyvalues to be deleted with Assign(pKeyValues). + // + class AutoDelete + { + public: + explicit inline AutoDelete( KeyValues *pKeyValues ) : m_pKeyValues( pKeyValues ) {} + explicit inline AutoDelete( const char *pchKVName ) : m_pKeyValues( new KeyValues( pchKVName ) ) {} + inline ~AutoDelete( void ) { if( m_pKeyValues ) m_pKeyValues->deleteThis(); } + inline void Assign( KeyValues *pKeyValues ) { m_pKeyValues = pKeyValues; } + KeyValues *operator->() { return m_pKeyValues; } + operator KeyValues *() { return m_pKeyValues; } + private: + AutoDelete( AutoDelete const &x ); // forbid + AutoDelete & operator= ( AutoDelete const &x ); // forbid + protected: + KeyValues *m_pKeyValues; + }; + + // + // AutoDeleteInline is useful when you want to hold your keyvalues object inside + // and delete it right after using. + // You can also pass temporary KeyValues object as an argument to a function by wrapping it into KeyValues::AutoDeleteInline + // instance: call_my_function( KeyValues::AutoDeleteInline( new KeyValues( "test" ) ) ) + // + class AutoDeleteInline : public AutoDelete + { + public: + explicit inline AutoDeleteInline( KeyValues *pKeyValues ) : AutoDelete( pKeyValues ) {} + inline operator KeyValues *() const { return m_pKeyValues; } + inline KeyValues * Get() const { return m_pKeyValues; } + }; + + // Quick setup constructors + KeyValues( const char *setName, const char *firstKey, const char *firstValue ); + KeyValues( const char *setName, const char *firstKey, const wchar_t *firstValue ); + KeyValues( const char *setName, const char *firstKey, int firstValue ); + KeyValues( const char *setName, const char *firstKey, const char *firstValue, const char *secondKey, const char *secondValue ); + KeyValues( const char *setName, const char *firstKey, int firstValue, const char *secondKey, int secondValue ); + + // Section name + const char *GetName() const; + void SetName( const char *setName); + + // gets the name as a unique int + int GetNameSymbol() const; + int GetNameSymbolCaseSensitive() const; + + // File access. Set UsesEscapeSequences true, if resource file/buffer uses Escape Sequences (eg \n, \t) + void UsesEscapeSequences(bool state); // default false + bool LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL, GetSymbolProc_t pfnEvaluateSymbolProc = NULL); + bool SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL); + + // Read from a buffer... Note that the buffer must be null terminated + bool LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem = NULL, const char *pPathID = NULL, GetSymbolProc_t pfnEvaluateSymbolProc = NULL ); + + // Read from a utlbuffer... + bool LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBaseFileSystem* pFileSystem = NULL, const char *pPathID = NULL, GetSymbolProc_t pfnEvaluateSymbolProc = NULL ); + + // Find a keyValue, create it if it is not found. + // Set bCreate to true to create the key if it doesn't already exist (which ensures a valid pointer will be returned) + KeyValues *FindKey(const char *keyName, bool bCreate = false); + KeyValues *FindKey(int keySymbol) const; + KeyValues *CreateNewKey(); // creates a new key, with an autogenerated name. name is guaranteed to be an integer, of value 1 higher than the highest other integer key name + void AddSubKey( KeyValues *pSubkey ); // Adds a subkey. Make sure the subkey isn't a child of some other keyvalues + void RemoveSubKey(KeyValues *subKey); // removes a subkey from the list, DOES NOT DELETE IT + void InsertSubKey( int nIndex, KeyValues *pSubKey ); // Inserts the given sub-key before the Nth child location + bool ContainsSubKey( KeyValues *pSubKey ); // Returns true if this key values contains the specified sub key, false otherwise. + void SwapSubKey( KeyValues *pExistingSubKey, KeyValues *pNewSubKey ); // Swaps an existing subkey for a new one, DOES NOT DELETE THE OLD ONE but takes ownership of the new one + void ElideSubKey( KeyValues *pSubKey ); // Removes a subkey but inserts all of its children in its place, in-order (flattens a tree, like firing a manager!) + + // Key iteration. + // + // NOTE: GetFirstSubKey/GetNextKey will iterate keys AND values. Use the functions + // below if you want to iterate over just the keys or just the values. + // + KeyValues *GetFirstSubKey(); // returns the first subkey in the list + KeyValues *GetNextKey(); // returns the next subkey + void SetNextKey( KeyValues * pDat); + + // + // These functions can be used to treat it like a true key/values tree instead of + // confusing values with keys. + // + // So if you wanted to iterate all subkeys, then all values, it would look like this: + // for ( KeyValues *pKey = pRoot->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() ) + // { + // Msg( "Key name: %s\n", pKey->GetName() ); + // } + // for ( KeyValues *pValue = pRoot->GetFirstValue(); pKey; pKey = pKey->GetNextValue() ) + // { + // Msg( "Int value: %d\n", pValue->GetInt() ); // Assuming pValue->GetDataType() == TYPE_INT... + // } + KeyValues* GetFirstTrueSubKey(); + KeyValues* GetNextTrueSubKey(); + + KeyValues* GetFirstValue(); // When you get a value back, you can use GetX and pass in NULL to get the value. + KeyValues* GetNextValue(); + + + // Data access + int GetInt( const char *keyName = NULL, int defaultValue = 0 ); + uint64 GetUint64( const char *keyName = NULL, uint64 defaultValue = 0 ); + float GetFloat( const char *keyName = NULL, float defaultValue = 0.0f ); + const char *GetString( const char *keyName = NULL, const char *defaultValue = "" ); + const wchar_t *GetWString( const char *keyName = NULL, const wchar_t *defaultValue = L"" ); + void *GetPtr( const char *keyName = NULL, void *defaultValue = (void*)0 ); + Color GetColor( const char *keyName = NULL , const Color &defaultColor = Color( 0, 0, 0, 0 ) ); + bool GetBool( const char *keyName = NULL, bool defaultValue = false ) { return GetInt( keyName, defaultValue ? 1 : 0 ) ? true : false; } + bool IsEmpty(const char *keyName = NULL); + + // Data access + int GetInt( int keySymbol, int defaultValue = 0 ); + uint64 GetUint64( int keySymbol, uint64 defaultValue = 0 ); + float GetFloat( int keySymbol, float defaultValue = 0.0f ); + const char *GetString( int keySymbol, const char *defaultValue = "" ); + const wchar_t *GetWString( int keySymbol, const wchar_t *defaultValue = L"" ); + void *GetPtr( int keySymbol, void *defaultValue = (void*)0 ); + Color GetColor( int keySymbol /* default value is all black */); + bool GetBool( int keySymbol, bool defaultValue = false ) { return GetInt( keySymbol, defaultValue ? 1 : 0 ) ? true : false; } + bool IsEmpty( int keySymbol ); + + // Key writing + void SetWString( const char *keyName, const wchar_t *value ); + void SetString( const char *keyName, const char *value ); + void SetInt( const char *keyName, int value ); + void SetUint64( const char *keyName, uint64 value ); + void SetFloat( const char *keyName, float value ); + void SetPtr( const char *keyName, void *value ); + void SetColor( const char *keyName, Color value); + void SetBool( const char *keyName, bool value ) { SetInt( keyName, value ? 1 : 0 ); } + + // Memory allocation (optimized) + void *operator new( size_t iAllocSize ); + void *operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine ); + void operator delete( void *pMem ); + void operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ); + + KeyValues& operator=( KeyValues& src ); + + // Adds a chain... if we don't find stuff in this keyvalue, we'll look + // in the one we're chained to. + void ChainKeyValue( KeyValues* pChain ); + + void RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel ); + + bool WriteAsBinary( CUtlBuffer &buffer ); + bool ReadAsBinary( CUtlBuffer &buffer ); + + // Allocate & create a new copy of the keys + KeyValues *MakeCopy( void ) const; + + // Make a new copy of all subkeys, add them all to the passed-in keyvalues + void CopySubkeys( KeyValues *pParent ) const; + + // Clear out all subkeys, and the current value + void Clear( void ); + + // Data type + enum types_t + { + TYPE_NONE = 0, + TYPE_STRING, + TYPE_INT, + TYPE_FLOAT, + TYPE_PTR, + TYPE_WSTRING, + TYPE_COLOR, + TYPE_UINT64, + TYPE_COMPILED_INT_BYTE, // hack to collapse 1 byte ints in the compiled format + TYPE_COMPILED_INT_0, // hack to collapse 0 in the compiled format + TYPE_COMPILED_INT_1, // hack to collapse 1 in the compiled format + TYPE_NUMTYPES, + }; + types_t GetDataType(const char *keyName = NULL); + + // Virtual deletion function - ensures that KeyValues object is deleted from correct heap + void deleteThis(); + + void SetStringValue( char const *strValue ); + + // unpack a key values list into a structure + void UnpackIntoStructure( struct KeyValuesUnpackStructure const *pUnpackTable, void *pDest ); + + // Process conditional keys for widescreen support. + bool ProcessResolutionKeys( const char *pResString ); + + // Dump keyvalues recursively into a dump context + bool Dump( IKeyValuesDumpContext *pDump, int nIndentLevel = 0 ); + + // Merge operations describing how two keyvalues can be combined + enum MergeKeyValuesOp_t + { + MERGE_KV_ALL, + MERGE_KV_UPDATE, // update values are copied into storage, adding new keys to storage or updating existing ones + MERGE_KV_DELETE, // update values specify keys that get deleted from storage + MERGE_KV_BORROW, // update values only update existing keys in storage, keys in update that do not exist in storage are discarded + }; + void MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp = MERGE_KV_ALL ); + + // Assign keyvalues from a string + static KeyValues * FromString( char const *szName, char const *szStringVal, char const **ppEndOfParse = NULL ); + +private: + KeyValues( KeyValues& ); // prevent copy constructor being used + + // prevent delete being called except through deleteThis() + ~KeyValues(); + + KeyValues* CreateKey( const char *keyName ); + + void RecursiveCopyKeyValues( KeyValues& src ); + void RemoveEverything(); +// void RecursiveSaveToFile( IBaseFileSystem *filesystem, CUtlBuffer &buffer, int indentLevel ); +// void WriteConvertedString( CUtlBuffer &buffer, const char *pszString ); + + // NOTE: If both filesystem and pBuf are non-null, it'll save to both of them. + // If filesystem is null, it'll ignore f. + void RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ); + void WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const char *pszString ); + + void RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc ); + + // for handling #include "filename" + void AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ); + void ParseIncludedKeys( char const *resourceName, const char *filetoinclude, + IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys, GetSymbolProc_t pfnEvaluateSymbolProc ); + + // For handling #base "filename" + void MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys ); + void RecursiveMergeKeyValues( KeyValues *baseKV ); + + // NOTE: If both filesystem and pBuf are non-null, it'll save to both of them. + // If filesystem is null, it'll ignore f. + void InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const void *pData, int len ); + + void Init(); + const char * ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasConditional ); + void WriteIndents( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ); + + void FreeAllocatedValue(); + void AllocateValueBlock(int size); + + bool ReadAsBinaryPooledFormat( CUtlBuffer &buf, IBaseFileSystem *pFileSystem, unsigned int poolKey, GetSymbolProc_t pfnEvaluateSymbolProc ); + + bool EvaluateConditional( const char *pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc ); + + uint32 m_iKeyName : 24; // keyname is a symbol defined in KeyValuesSystem + uint32 m_iKeyNameCaseSensitive1 : 8; // 1st part of case sensitive symbol defined in KeyValueSystem + + // These are needed out of the union because the API returns string pointers + char *m_sValue; + wchar_t *m_wsValue; + + // we don't delete these + union + { + int m_iValue; + float m_flValue; + void *m_pValue; + unsigned char m_Color[4]; + }; + + char m_iDataType; + char m_bHasEscapeSequences; // true, if while parsing this KeyValue, Escape Sequences are used (default false) + uint16 m_iKeyNameCaseSensitive2; // 2nd part of case sensitive symbol defined in KeyValueSystem; + + KeyValues *m_pPeer; // pointer to next key in list + KeyValues *m_pSub; // pointer to Start of a new sub key list + KeyValues *m_pChain;// Search here if it's not in our list +}; + +typedef KeyValues::AutoDelete KeyValuesAD; + +enum KeyValuesUnpackDestinationTypes_t +{ + UNPACK_TYPE_FLOAT, // dest is a float + UNPACK_TYPE_VECTOR, // dest is a Vector + UNPACK_TYPE_VECTOR_COLOR, // dest is a vector, src is a color + UNPACK_TYPE_STRING, // dest is a char *. unpacker will allocate. + UNPACK_TYPE_INT, // dest is an int + UNPACK_TYPE_FOUR_FLOATS, // dest is an array of 4 floats. source is a string like "1 2 3 4" + UNPACK_TYPE_TWO_FLOATS, // dest is an array of 2 floats. source is a string like "1 2" +}; + +#define UNPACK_FIXED( kname, kdefault, dtype, ofs ) { kname, kdefault, dtype, ofs, 0 } +#define UNPACK_VARIABLE( kname, kdefault, dtype, ofs, sz ) { kname, kdefault, dtype, ofs, sz } +#define UNPACK_END_MARKER { NULL, NULL, UNPACK_TYPE_FLOAT, 0 } + +struct KeyValuesUnpackStructure +{ + char const *m_pKeyName; // null to terminate tbl + char const *m_pKeyDefault; // null ok + KeyValuesUnpackDestinationTypes_t m_eDataType; // UNPACK_TYPE_INT, .. + size_t m_nFieldOffset; // use offsetof to set + size_t m_nFieldSize; // for strings or other variable length +}; + +//----------------------------------------------------------------------------- +// inline methods +//----------------------------------------------------------------------------- +inline int KeyValues::GetInt( int keySymbol, int defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetInt( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline uint64 KeyValues::GetUint64( int keySymbol, uint64 defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetUint64( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline float KeyValues::GetFloat( int keySymbol, float defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetFloat( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline const char *KeyValues::GetString( int keySymbol, const char *defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetString( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline const wchar_t *KeyValues::GetWString( int keySymbol, const wchar_t *defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetWString( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline void *KeyValues::GetPtr( int keySymbol, void *defaultValue ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetPtr( (const char *)NULL, defaultValue ) : defaultValue; +} + +inline Color KeyValues::GetColor( int keySymbol ) +{ + Color defaultValue( 0, 0, 0, 0 ); + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->GetColor( ) : defaultValue; +} + +inline bool KeyValues::IsEmpty( int keySymbol ) +{ + KeyValues *dat = FindKey( keySymbol ); + return dat ? dat->IsEmpty( ) : true; +} + + +// +// KeyValuesDumpContext and generic implementations +// + +class IKeyValuesDumpContext +{ +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ) = 0; + virtual bool KvWriteValue( KeyValues *pValue, int nIndentLevel ) = 0; + virtual bool KvEndKey( KeyValues *pKey, int nIndentLevel ) = 0; +}; + +class IKeyValuesDumpContextAsText : public IKeyValuesDumpContext +{ +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ); + virtual bool KvWriteValue( KeyValues *pValue, int nIndentLevel ); + virtual bool KvEndKey( KeyValues *pKey, int nIndentLevel ); + +public: + virtual bool KvWriteIndent( int nIndentLevel ); + virtual bool KvWriteText( char const *szText ) = 0; +}; + +class CKeyValuesDumpContextAsDevMsg : public IKeyValuesDumpContextAsText +{ +public: + // Overrides developer level to dump in DevMsg, zero to dump as Msg + CKeyValuesDumpContextAsDevMsg( int nDeveloperLevel = 1 ) : m_nDeveloperLevel( nDeveloperLevel ) {} + +public: + virtual bool KvBeginKey( KeyValues *pKey, int nIndentLevel ); + virtual bool KvWriteText( char const *szText ); + +protected: + int m_nDeveloperLevel; +}; + +inline bool KeyValuesDumpAsDevMsg( KeyValues *pKeyValues, int nIndentLevel = 0, int nDeveloperLevel = 1 ) +{ + CKeyValuesDumpContextAsDevMsg ctx( nDeveloperLevel ); + return pKeyValues->Dump( &ctx, nIndentLevel ); +} + + +#endif // KEYVALUES_H diff --git a/public/tier1/UtlSortVector.h b/public/tier1/UtlSortVector.h new file mode 100644 index 0000000..42c3595 --- /dev/null +++ b/public/tier1/UtlSortVector.h @@ -0,0 +1,311 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// $Header: $ +// $NoKeywords: $ +// +// A growable array class that keeps all elements in order using binary search +//===========================================================================// + +#ifndef UTLSORTVECTOR_H +#define UTLSORTVECTOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" + + +//----------------------------------------------------------------------------- +// class CUtlSortVector: +// description: +// This in an sorted order-preserving vector. Items may be inserted or removed +// at any point in the vector. When an item is inserted, all elements are +// moved down by one element using memmove. When an item is removed, all +// elements are shifted back down. Items are searched for in the vector +// using a binary search technique. Clients must pass in a Less() function +// into the constructor of the vector to determine the sort order. +//----------------------------------------------------------------------------- + +#ifndef _WIN32 +// gcc has no qsort_s, so i need to use a static var to hold the sort context. this makes cutlsortvector _not_ thread sfae under linux +extern void *g_pUtlSortVectorQSortContext; +#endif + +template +class CUtlSortVector : public CUtlVector +{ +public: + + // constructor + CUtlSortVector( int nGrowSize = 0, int initSize = 0 ); + CUtlSortVector( T* pMemory, int numElements ); + + // inserts (copy constructs) an element in sorted order into the list + int Insert( const T& src ); + + // Finds an element within the list using a binary search + int Find( const T& search ) const; + int FindLessOrEqual( const T& search ) const; + int FindLess( const T& search ) const; + + // Removes a particular element + void Remove( const T& search ); + void Remove( int i ); + + // Allows methods to set a context to be used with the less function.. + void SetLessContext( void *pCtx ); + + // Note that you can only use this index until sorting is redone!!! + int InsertNoSort( const T& src ); + void RedoSort( bool bForceSort = false ); + +protected: + // No copy constructor + CUtlSortVector( const CUtlSortVector & ); + + // never call these; illegal for this class + int AddToHead(); + int AddToTail(); + int InsertBefore( int elem ); + int InsertAfter( int elem ); + int AddToHead( const T& src ); + int AddToTail( const T& src ); + int InsertBefore( int elem, const T& src ); + int InsertAfter( int elem, const T& src ); + int AddMultipleToHead( int num ); + int AddMultipleToTail( int num, const T *pToCopy=NULL ); + int InsertMultipleBefore( int elem, int num, const T *pToCopy=NULL ); + int InsertMultipleAfter( int elem, int num ); + int AddVectorToTail( CUtlVector const &src ); + + struct QSortContext_t + { + void *m_pLessContext; + LessFunc *m_pLessFunc; + }; + +#ifdef _WIN32 + static int CompareHelper( void *context, const T *lhs, const T *rhs ) + { + QSortContext_t *ctx = reinterpret_cast< QSortContext_t * >( context ); + if ( ctx->m_pLessFunc->Less( *lhs, *rhs, ctx->m_pLessContext ) ) + return -1; + if ( ctx->m_pLessFunc->Less( *rhs, *lhs, ctx->m_pLessContext ) ) + return 1; + return 0; + } +#else + static int CompareHelper( const T *lhs, const T *rhs ) + { + QSortContext_t *ctx = reinterpret_cast< QSortContext_t * >( g_pUtlSortVectorQSortContext ); + if ( ctx->m_pLessFunc->Less( *lhs, *rhs, ctx->m_pLessContext ) ) + return -1; + if ( ctx->m_pLessFunc->Less( *rhs, *lhs, ctx->m_pLessContext ) ) + return 1; + return 0; + } +#endif + + void *m_pLessContext; + bool m_bNeedsSort; + +private: + void QuickSort( LessFunc& less, int X, int I ); +}; + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +template +CUtlSortVector::CUtlSortVector( int nGrowSize, int initSize ) : + m_pLessContext(NULL), CUtlVector( nGrowSize, initSize ), m_bNeedsSort( false ) +{ +} + +template +CUtlSortVector::CUtlSortVector( T* pMemory, int numElements ) : + m_pLessContext(NULL), CUtlVector( pMemory, numElements ), m_bNeedsSort( false ) +{ +} + +//----------------------------------------------------------------------------- +// Allows methods to set a context to be used with the less function.. +//----------------------------------------------------------------------------- +template +void CUtlSortVector::SetLessContext( void *pCtx ) +{ + m_pLessContext = pCtx; +} + +//----------------------------------------------------------------------------- +// grows the vector +//----------------------------------------------------------------------------- +template +int CUtlSortVector::Insert( const T& src ) +{ + AssertFatal( !m_bNeedsSort ); + + int pos = FindLessOrEqual( src ) + 1; + this->GrowVector(); + this->ShiftElementsRight(pos); + CopyConstruct( &this->Element(pos), src ); + return pos; +} + +template +int CUtlSortVector::InsertNoSort( const T& src ) +{ + m_bNeedsSort = true; + int lastElement = CUtlVector::m_Size; + // Just stick the new element at the end of the vector, but don't do a sort + this->GrowVector(); + this->ShiftElementsRight(lastElement); + CopyConstruct( &this->Element(lastElement), src ); + return lastElement; +} + +template +void CUtlSortVector::QuickSort( LessFunc& less, int nLower, int nUpper ) +{ +#ifdef _WIN32 + typedef int (__cdecl *QSortCompareFunc_t)(void *context, const void *, const void *); + if ( this->Count() > 1 ) + { + QSortContext_t ctx; + ctx.m_pLessContext = m_pLessContext; + ctx.m_pLessFunc = &less; + + qsort_s( Base(), Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper, &ctx ); + } +#else + typedef int (__cdecl *QSortCompareFunc_t)( const void *, const void *); + if ( this->Count() > 1 ) + { + QSortContext_t ctx; + ctx.m_pLessContext = m_pLessContext; + ctx.m_pLessFunc = &less; + g_pUtlSortVectorQSortContext = &ctx; + + qsort( this->Base(), this->Count(), sizeof(T), (QSortCompareFunc_t)&CUtlSortVector::CompareHelper ); + } +#endif +} + +template +void CUtlSortVector::RedoSort( bool bForceSort /*= false */ ) +{ + if ( !m_bNeedsSort && !bForceSort ) + return; + + m_bNeedsSort = false; + LessFunc less; + QuickSort( less, 0, this->Count() - 1 ); +} + +//----------------------------------------------------------------------------- +// finds a particular element +//----------------------------------------------------------------------------- +template +int CUtlSortVector::Find( const T& src ) const +{ + AssertFatal( !m_bNeedsSort ); + + LessFunc less; + + int start = 0, end = this->Count() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if ( less.Less( this->Element(mid), src, m_pLessContext ) ) + { + start = mid + 1; + } + else if ( less.Less( src, this->Element(mid), m_pLessContext ) ) + { + end = mid - 1; + } + else + { + return mid; + } + } + return -1; +} + + +//----------------------------------------------------------------------------- +// finds a particular element +//----------------------------------------------------------------------------- +template +int CUtlSortVector::FindLessOrEqual( const T& src ) const +{ + AssertFatal( !m_bNeedsSort ); + + LessFunc less; + int start = 0, end = this->Count() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if ( less.Less( this->Element(mid), src, m_pLessContext ) ) + { + start = mid + 1; + } + else if ( less.Less( src, this->Element(mid), m_pLessContext ) ) + { + end = mid - 1; + } + else + { + return mid; + } + } + return end; +} + +template +int CUtlSortVector::FindLess( const T& src ) const +{ + AssertFatal( !m_bNeedsSort ); + + LessFunc less; + int start = 0, end = this->Count() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if ( less.Less( this->Element(mid), src, m_pLessContext ) ) + { + start = mid + 1; + } + else + { + end = mid - 1; + } + } + return end; +} + + +//----------------------------------------------------------------------------- +// Removes a particular element +//----------------------------------------------------------------------------- +template +void CUtlSortVector::Remove( const T& search ) +{ + AssertFatal( !m_bNeedsSort ); + + int pos = Find(search); + if (pos != -1) + { + CUtlVector::Remove(pos); + } +} + +template +void CUtlSortVector::Remove( int i ) +{ + CUtlVector::Remove( i ); +} + +#endif // UTLSORTVECTOR_H diff --git a/public/tier1/UtlStringMap.h b/public/tier1/UtlStringMap.h new file mode 100644 index 0000000..7176e1e --- /dev/null +++ b/public/tier1/UtlStringMap.h @@ -0,0 +1,110 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef UTLSTRINGMAP_H +#define UTLSTRINGMAP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlsymbol.h" + +template +class CUtlStringMap +{ +public: + CUtlStringMap( bool caseInsensitive = true ) : m_SymbolTable( 0, 32, caseInsensitive ) + { + } + + // Get data by the string itself: + T& operator[]( const char *pString ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); + int index = ( int )( UtlSymId_t )symbol; + if( m_Vector.Count() <= index ) + { + m_Vector.EnsureCount( index + 1 ); + } + return m_Vector[index]; + } + + // Get data by the string's symbol table ID - only used to retrieve a pre-existing symbol, not create a new one! + T& operator[]( UtlSymId_t n ) + { + Assert( n <= m_Vector.Count() ); + return m_Vector[n]; + } + + const T& operator[]( UtlSymId_t n ) const + { + Assert( n <= m_Vector.Count() ); + return m_Vector[n]; + } + + bool Defined( const char *pString ) const + { + return m_SymbolTable.Find( pString ) != UTL_INVAL_SYMBOL; + } + + UtlSymId_t Find( const char *pString ) const + { + return m_SymbolTable.Find( pString ); + } + + UtlSymId_t AddString( const char *pString ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); + int index = ( int )( UtlSymId_t )symbol; + if( m_Vector.Count() <= index ) + { + m_Vector.EnsureCount( index + 1 ); + } + return symbol; + } + + static UtlSymId_t InvalidIndex() + { + return UTL_INVAL_SYMBOL; + } + + int GetNumStrings( void ) const + { + return m_SymbolTable.GetNumStrings(); + } + + const char *String( int n ) const + { + return m_SymbolTable.String( n ); + } + + // Clear all of the data from the map + void Clear() + { + m_Vector.RemoveAll(); + m_SymbolTable.RemoveAll(); + } + + void Purge() + { + m_Vector.Purge(); + m_SymbolTable.RemoveAll(); + } + + void PurgeAndDeleteElements() + { + m_Vector.PurgeAndDeleteElements(); + m_SymbolTable.RemoveAll(); + } + + + +private: + CUtlVector m_Vector; + CUtlSymbolTable m_SymbolTable; +}; + +#endif // UTLSTRINGMAP_H diff --git a/public/tier1/bitbuf.h b/public/tier1/bitbuf.h new file mode 100644 index 0000000..37a45f6 --- /dev/null +++ b/public/tier1/bitbuf.h @@ -0,0 +1,1501 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +// NOTE: old_bf_read is guaranteed to return zeros if it overflows. + +#ifndef BITBUF_H +#define BITBUF_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/mathlib.h" +#include "mathlib/vector.h" +#include "basetypes.h" +#include "tier0/dbg.h" + + + + +//----------------------------------------------------------------------------- +// Forward declarations. +//----------------------------------------------------------------------------- + +class Vector; +class QAngle; + +//----------------------------------------------------------------------------- +// You can define a handler function that will be called in case of +// out-of-range values and overruns here. +// +// NOTE: the handler is only called in debug mode. +// +// Call SetBitBufErrorHandler to install a handler. +//----------------------------------------------------------------------------- + +typedef enum +{ + BITBUFERROR_VALUE_OUT_OF_RANGE=0, // Tried to write a value with too few bits. + BITBUFERROR_BUFFER_OVERRUN, // Was about to overrun a buffer. + + BITBUFERROR_NUM_ERRORS +} BitBufErrorType; + + +typedef void (*BitBufErrorHandler)( BitBufErrorType errorType, const char *pDebugName ); + + +#if defined( _DEBUG ) + extern void InternalBitBufErrorHandler( BitBufErrorType errorType, const char *pDebugName ); + #define CallErrorHandler( errorType, pDebugName ) InternalBitBufErrorHandler( errorType, pDebugName ); +#else + #define CallErrorHandler( errorType, pDebugName ) +#endif + + +// Use this to install the error handler. Call with NULL to uninstall your error handler. +void SetBitBufErrorHandler( BitBufErrorHandler fn ); + + +//----------------------------------------------------------------------------- +// Helpers. +//----------------------------------------------------------------------------- + +inline int BitByte( int bits ) +{ + // return PAD_NUMBER( bits, 8 ) >> 3; + return (bits + 7) >> 3; +} + +//----------------------------------------------------------------------------- +enum EBitCoordType +{ + kCW_None, + kCW_LowPrecision, + kCW_Integral +}; + +//----------------------------------------------------------------------------- +// Used for serialization +//----------------------------------------------------------------------------- + +class bf_write +{ +public: + bf_write(); + + // nMaxBits can be used as the number of bits in the buffer. + // It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. + bf_write( void *pData, int nBytes, int nMaxBits = -1 ); + bf_write( const char *pDebugName, void *pData, int nBytes, int nMaxBits = -1 ); + + // Start writing to the specified buffer. + // nMaxBits can be used as the number of bits in the buffer. + // It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. + void StartWriting( void *pData, int nBytes, int iStartBit = 0, int nMaxBits = -1 ); + + // Restart buffer writing. + void Reset(); + + // Get the base pointer. + unsigned char* GetBasePointer() { return m_pData; } + + // Enable or disable assertion on overflow. 99% of the time, it's a bug that we need to catch, + // but there may be the occasional buffer that is allowed to overflow gracefully. + void SetAssertOnOverflow( bool bAssert ); + + // This can be set to assign a name that gets output if the buffer overflows. + const char* GetDebugName(); + void SetDebugName( const char *pDebugName ); + + +// Seek to a specific position. +public: + + void SeekToBit( int bitPos ); + + +// Bit functions. +public: + + void WriteOneBit(int nValue); + void WriteOneBitNoCheck(int nValue); + void WriteOneBitAt( int iBit, int nValue ); + + // Write signed or unsigned. Range is only checked in debug. + void WriteUBitLong( unsigned int data, int numbits, bool bCheckRange=true ); + void WriteSBitLong( int data, int numbits ); + + // Tell it whether or not the data is unsigned. If it's signed, + // cast to unsigned before passing in (it will cast back inside). + void WriteBitLong(unsigned int data, int numbits, bool bSigned); + + // Write a list of bits in. + bool WriteBits(const void *pIn, int nBits); + + // writes an unsigned integer with variable bit length + void WriteUBitVar( unsigned int data ); + + // Copy the bits straight out of pIn. This seeks pIn forward by nBits. + // Returns an error if this buffer or the read buffer overflows. + bool WriteBitsFromBuffer( class bf_read *pIn, int nBits ); + + void WriteBitAngle( float fAngle, int numbits ); + void WriteBitCoord (const float f); + void WriteBitCoordMP( const float f, EBitCoordType coordType ); + void WriteBitCellCoord( const float f, int bits, EBitCoordType coordType ); + void WriteBitFloat(float val); + void WriteBitVec3Coord( const Vector& fa ); + void WriteBitNormal( float f ); + void WriteBitVec3Normal( const Vector& fa ); + void WriteBitAngles( const QAngle& fa ); + + +// Byte functions. +public: + + void WriteChar(int val); + void WriteByte( unsigned int val ); + void WriteShort(int val); + void WriteWord( unsigned int val ); + void WriteLong(long val); + void WriteLongLong(int64 val); + void WriteFloat(float val); + bool WriteBytes( const void *pBuf, int nBytes ); + + // Returns false if it overflows the buffer. + bool WriteString(const char *pStr); + bool WriteString(const wchar_t *pStr); + + +// Status. +public: + + // How many bytes are filled in? + int GetNumBytesWritten(); + int GetNumBitsWritten(); + int GetMaxNumBits(); + int GetNumBitsLeft(); + int GetNumBytesLeft(); + unsigned char* GetData(); + + // Has the buffer overflowed? + bool CheckForOverflow(int nBits); + inline bool IsOverflowed() const {return m_bOverflow;} + + inline void SetOverflowFlag(); + + +public: + // The current buffer. + unsigned char* m_pData; + int m_nDataBytes; + int m_nDataBits; + + // Where we are in the buffer. + int m_iCurBit; + +private: + + // Errors? + bool m_bOverflow; + + bool m_bAssertOnOverflow; + const char *m_pDebugName; +}; + + +//----------------------------------------------------------------------------- +// Inlined methods +//----------------------------------------------------------------------------- + +// How many bytes are filled in? +inline int bf_write::GetNumBytesWritten() +{ + return BitByte(m_iCurBit); +} + +inline int bf_write::GetNumBitsWritten() +{ + return m_iCurBit; +} + +inline int bf_write::GetMaxNumBits() +{ + return m_nDataBits; +} + +inline int bf_write::GetNumBitsLeft() +{ + return m_nDataBits - m_iCurBit; +} + +inline int bf_write::GetNumBytesLeft() +{ + return GetNumBitsLeft() >> 3; +} + +inline unsigned char* bf_write::GetData() +{ + return m_pData; +} + +inline bool bf_write::CheckForOverflow(int nBits) +{ + if ( m_iCurBit + nBits > m_nDataBits ) + { + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); + } + + return m_bOverflow; +} + +inline void bf_write::SetOverflowFlag() +{ + if ( m_bAssertOnOverflow ) + { + Assert( false ); + } + + m_bOverflow = true; +} + +inline void bf_write::WriteOneBitNoCheck(int nValue) +{ + if(nValue) + m_pData[m_iCurBit >> 3] |= (1 << (m_iCurBit & 7)); + else + m_pData[m_iCurBit >> 3] &= ~(1 << (m_iCurBit & 7)); + + ++m_iCurBit; +} + +inline void bf_write::WriteOneBit(int nValue) +{ + if( !CheckForOverflow(1) ) + WriteOneBitNoCheck( nValue ); +} + + +inline void bf_write::WriteOneBitAt( int iBit, int nValue ) +{ + if( iBit+1 > m_nDataBits ) + { + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); + return; + } + + if( nValue ) + m_pData[iBit >> 3] |= (1 << (iBit & 7)); + else + m_pData[iBit >> 3] &= ~(1 << (iBit & 7)); +} + + +inline void bf_write::WriteUBitLong( unsigned int curData, int numbits, bool bCheckRange ) +{ +#ifdef _DEBUG + // Make sure it doesn't overflow. + if ( bCheckRange && numbits < 32 ) + { + if ( curData >= (unsigned long)(1 << numbits) ) + { + CallErrorHandler( BITBUFERROR_VALUE_OUT_OF_RANGE, GetDebugName() ); + } + } + Assert( numbits >= 0 && numbits <= 32 ); +#endif + + extern unsigned long g_BitWriteMasks[32][33]; + + // Bounds checking.. + if ((m_iCurBit+numbits) > m_nDataBits) + { + m_iCurBit = m_nDataBits; + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); + return; + } + + int nBitsLeft = numbits; + int iCurBit = m_iCurBit; + + // Mask in a dword. + unsigned int iDWord = iCurBit >> 5; + Assert( (iDWord*4 + sizeof(long)) <= (unsigned int)m_nDataBytes ); + + unsigned long iCurBitMasked = iCurBit & 31; + + uint32 dword = LoadLittleDWord( (uint32*)m_pData, iDWord ); + + dword &= g_BitWriteMasks[iCurBitMasked][nBitsLeft]; + dword |= curData << iCurBitMasked; + + // write to stream (lsb to msb ) properly + StoreLittleDWord( (uint32*)m_pData, iDWord, dword ); + + // Did it span a dword? + int nBitsWritten = 32 - iCurBitMasked; + if ( nBitsWritten < nBitsLeft ) + { + nBitsLeft -= nBitsWritten; + curData >>= nBitsWritten; + + // read from stream (lsb to msb) properly + dword = LoadLittleDWord( (uint32*)m_pData, iDWord+1 ); + + dword &= g_BitWriteMasks[0][nBitsLeft]; + dword |= curData; + + // write to stream (lsb to msb) properly + StoreLittleDWord( (uint32*)m_pData, iDWord+1, dword ); + } + + m_iCurBit += numbits; +} + + +//----------------------------------------------------------------------------- +// This is useful if you just want a buffer to write into on the stack. +//----------------------------------------------------------------------------- + +template +class old_bf_write_static : public bf_write +{ +public: + inline old_bf_write_static() : bf_write(m_StaticData, SIZE) {} + + char m_StaticData[SIZE]; +}; + + + +//----------------------------------------------------------------------------- +// Used for unserialization +//----------------------------------------------------------------------------- + +class old_bf_read +{ +public: + old_bf_read(); + + // nMaxBits can be used as the number of bits in the buffer. + // It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. + old_bf_read( const void *pData, int nBytes, int nBits = -1 ); + old_bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits = -1 ); + + // Start reading from the specified buffer. + // pData's start address must be dword-aligned. + // nMaxBits can be used as the number of bits in the buffer. + // It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. + void StartReading( const void *pData, int nBytes, int iStartBit = 0, int nBits = -1 ); + + // Restart buffer reading. + void Reset(); + + // Enable or disable assertion on overflow. 99% of the time, it's a bug that we need to catch, + // but there may be the occasional buffer that is allowed to overflow gracefully. + void SetAssertOnOverflow( bool bAssert ); + + // This can be set to assign a name that gets output if the buffer overflows. + const char* GetDebugName(); + void SetDebugName( const char *pName ); + + void ExciseBits( int startbit, int bitstoremove ); + + +// Bit functions. +public: + + // Returns 0 or 1. + int ReadOneBit(); + + +protected: + + unsigned int CheckReadUBitLong(int numbits); // For debugging. + int ReadOneBitNoCheck(); // Faster version, doesn't check bounds and is inlined. + bool CheckForOverflow(int nBits); + + +public: + + // Get the base pointer. + const unsigned char* GetBasePointer() { return m_pData; } + + FORCEINLINE int TotalBytesAvailable( void ) const + { + return m_nDataBytes; + } + + // Read a list of bits in.. + void ReadBits(void *pOut, int nBits); + + float ReadBitAngle( int numbits ); + + unsigned int ReadUBitLong( int numbits ); + unsigned int PeekUBitLong( int numbits ); + int ReadSBitLong( int numbits ); + + // reads an unsigned integer with variable bit length + unsigned int ReadUBitVar(); + + // You can read signed or unsigned data with this, just cast to + // a signed int if necessary. + unsigned int ReadBitLong(int numbits, bool bSigned); + + float ReadBitCoord(); + float ReadBitCoordMP( EBitCoordType coordType ); + float ReadBitCellCoord( int bits, EBitCoordType coordType ); + float ReadBitFloat(); + float ReadBitNormal(); + void ReadBitVec3Coord( Vector& fa ); + void ReadBitVec3Normal( Vector& fa ); + void ReadBitAngles( QAngle& fa ); + + +// Byte functions (these still read data in bit-by-bit). +public: + + int ReadChar(); + int ReadByte(); + int ReadShort(); + int ReadWord(); + long ReadLong(); + int64 ReadLongLong(); + float ReadFloat(); + bool ReadBytes(void *pOut, int nBytes); + + // Returns false if bufLen isn't large enough to hold the + // string in the buffer. + // + // Always reads to the end of the string (so you can read the + // next piece of data waiting). + // + // If bLine is true, it stops when it reaches a '\n' or a null-terminator. + // + // pStr is always null-terminated (unless bufLen is 0). + // + // pOutNumChars is set to the number of characters left in pStr when the routine is + // complete (this will never exceed bufLen-1). + // + bool ReadString( char *pStr, int bufLen, bool bLine=false, int *pOutNumChars=NULL ); + bool ReadWString( wchar_t *pStr, int bufLen, bool bLine=false, int *pOutNumChars=NULL ); + + // Reads a string and allocates memory for it. If the string in the buffer + // is > 2048 bytes, then pOverflow is set to true (if it's not NULL). + char* ReadAndAllocateString( bool *pOverflow = 0 ); + +// Status. +public: + int GetNumBytesLeft(); + int GetNumBytesRead(); + int GetNumBitsLeft(); + int GetNumBitsRead() const; + + // Has the buffer overflowed? + inline bool IsOverflowed() const {return m_bOverflow;} + + inline bool Seek(int iBit); // Seek to a specific bit. + inline bool SeekRelative(int iBitDelta); // Seek to an offset from the current position. + + // Called when the buffer is overflowed. + inline void SetOverflowFlag(); + + +public: + + // The current buffer. + const unsigned char* m_pData; + int m_nDataBytes; + int m_nDataBits; + + // Where we are in the buffer. + int m_iCurBit; + + +private: + // used by varbit reads internally + inline int CountRunOfZeros(); + + // Errors? + bool m_bOverflow; + + // For debugging.. + bool m_bAssertOnOverflow; + + const char *m_pDebugName; +}; + +//----------------------------------------------------------------------------- +// Inlines. +//----------------------------------------------------------------------------- + +inline int old_bf_read::GetNumBytesRead() +{ + return BitByte(m_iCurBit); +} + +inline int old_bf_read::GetNumBitsLeft() +{ + return m_nDataBits - m_iCurBit; +} + +inline int old_bf_read::GetNumBytesLeft() +{ + return GetNumBitsLeft() >> 3; +} + +inline int old_bf_read::GetNumBitsRead() const +{ + return m_iCurBit; +} + +inline void old_bf_read::SetOverflowFlag() +{ + if ( m_bAssertOnOverflow ) + { + Assert( false ); + } + + m_bOverflow = true; +} + +inline bool old_bf_read::Seek(int iBit) +{ + if(iBit < 0 || iBit > m_nDataBits) + { + SetOverflowFlag(); + m_iCurBit = m_nDataBits; + return false; + } + else + { + m_iCurBit = iBit; + return true; + } +} + +// Seek to an offset from the current position. +inline bool old_bf_read::SeekRelative(int iBitDelta) +{ + return Seek(m_iCurBit+iBitDelta); +} + +inline bool old_bf_read::CheckForOverflow(int nBits) +{ + if( m_iCurBit + nBits > m_nDataBits ) + { + SetOverflowFlag(); + CallErrorHandler( BITBUFERROR_BUFFER_OVERRUN, GetDebugName() ); + } + + return m_bOverflow; +} + +inline int old_bf_read::ReadOneBitNoCheck() +{ + int value = m_pData[m_iCurBit >> 3] & (1 << (m_iCurBit & 7)); + ++m_iCurBit; + return !!value; +} + +inline int old_bf_read::ReadOneBit() +{ + return (!CheckForOverflow(1)) ? ReadOneBitNoCheck() : 0; +} + +inline float old_bf_read::ReadBitFloat() +{ + long val; + + Assert(sizeof(float) == sizeof(long)); + Assert(sizeof(float) == 4); + + if(CheckForOverflow(32)) + return 0.0f; + + int bit = m_iCurBit & 0x7; + int byte = m_iCurBit >> 3; + val = m_pData[byte] >> bit; + val |= ((int)m_pData[byte + 1]) << (8 - bit); + val |= ((int)m_pData[byte + 2]) << (16 - bit); + val |= ((int)m_pData[byte + 3]) << (24 - bit); + if (bit != 0) + val |= ((int)m_pData[byte + 4]) << (32 - bit); + m_iCurBit += 32; + return *((float*)&val); +} + + +inline unsigned int old_bf_read::ReadUBitLong( int numbits ) +{ + extern unsigned long g_ExtraMasks[32]; + + if ( (m_iCurBit+numbits) > m_nDataBits ) + { + m_iCurBit = m_nDataBits; + SetOverflowFlag(); + return 0; + } + + Assert( numbits > 0 && numbits <= 32 ); + + // Read the current dword. + int idword1 = m_iCurBit >> 5; + unsigned int dword1 = LoadLittleDWord( (uint32*)m_pData, idword1 ); + + dword1 >>= (m_iCurBit & 31); // Get the bits we're interested in. + + m_iCurBit += numbits; + unsigned int ret = dword1; + + // Does it span this dword? + if ( (m_iCurBit-1) >> 5 == idword1 ) + { + if (numbits != 32) + ret &= g_ExtraMasks[numbits]; + } + else + { + int nExtraBits = m_iCurBit & 31; + unsigned int dword2 = LoadLittleDWord( (uint32*)m_pData, idword1+1 ); + + dword2 &= g_ExtraMasks[nExtraBits]; + + // No need to mask since we hit the end of the dword. + // Shift the second dword's part into the high bits. + ret |= (dword2 << (numbits - nExtraBits)); + } + + return ret; +} + + +class CBitBuffer +{ +public: + char const * m_pDebugName; + bool m_bOverflow; + int m_nDataBits; + size_t m_nDataBytes; + + void SetDebugName( char const *pName ) + { + m_pDebugName = pName; + } + + CBitBuffer( void ) + { + m_bOverflow = false; + m_pDebugName = NULL; + m_nDataBits = -1; + m_nDataBytes = 0; + } + + FORCEINLINE void SetOverflowFlag( void ) + { + m_bOverflow = true; + } + + FORCEINLINE bool IsOverflowed( void ) const + { + return m_bOverflow; + } + + static const uint32 s_nMaskTable[33]; // 0 1 3 7 15 .. + +}; + +class CBitWrite : public CBitBuffer +{ + uint32 m_nOutBufWord; + int m_nOutBitsAvail; + uint32 *m_pDataOut; + uint32 *m_pBufferEnd; + uint32 *m_pData; + bool m_bFlushed; + +public: + void StartWriting( void *pData, int nBytes, int iStartBit = 0, int nMaxBits = -1 ); + + + CBitWrite( void *pData, int nBytes, int nBits = -1 ) + { + m_bFlushed = false; + StartWriting( pData, nBytes, 0, nBits ); + } + + CBitWrite( const char *pDebugName, void *pData, int nBytes, int nBits = -1 ) + { + m_bFlushed = false; + SetDebugName( pDebugName ); + StartWriting( pData, nBytes, 0, nBits ); + } + + CBitWrite( void ) + { + m_bFlushed = false; + } + + ~CBitWrite( void ) + { + TempFlush(); + Assert( (! m_pData ) || m_bFlushed ); + } + FORCEINLINE int GetNumBitsLeft( void ) const + { + return m_nOutBitsAvail + ( 32 * ( m_pBufferEnd - m_pDataOut -1 ) ); + } + + FORCEINLINE void Reset( void ) + { + m_bOverflow = false; + m_nOutBitsAvail = 32; + m_pDataOut = m_pData; + m_nOutBufWord = 0; + + } + + FORCEINLINE void TempFlush( void ) + { + // someone wants to know how much data we have written, or the pointer to it, so we'd better make + // sure we write our data + if ( m_nOutBitsAvail != 32 ) + { + if ( m_pDataOut == m_pBufferEnd ) + { + SetOverflowFlag(); + } + else + { + StoreLittleDWord( m_pDataOut, 0, LoadLittleDWord(m_pDataOut,0) & ~s_nMaskTable[ 32 - m_nOutBitsAvail ] | m_nOutBufWord ); + } + } + m_bFlushed = true; + } + + FORCEINLINE unsigned char *GetBasePointer() + { + TempFlush(); + return reinterpret_cast< unsigned char *>( m_pData ); + } + + FORCEINLINE unsigned char *GetData() + { + return GetBasePointer(); + } + + FORCEINLINE void Finish(); + FORCEINLINE void Flush(); + FORCEINLINE void FlushNoCheck(); + FORCEINLINE void WriteOneBit(int nValue); + FORCEINLINE void WriteOneBitNoCheck(int nValue); + FORCEINLINE void WriteUBitLong( unsigned int data, int numbits, bool bCheckRange=true ); + FORCEINLINE void WriteSBitLong( int data, int numbits ); + FORCEINLINE void WriteUBitVar( unsigned int data ); + FORCEINLINE void WriteBitFloat( float flValue ); + FORCEINLINE void WriteFloat( float flValue ); + bool WriteBits(const void *pInData, int nBits); + void WriteBytes( const void *pBuf, int nBytes ); + void SeekToBit( int nSeekPos ); + + FORCEINLINE int GetNumBitsWritten( void ) const + { + return ( 32 - m_nOutBitsAvail ) + ( 32 * ( m_pDataOut - m_pData ) ); + } + + FORCEINLINE int GetNumBytesWritten( void ) const + { + return ( GetNumBitsWritten() + 7 ) >> 3; + } + + + FORCEINLINE void WriteLong(long val) + { + WriteSBitLong( val, 32 ); + } + + + + FORCEINLINE void WriteChar( int val ) + { + WriteSBitLong(val, sizeof(char) << 3 ); + } + + FORCEINLINE void WriteByte( int val ) + { + WriteUBitLong(val, sizeof(unsigned char) << 3, false ); + } + + FORCEINLINE void WriteShort(int val) + { + WriteSBitLong(val, sizeof(short) << 3); + } + + FORCEINLINE void WriteWord(int val) + { + WriteUBitLong(val, sizeof(unsigned short) << 3); + } + + bool WriteString( const char *pStr ); + bool WriteString( const wchar_t *pStr ); + + void WriteLongLong( int64 val ); + + void WriteBitAngle( float fAngle, int numbits ); + void WriteBitCoord (const float f); + void WriteBitCoordMP( const float f, EBitCoordType coordType ); + void WriteBitCellCoord( const float f, int bits, EBitCoordType coordType ); + void WriteBitVec3Coord( const Vector& fa ); + void WriteBitNormal( float f ); + void WriteBitVec3Normal( const Vector& fa ); + void WriteBitAngles( const QAngle& fa ); + + // Copy the bits straight out of pIn. This seeks pIn forward by nBits. + // Returns an error if this buffer or the read buffer overflows. + bool WriteBitsFromBuffer( class bf_read *pIn, int nBits ); + +}; + +void CBitWrite::Finish( void ) +{ + if ( m_nOutBitsAvail != 32 ) + { + if ( m_pDataOut == m_pBufferEnd ) + { + SetOverflowFlag(); + } + StoreLittleDWord( m_pDataOut, 0, m_nOutBufWord ); + } +} + +void CBitWrite::FlushNoCheck( void ) +{ + StoreLittleDWord( m_pDataOut++, 0, m_nOutBufWord ); + m_nOutBitsAvail = 32; + m_nOutBufWord = 0; // ugh - I need this because of 32 bit writes. a<<=32 is a nop + +} +void CBitWrite::Flush( void ) +{ + if ( m_pDataOut == m_pBufferEnd ) + { + SetOverflowFlag(); + } + else + { + StoreLittleDWord( m_pDataOut++, 0, m_nOutBufWord ); + } + m_nOutBufWord = 0; // ugh - I need this because of 32 bit writes. a<<=32 is a nop + m_nOutBitsAvail = 32; + +} +void CBitWrite::WriteOneBitNoCheck( int nValue ) +{ + m_nOutBufWord |= ( nValue & 1 ) << ( 32 - m_nOutBitsAvail ); + if ( --m_nOutBitsAvail == 0 ) + { + FlushNoCheck(); + } +} + +void CBitWrite::WriteOneBit( int nValue ) +{ + m_nOutBufWord |= ( nValue & 1 ) << ( 32 - m_nOutBitsAvail ); + if ( --m_nOutBitsAvail == 0 ) + { + Flush(); + } +} + +FORCEINLINE void CBitWrite::WriteUBitLong( unsigned int nData, int nNumBits, bool bCheckRange ) +{ + +#ifdef _DEBUG + // Make sure it doesn't overflow. + if ( bCheckRange && nNumBits < 32 ) + { + Assert( nData <= (unsigned long)(1 << nNumBits ) ); + } + Assert( nNumBits >= 0 && nNumBits <= 32 ); +#endif + if ( nNumBits <= m_nOutBitsAvail ) + { + if ( bCheckRange ) + m_nOutBufWord |= ( nData ) << ( 32 - m_nOutBitsAvail ); + else + m_nOutBufWord |= ( nData & s_nMaskTable[ nNumBits] ) << ( 32 - m_nOutBitsAvail ); + m_nOutBitsAvail -= nNumBits; + if ( m_nOutBitsAvail == 0 ) + { + Flush(); + } + } + else + { + // split dwords case + int nOverflowBits = ( nNumBits - m_nOutBitsAvail ); + m_nOutBufWord |= ( nData & s_nMaskTable[m_nOutBitsAvail] ) << ( 32 - m_nOutBitsAvail ); + Flush(); + m_nOutBufWord = ( nData >> ( nNumBits - nOverflowBits ) ); + m_nOutBitsAvail = 32 - nOverflowBits; + } +} + +FORCEINLINE void CBitWrite::WriteSBitLong( int nData, int nNumBits ) +{ + WriteUBitLong( ( uint32 ) nData, nNumBits, false ); +} + +FORCEINLINE void CBitWrite::WriteUBitVar( unsigned int n ) +{ + if ( n < 16 ) + WriteUBitLong( n, 6 ); + else + if ( n < 256 ) + WriteUBitLong( ( n & 15 ) | 16 | ( ( n & ( 128 | 64 | 32 | 16 ) ) << 2 ), 10 ); + else + if ( n < 4096 ) + WriteUBitLong( ( n & 15 ) | 32 | ( ( n & ( 2048 | 1024 | 512 | 256 | 128 | 64 | 32 | 16 ) ) << 2 ), 14 ); + else + { + WriteUBitLong( ( n & 15 ) | 48, 6 ); + WriteUBitLong( ( n >> 4 ), 32 - 4 ); + } +} + +FORCEINLINE void CBitWrite::WriteBitFloat( float flValue ) +{ + WriteUBitLong( *((uint32 *) &flValue ), 32 ); +} + +FORCEINLINE void CBitWrite::WriteFloat( float flValue ) +{ + // Pre-swap the float, since WriteBits writes raw data + LittleFloat( &flValue, &flValue ); + WriteUBitLong( *((uint32 *) &flValue ), 32 ); +} + +class CBitRead : public CBitBuffer +{ + uint32 m_nInBufWord; + int m_nBitsAvail; + uint32 const *m_pDataIn; + uint32 const *m_pBufferEnd; + uint32 const *m_pData; + +public: + CBitRead( const void *pData, int nBytes, int nBits = -1 ) + { + StartReading( pData, nBytes, 0, nBits ); + } + + CBitRead( const char *pDebugName, const void *pData, int nBytes, int nBits = -1 ) + { + SetDebugName( pDebugName ); + StartReading( pData, nBytes, 0, nBits ); + } + + CBitRead( void ) : CBitBuffer() + { + } + + FORCEINLINE int Tell( void ) const + { + return GetNumBitsRead(); + } + + FORCEINLINE size_t TotalBytesAvailable( void ) const + { + return m_nDataBytes; + } + + FORCEINLINE int GetNumBitsLeft() const + { + return m_nDataBits - Tell(); + } + + FORCEINLINE int GetNumBytesLeft() const + { + return GetNumBitsLeft() >> 3; + } + + bool Seek( int nPosition ); + + FORCEINLINE bool SeekRelative( int nOffset ) + { + return Seek( GetNumBitsRead() + nOffset ); + } + + FORCEINLINE unsigned char const * GetBasePointer() + { + return reinterpret_cast< unsigned char const *>( m_pData ); + } + + void StartReading( const void *pData, int nBytes, int iStartBit = 0, int nBits = -1 ); + + FORCEINLINE int GetNumBitsRead( void ) const; + + FORCEINLINE void GrabNextDWord( bool bOverFlowImmediately = false ); + FORCEINLINE void FetchNext( void ); + FORCEINLINE unsigned int ReadUBitLong( int numbits ); + FORCEINLINE int ReadSBitLong( int numbits ); + FORCEINLINE unsigned int ReadUBitVar( void ); + FORCEINLINE unsigned int PeekUBitLong( int numbits ); + FORCEINLINE float ReadBitFloat( void ); + float ReadBitCoord(); + float ReadBitCoordMP( EBitCoordType coordType ); + float ReadBitCellCoord( int bits, EBitCoordType coordType ); + float ReadBitNormal(); + void ReadBitVec3Coord( Vector& fa ); + void ReadBitVec3Normal( Vector& fa ); + void ReadBitAngles( QAngle& fa ); + bool ReadBytes(void *pOut, int nBytes); + float ReadBitAngle( int numbits ); + + // Returns 0 or 1. + FORCEINLINE int ReadOneBit( void ); + FORCEINLINE int ReadLong( void ); + FORCEINLINE int ReadChar( void ); + FORCEINLINE int ReadByte( void ); + FORCEINLINE int ReadShort( void ); + FORCEINLINE int ReadWord( void ); + FORCEINLINE float ReadFloat( void ); + void ReadBits(void *pOut, int nBits); + + // Returns false if bufLen isn't large enough to hold the + // string in the buffer. + // + // Always reads to the end of the string (so you can read the + // next piece of data waiting). + // + // If bLine is true, it stops when it reaches a '\n' or a null-terminator. + // + // pStr is always null-terminated (unless bufLen is 0). + // + // pOutN m_pBufferEnd ) + { + SetOverflowFlag(); + m_nInBufWord = 0; + } + else + { + Assert( reinterpret_cast(m_pDataIn) + 3 < reinterpret_cast(m_pBufferEnd)); + m_nInBufWord = LittleDWord( *( m_pDataIn++ ) ); + } +} + +FORCEINLINE void CBitRead::FetchNext( void ) +{ + m_nBitsAvail = 32; + GrabNextDWord( false ); +} + +int CBitRead::ReadOneBit( void ) +{ + int nRet = m_nInBufWord & 1; + if ( --m_nBitsAvail == 0 ) + { + FetchNext(); + } + else + m_nInBufWord >>= 1; + return nRet; +} + + +unsigned int CBitRead::ReadUBitLong( int numbits ) +{ + if ( m_nBitsAvail >= numbits ) + { + unsigned int nRet = m_nInBufWord & s_nMaskTable[ numbits ]; + m_nBitsAvail -= numbits; + if ( m_nBitsAvail ) + { + m_nInBufWord >>= numbits; + } + else + { + FetchNext(); + } + return nRet; + } + else + { + // need to merge words + unsigned int nRet = m_nInBufWord; + numbits -= m_nBitsAvail; + GrabNextDWord( true ); + if ( m_bOverflow ) + return 0; + nRet |= ( ( m_nInBufWord & s_nMaskTable[numbits] ) << m_nBitsAvail ); + m_nBitsAvail = 32 - numbits; + m_nInBufWord >>= numbits; + return nRet; + } +} + +FORCEINLINE unsigned int CBitRead::PeekUBitLong( int numbits ) +{ + int nSaveBA = m_nBitsAvail; + int nSaveW = m_nInBufWord; + uint32 const *pSaveP = m_pDataIn; + unsigned int nRet = ReadUBitLong( numbits ); + m_nBitsAvail = nSaveBA; + m_nInBufWord = nSaveW; + m_pDataIn = pSaveP; + return nRet; +} + +FORCEINLINE int CBitRead::ReadSBitLong( int numbits ) +{ + int nRet = ReadUBitLong( numbits ); + // sign extend + return ( nRet << ( 32 - numbits ) ) >> ( 32 - numbits ); +} + +FORCEINLINE int CBitRead::ReadLong( void ) +{ + return ( int ) ReadUBitLong( sizeof(long) << 3 ); +} + +FORCEINLINE float CBitRead::ReadFloat( void ) +{ + uint32 nUval = ReadUBitLong( sizeof(long) << 3 ); + return * ( ( float * ) &nUval ); +} + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable : 4715) // disable warning on not all cases + // returning a value. throwing default: + // in measurably reduces perf in bit + // packing benchmark +#endif +FORCEINLINE unsigned int CBitRead::ReadUBitVar( void ) +{ + unsigned int ret = ReadUBitLong( 6 ); + switch( ret & ( 16 | 32 ) ) + { + case 16: + ret = ( ret & 15 ) | ( ReadUBitLong( 4 ) << 4 ); + Assert( ret >= 16); + break; + + case 32: + ret = ( ret & 15 ) | ( ReadUBitLong( 8 ) << 4 ); + Assert( ret >= 256); + break; + case 48: + ret = ( ret & 15 ) | ( ReadUBitLong( 32 - 4 ) << 4 ); + Assert( ret >= 4096 ); + break; + } + return ret; +} +#ifdef _WIN32 +#pragma warning(pop) +#endif + +FORCEINLINE float CBitRead::ReadBitFloat( void ) +{ + uint32 nvalue = ReadUBitLong( 32 ); + return *( ( float * ) &nvalue ); +} + +int CBitRead::ReadChar( void ) +{ + return ReadSBitLong(sizeof(char) << 3); +} + +int CBitRead::ReadByte( void ) +{ + return ReadUBitLong(sizeof(unsigned char) << 3); +} + +int CBitRead::ReadShort( void ) +{ + return ReadSBitLong(sizeof(short) << 3); +} + +int CBitRead::ReadWord( void ) +{ + return ReadUBitLong(sizeof(unsigned short) << 3); +} + +#define WRAP_READ( bc ) \ +class bf_read : public bc \ +{ \ +public: \ + FORCEINLINE bf_read( void ) : bc( ) \ + { \ + } \ + \ + FORCEINLINE bf_read( const void *pData, int nBytes, int nBits = -1 ) : bc( pData, nBytes, nBits ) \ + { \ + } \ + \ + FORCEINLINE bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits = -1 ) : bc( pDebugName, pData, nBytes, nBits ) \ + { \ + } \ +}; + +#if 0 + + +#define DELEGATE0( t, m ) t m() \ +{ \ + Check(); \ + t nOld = old1.m(); \ + t nNew = new1.m(); \ + Assert( nOld == nNew ); \ + Check(); \ + return nOld; \ +} +#define DELEGATE1( t, m, t1 ) t m( t1 x) \ +{ \ + Check(); \ + t nOld = old1.m( x); \ + t nNew = new1.m( x ); \ + Assert( nOld == nNew ); \ + Check(); \ + return nOld; \ +} + +#define DELEGATE0I( m ) DELEGATE0( int, m ) +#define DELEGATE0LL( m ) DELEGATE0( int64, m ) + +class bf_read +{ + old_bf_read old1; + CBitRead new1; + + void Check( void ) const + { + int n=new1.GetNumBitsRead(); + int o=old1.GetNumBitsRead(); + Assert( n == o ); + Assert( old1.IsOverflowed() == new1.IsOverflowed() ); + } + +public: + FORCEINLINE bf_read( void ) : old1(), new1() + { + } + + FORCEINLINE bf_read( const void *pData, int nBytes, int nBits = -1 ) : old1( pData, nBytes, nBits ),new1( pData, nBytes, nBits ) + { + } + + FORCEINLINE bf_read( const char *pDebugName, const void *pData, int nBytes, int nBits = -1 ) : old1( pDebugName, pData, nBytes, nBits ), new1( pDebugName, pData, nBytes, nBits ) + { + } + + FORCEINLINE bool IsOverflowed( void ) const + { + bool bOld = old1.IsOverflowed(); + bool bNew = new1.IsOverflowed(); + Assert( bOld == bNew ); + Check(); + return bOld; + + } + + void ReadBits(void *pOut, int nBits) + { + old1.ReadBits( pOut, nBits ); + void *mem=stackalloc( 1+ ( nBits / 8 ) ); + new1.ReadBits( mem, nBits ); + Assert( memcmp( mem, pOut, nBits / 8 ) == 0 ); + } + + bool ReadBytes(void *pOut, int nBytes) + { + ReadBits(pOut, nBytes << 3); + return ! IsOverflowed(); + } + + + unsigned int ReadUBitLong( int numbits ) + { + unsigned int nOld = old1.ReadUBitLong( numbits ); + unsigned int nNew = new1.ReadUBitLong( numbits ); + Assert( nOld == nNew ); + Check(); + return nOld; + } + + unsigned const char* GetBasePointer() + { + Assert( old1.GetBasePointer() == new1.GetBasePointer() ); + Check(); + return old1.GetBasePointer(); + } + void SetDebugName( const char *pDebugName ) + { + old1.SetDebugName( pDebugName ); + new1.SetDebugName( pDebugName ); + Check(); + } + + void StartReading( const void *pData, int nBytes, int iStartBit = 0, int nBits = -1 ) + { + old1.StartReading( pData, nBytes, iStartBit, nBits ); + new1.StartReading( pData, nBytes, iStartBit, nBits ); + Check(); + } + + void SetAssertOnOverflow( bool bAssert ) + { + old1.SetAssertOnOverflow( bAssert ); +// new1.SetAssertOnOverflow( bAssert ); + Check(); + } + + DELEGATE0I( ReadOneBit ); + DELEGATE0I( ReadByte ); + DELEGATE0I( ReadWord ); + DELEGATE0I( ReadLong ); + DELEGATE0I( GetNumBytesLeft ); + DELEGATE0I( ReadShort ); + DELEGATE1( int, PeekUBitLong, int ); + DELEGATE0I( ReadChar ); + DELEGATE0I( GetNumBitsRead ); + DELEGATE0LL( ReadLongLong ); + DELEGATE0( float, ReadFloat); + DELEGATE0( unsigned int, ReadUBitVar ); + DELEGATE0( float, ReadBitCoord); + DELEGATE2( float, ReadBitCoordMP, bool, bool ); + DELEGATE0( float, ReadBitFloat); + DELEGATE0( float, ReadBitNormal); + DELEGATE1( bool,Seek, int ); + DELEGATE1( float, ReadBitAngle, int ); + DELEGATE1( bool,SeekRelative,int); + DELEGATE0I( GetNumBitsLeft ); + DELEGATE0I( TotalBytesAvailable ); + + void SetOverflowFlag() + { + old1.SetOverflowFlag(); + new1.SetOverflowFlag(); + Check(); + } + + bool ReadString( char *pStr, int bufLen, bool bLine=false, int *pOutNumChars=NULL ) + { + Check(); + int oldn, newn; + bool bOld = old1.ReadString( pStr, bufLen, bLine, &oldn ); + bool bNew = new1.ReadString( pStr, bufLen, bLine, &newn ); + Assert( bOld == bNew ); + Assert( oldn == newn ); + if ( pOutNumChars ) + *pOutNumChars = oldn; + Check(); + return bOld; + } + + bool ReadWString( wchar_t *pStr, int bufLen, bool bLine=false, int *pOutNumChars=NULL ) + { + Check(); + int oldn, newn; + bool bOld = old1.ReadWString( pStr, bufLen, bLine, &oldn ); + bool bNew = new1.ReadWString( pStr, bufLen, bLine, &newn ); + Assert( bOld == bNew ); + Assert( oldn == newn ); + if ( pOutNumChars ) + *pOutNumChars = oldn; + Check(); + return bOld; + } + + void ReadBitVec3Coord( Vector& fa ) + { + Check(); + old1.ReadBitVec3Coord( fa ); + Vector test; + new1.ReadBitVec3Coord( test ); + Assert( VectorsAreEqual( fa, test )); + Check(); + } + void ReadBitVec3Normal( Vector& fa ) + { + Check(); + old1.ReadBitVec3Coord( fa ); + Vector test; + new1.ReadBitVec3Coord( test ); + Assert( VectorsAreEqual( fa, test )); + Check(); + } + + char* ReadAndAllocateString( bool *pOverflow = NULL ) + { + Check(); + bool bold, bnew; + char *pold = old1.ReadAndAllocateString( &bold ); + char *pnew = new1.ReadAndAllocateString( &bnew ); + Assert( bold == bnew ); + Assert(strcmp( pold, pnew ) == 0 ); + delete[] pnew; + Check(); + if ( pOverflow ) + *pOverflow = bold; + return pold; + + } + + DELEGATE1( int, ReadSBitLong, int ); + +}; +#endif + + +WRAP_READ( CBitRead ); + +#endif + + + diff --git a/public/tier1/byteswap.h b/public/tier1/byteswap.h new file mode 100644 index 0000000..38ca1ec --- /dev/null +++ b/public/tier1/byteswap.h @@ -0,0 +1,268 @@ +//========= Copyright © 1996-2006, Valve LLC, All rights reserved. ============ +// +// Purpose: Low level byte swapping routines. +// +// $NoKeywords: $ +//============================================================================= +#ifndef BYTESWAP_H +#define BYTESWAP_H +#if defined(_WIN32) +#pragma once +#endif + +#include "tier0/dbg.h" +#include "datamap.h" // needed for typedescription_t. note datamap.h is tier1 as well. + +class CByteswap +{ +public: + CByteswap() + { + // Default behavior sets the target endian to match the machine native endian (no swap). + SetTargetBigEndian( IsMachineBigEndian() ); + } + + //----------------------------------------------------------------------------- + // Write a single field. + //----------------------------------------------------------------------------- + void SwapFieldToTargetEndian( void* pOutputBuffer, void *pData, typedescription_t *pField ); + + //----------------------------------------------------------------------------- + // Write a block of fields. Works a bit like the saverestore code. + //----------------------------------------------------------------------------- + void SwapFieldsToTargetEndian( void *pOutputBuffer, void *pBaseData, datamap_t *pDataMap ); + + // Swaps fields for the templated type to the output buffer. + template inline void SwapFieldsToTargetEndian( T* pOutputBuffer, void *pBaseData, unsigned int objectCount = 1 ) + { + for ( unsigned int i = 0; i < objectCount; ++i, ++pOutputBuffer ) + { + SwapFieldsToTargetEndian( (void*)pOutputBuffer, pBaseData, &T::m_DataMap ); + pBaseData = (byte*)pBaseData + sizeof(T); + } + } + + // Swaps fields for the templated type in place. + template inline void SwapFieldsToTargetEndian( T* pOutputBuffer, unsigned int objectCount = 1 ) + { + SwapFieldsToTargetEndian( pOutputBuffer, (void*)pOutputBuffer, objectCount ); + } + + //----------------------------------------------------------------------------- + // True if the current machine is detected as big endian. + // (Endienness is effectively detected at compile time when optimizations are + // enabled) + //----------------------------------------------------------------------------- + static bool IsMachineBigEndian() + { + short nIsBigEndian = 1; + + // if we are big endian, the first byte will be a 0, if little endian, it will be a one. + return (bool)(0 == *(char *)&nIsBigEndian ); + } + + //----------------------------------------------------------------------------- + // Sets the target byte ordering we are swapping to or from. + // + // Braindead Endian Reference: + // x86 is LITTLE Endian + // PowerPC is BIG Endian + //----------------------------------------------------------------------------- + inline void SetTargetBigEndian( bool bigEndian ) + { + m_bBigEndian = bigEndian; + m_bSwapBytes = IsMachineBigEndian() != bigEndian; + } + + // Changes target endian + inline void FlipTargetEndian( void ) + { + m_bSwapBytes = !m_bSwapBytes; + m_bBigEndian = !m_bBigEndian; + } + + // Forces byte swapping state, regardless of endianess + inline void ActivateByteSwapping( bool bActivate ) + { + SetTargetBigEndian( IsMachineBigEndian() != bActivate ); + } + + //----------------------------------------------------------------------------- + // Returns true if the target machine is the same as this one in endianness. + // + // Used to determine when a byteswap needs to take place. + //----------------------------------------------------------------------------- + inline bool IsSwappingBytes( void ) // Are bytes being swapped? + { + return m_bSwapBytes; + } + + inline bool IsTargetBigEndian( void ) // What is the current target endian? + { + return m_bBigEndian; + } + + //----------------------------------------------------------------------------- + // IsByteSwapped() + // + // When supplied with a chunk of input data and a constant or magic number + // (in native format) determines the endienness of the current machine in + // relation to the given input data. + // + // Returns: + // 1 if input is the same as nativeConstant. + // 0 if input is byteswapped relative to nativeConstant. + // -1 if input is not the same as nativeConstant and not byteswapped either. + // + // ( This is useful for detecting byteswapping in magic numbers in structure + // headers for example. ) + //----------------------------------------------------------------------------- + template inline int SourceIsNativeEndian( T input, T nativeConstant ) + { + // If it's the same, it isn't byteswapped: + if( input == nativeConstant ) + return 1; + + int output; + LowLevelByteSwap( &output, &input ); + if( output == nativeConstant ) + return 0; + + Assert( 0 ); // if we get here, input is neither a swapped nor unswapped version of nativeConstant. + return -1; + } + + //----------------------------------------------------------------------------- + // Swaps an input buffer full of type T into the given output buffer. + // + // Swaps [count] items from the inputBuffer to the outputBuffer. + // If inputBuffer is omitted or NULL, then it is assumed to be the same as + // outputBuffer - effectively swapping the contents of the buffer in place. + //----------------------------------------------------------------------------- + template inline void SwapBuffer( T* outputBuffer, T* inputBuffer = NULL, int count = 1 ) + { + Assert( count >= 0 ); + Assert( outputBuffer ); + + // Fail gracefully in release: + if( count <=0 || !outputBuffer ) + return; + + // Optimization for the case when we are swapping in place. + if( inputBuffer == NULL ) + { + inputBuffer = outputBuffer; + } + + // Swap everything in the buffer: + for( int i = 0; i < count; i++ ) + { + LowLevelByteSwap( &outputBuffer[i], &inputBuffer[i] ); + } + } + + //----------------------------------------------------------------------------- + // Swaps an input buffer full of type T into the given output buffer. + // + // Swaps [count] items from the inputBuffer to the outputBuffer. + // If inputBuffer is omitted or NULL, then it is assumed to be the same as + // outputBuffer - effectively swapping the contents of the buffer in place. + //----------------------------------------------------------------------------- + template inline void SwapBufferToTargetEndian( T* outputBuffer, T* inputBuffer = NULL, int count = 1 ) + { + Assert( count >= 0 ); + Assert( outputBuffer ); + + // Fail gracefully in release: + if( count <=0 || !outputBuffer ) + return; + + // Optimization for the case when we are swapping in place. + if( inputBuffer == NULL ) + { + inputBuffer = outputBuffer; + } + + // Are we already the correct endienness? ( or are we swapping 1 byte items? ) + if( !m_bSwapBytes || ( sizeof(T) == 1 ) ) + { + // If we were just going to swap in place then return. + if( !inputBuffer ) + return; + + // Otherwise copy the inputBuffer to the outputBuffer: + if ( outputBuffer != inputBuffer ) + memcpy( outputBuffer, inputBuffer, count * sizeof( T ) ); + return; + + } + + // Swap everything in the buffer: + for( int i = 0; i < count; i++ ) + { + LowLevelByteSwap( &outputBuffer[i], &inputBuffer[i] ); + } + } + +private: + //----------------------------------------------------------------------------- + // The lowest level byte swapping workhorse of doom. output always contains the + // swapped version of input. ( Doesn't compare machine to target endianness ) + //----------------------------------------------------------------------------- + template static void LowLevelByteSwap( T *output, T *input ) + { + T temp = *output; +#if defined( _X360 ) + // Intrinsics need the source type to be fixed-point + DWORD* word = (DWORD*)input; + switch( sizeof(T) ) + { + case 8: + { + __storewordbytereverse( *(word+1), 0, &temp ); + __storewordbytereverse( *(word+0), 4, &temp ); + } + break; + + case 4: + __storewordbytereverse( *word, 0, &temp ); + break; + + case 2: + __storeshortbytereverse( *input, 0, &temp ); + break; + + case 1: + Q_memcpy( &temp, input, 1 ); + break; + + default: + Assert( "Invalid size in CByteswap::LowLevelByteSwap" && 0 ); + } +#else + for( int i = 0; i < sizeof(T); i++ ) + { + ((unsigned char* )&temp)[i] = ((unsigned char*)input)[sizeof(T)-(i+1)]; + } +#endif + Q_memcpy( output, &temp, sizeof(T) ); + } + +#if defined( _X360 ) + // specialized for void * to get 360 XDK compile working despite changelist 281331 + //----------------------------------------------------------------------------- + // The lowest level byte swapping workhorse of doom. output always contains the + // swapped version of input. ( Doesn't compare machine to target endianness ) + //----------------------------------------------------------------------------- + template<> static void LowLevelByteSwap( void **output, void **input ) + { + AssertMsgOnce( sizeof(void *) == sizeof(unsigned int) , "void *'s on this platform are not four bytes!" ); + __storewordbytereverse( *reinterpret_cast(input), 0, output ); + } +#endif + + unsigned int m_bSwapBytes : 1; + unsigned int m_bBigEndian : 1; +}; + +#endif /* !BYTESWAP_H */ diff --git a/public/tier1/callqueue.h b/public/tier1/callqueue.h new file mode 100644 index 0000000..8fb8813 --- /dev/null +++ b/public/tier1/callqueue.h @@ -0,0 +1,233 @@ +//========== Copyright © 2006, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef CALLQUEUE_H +#define CALLQUEUE_H + +#include "tier0/tslist.h" +#include "functors.h" +#include "vstdlib/jobthread.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------- +// Avert thy eyes! Imagine rather: +// +// void QueueCall( , [args1, [arg2,]...] +// void QueueCall( , , [args1, [arg2,]...] +// void QueueRefCall( , <, [args1, [arg2,]...] +//----------------------------------------------------- + +#define DEFINE_CALLQUEUE_NONMEMBER_QUEUE_CALL(N) \ + template \ + void QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + QueueFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ) ); \ + } + +//------------------------------------- + +#define DEFINE_CALLQUEUE_MEMBER_QUEUE_CALL(N) \ + template \ + void QueueCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + QueueFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ) ); \ + } + +//------------------------------------- + +#define DEFINE_CALLQUEUE_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + void QueueCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + QueueFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ) ); \ + } + +//------------------------------------- + +#define DEFINE_CALLQUEUE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \ + template \ + void QueueRefCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + QueueFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ) ); \ + } + +//------------------------------------- + +#define DEFINE_CALLQUEUE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + void QueueRefCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + QueueFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ) ); \ + \ + } + +#define FUNC_GENERATE_QUEUE_METHODS() \ + FUNC_GENERATE_ALL( DEFINE_CALLQUEUE_NONMEMBER_QUEUE_CALL ); \ + FUNC_GENERATE_ALL( DEFINE_CALLQUEUE_MEMBER_QUEUE_CALL ); \ + FUNC_GENERATE_ALL( DEFINE_CALLQUEUE_CONST_MEMBER_QUEUE_CALL );\ + FUNC_GENERATE_ALL( DEFINE_CALLQUEUE_REF_COUNTING_MEMBER_QUEUE_CALL ); \ + FUNC_GENERATE_ALL( DEFINE_CALLQUEUE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL ) + +//----------------------------------------------------- + +template > +class CCallQueueT +{ +public: + CCallQueueT() + : m_bNoQueue( false ) + { +#ifdef _DEBUG + m_nCurSerialNumber = 0; + m_nBreakSerialNumber = (unsigned)-1; +#endif + } + + void DisableQueue( bool bDisable ) + { + if ( m_bNoQueue == bDisable ) + { + return; + } + if ( !m_bNoQueue ) + CallQueued(); + + m_bNoQueue = bDisable; + } + + bool IsDisabled() const + { + return m_bNoQueue; + } + + int Count() + { + return m_queue.Count(); + } + + void CallQueued() + { + if ( !m_queue.Count() ) + { + return; + } + + m_queue.PushItem( NULL ); + + CFunctor *pFunctor; + + while ( m_queue.PopItem( &pFunctor ) && pFunctor != NULL ) + { +#ifdef _DEBUG + if ( pFunctor->m_nUserID == m_nBreakSerialNumber) + { + m_nBreakSerialNumber = (unsigned)-1; + } +#endif + (*pFunctor)(); + pFunctor->Release(); + } + + } + + void ParallelCallQueued( IThreadPool *pPool = NULL ) + { + if ( ! pPool ) + { + pPool = g_pThreadPool; + } + int nNumThreads = 1; + + if ( pPool ) + { + nNumThreads = MIN( pPool->NumThreads(), MAX( 1, Count() ) ); + } + + if ( nNumThreads < 2 ) + { + CallQueued(); + } + else + { + int *pDummy = NULL; + ParallelProcess( pPool, pDummy, nNumThreads, this, &CCallQueueT<>::ExecuteWrapper ); + } + } + + void QueueFunctor( CFunctor *pFunctor ) + { + Assert( pFunctor ); + QueueFunctorInternal( RetAddRef( pFunctor ) ); + } + + void Flush() + { + m_queue.PushItem( NULL ); + + CFunctor *pFunctor; + + while ( m_queue.PopItem( &pFunctor ) && pFunctor != NULL ) + { + pFunctor->Release(); + } + } + + FUNC_GENERATE_QUEUE_METHODS(); + +private: + void ExecuteWrapper( int &nDummy ) // to match paralell process function template + { + CallQueued(); + } + + void QueueFunctorInternal( CFunctor *pFunctor ) + { + if ( !m_bNoQueue ) + { +#ifdef _DEBUG + pFunctor->m_nUserID = m_nCurSerialNumber++; +#endif + m_queue.PushItem( pFunctor ); + } + else + { + (*pFunctor)(); + pFunctor->Release(); + } + } + + QUEUE_TYPE m_queue; + bool m_bNoQueue; + unsigned m_nCurSerialNumber; + unsigned m_nBreakSerialNumber; +}; + +class CCallQueue : public CCallQueueT<> +{ +}; + +//----------------------------------------------------- +// Optional interface that can be bound to concrete CCallQueue +//----------------------------------------------------- + +class ICallQueue +{ +public: + void QueueFunctor( CFunctor *pFunctor ) + { + QueueFunctorInternal( RetAddRef( pFunctor ) ); + } + + FUNC_GENERATE_QUEUE_METHODS(); + +private: + virtual void QueueFunctorInternal( CFunctor *pFunctor ) = 0; +}; + +#endif // CALLQUEUE_H diff --git a/public/tier1/characterset.h b/public/tier1/characterset.h new file mode 100644 index 0000000..85540b9 --- /dev/null +++ b/public/tier1/characterset.h @@ -0,0 +1,43 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Shared code for parsing / searching for characters in a string +// using lookup tables +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef CHARACTERSET_H +#define CHARACTERSET_H + +#ifdef _WIN32 +#pragma once +#endif + + +struct characterset_t +{ + char set[256]; +}; + + +// This is essentially a strpbrk() using a precalculated lookup table +//----------------------------------------------------------------------------- +// Purpose: builds a simple lookup table of a group of important characters +// Input : *pSetBuffer - pointer to the buffer for the group +// *pSetString - list of characters to flag +//----------------------------------------------------------------------------- +extern void CharacterSetBuild( characterset_t *pSetBuffer, const char *pSetString ); + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pSetBuffer - pre-build group buffer +// character - character to lookup +// Output : int - 1 if the character was in the set +//----------------------------------------------------------------------------- +#define IN_CHARACTERSET( SetBuffer, character ) ((SetBuffer).set[(character)]) + + +#endif // CHARACTERSET_H diff --git a/public/tier1/checksum_crc.h b/public/tier1/checksum_crc.h new file mode 100644 index 0000000..fa9a766 --- /dev/null +++ b/public/tier1/checksum_crc.h @@ -0,0 +1,31 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Generic CRC functions +// +// $NoKeywords: $ +//=============================================================================// +#ifndef CHECKSUM_CRC_H +#define CHECKSUM_CRC_H +#ifdef _WIN32 +#pragma once +#endif + +typedef unsigned long CRC32_t; + +void CRC32_Init( CRC32_t *pulCRC ); +void CRC32_ProcessBuffer( CRC32_t *pulCRC, const void *p, int len ); +void CRC32_Final( CRC32_t *pulCRC ); +CRC32_t CRC32_GetTableEntry( unsigned int slot ); + +inline CRC32_t CRC32_ProcessSingleBuffer( const void *p, int len ) +{ + CRC32_t crc; + + CRC32_Init( &crc ); + CRC32_ProcessBuffer( &crc, p, len ); + CRC32_Final( &crc ); + + return crc; +} + +#endif // CHECKSUM_CRC_H diff --git a/public/tier1/checksum_md5.h b/public/tier1/checksum_md5.h new file mode 100644 index 0000000..3692cf1 --- /dev/null +++ b/public/tier1/checksum_md5.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Generic MD5 hashing algo +// +//=============================================================================// + +#ifndef CHECKSUM_MD5_H +#define CHECKSUM_MD5_H + +#ifdef _WIN32 +#pragma once +#endif + +// 16 bytes == 128 bit digest +#define MD5_DIGEST_LENGTH 16 + +// MD5 Hash +typedef struct +{ + unsigned int buf[4]; + unsigned int bits[2]; + unsigned char in[64]; +} MD5Context_t; + +void MD5Init( MD5Context_t *context ); +void MD5Update( MD5Context_t *context, unsigned char const *buf, unsigned int len ); +void MD5Final( unsigned char digest[ MD5_DIGEST_LENGTH ], MD5Context_t *context ); + +char *MD5_Print(unsigned char *digest, int hashlen ); + +unsigned int MD5_PseudoRandom(unsigned int nSeed); + +#endif // CHECKSUM_MD5_H diff --git a/public/tier1/convar.h b/public/tier1/convar.h new file mode 100644 index 0000000..c96fc7f --- /dev/null +++ b/public/tier1/convar.h @@ -0,0 +1,1011 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $NoKeywords: $ +//===========================================================================// + +#ifndef CONVAR_H +#define CONVAR_H + +#if _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier1/iconvar.h" +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" +#include "color.h" +#include "icvar.h" + +#ifdef _WIN32 +#define FORCEINLINE_CVAR FORCEINLINE +#elif POSIX +#define FORCEINLINE_CVAR inline +#else +#error "implement me" +#endif + + +//----------------------------------------------------------------------------- +// Uncomment me to test for threading issues for material system convars +// NOTE: You want to disable all threading when you do this +// +host_thread_mode 0 +r_threaded_particles 0 +sv_parallel_packentities 0 +sv_disable_querycache 0 +//----------------------------------------------------------------------------- +//#define CONVAR_TEST_MATERIAL_THREAD_CONVARS 1 + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class ConVar; +class CCommand; +class ConCommand; +class ConCommandBase; +struct characterset_t; + + + +//----------------------------------------------------------------------------- +// Any executable that wants to use ConVars need to implement one of +// these to hook up access to console variables. +//----------------------------------------------------------------------------- +class IConCommandBaseAccessor +{ +public: + // Flags is a combination of FCVAR flags in cvar.h. + // hOut is filled in with a handle to the variable. + virtual bool RegisterConCommandBase( ConCommandBase *pVar ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Helper method for console development +//----------------------------------------------------------------------------- +#if defined( _X360 ) +void ConVar_PublishToVXConsole(); +#endif + + +//----------------------------------------------------------------------------- +// Called when a ConCommand needs to execute +//----------------------------------------------------------------------------- +typedef void ( *FnCommandCallbackV1_t )( void ); +typedef void ( *FnCommandCallback_t )( const CCommand &command ); + +#define COMMAND_COMPLETION_MAXITEMS 64 +#define COMMAND_COMPLETION_ITEM_LENGTH 64 + +//----------------------------------------------------------------------------- +// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings +//----------------------------------------------------------------------------- +typedef int ( *FnCommandCompletionCallback )( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ); + + +//----------------------------------------------------------------------------- +// Interface version +//----------------------------------------------------------------------------- +class ICommandCallback +{ +public: + virtual void CommandCallback( const CCommand &command ) = 0; +}; + +class ICommandCompletionCallback +{ +public: + virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: The base console invoked command/cvar interface +//----------------------------------------------------------------------------- +class ConCommandBase +{ + friend class CCvar; + friend class ConVar; + friend class ConCommand; + friend void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor ); + friend void ConVar_PublishToVXConsole(); + + // FIXME: Remove when ConVar changes are done + friend class CDefaultCvar; + +public: + ConCommandBase( void ); + ConCommandBase( const char *pName, const char *pHelpString = 0, + int flags = 0 ); + + virtual ~ConCommandBase( void ); + + virtual bool IsCommand( void ) const; + + // Check flag + virtual bool IsFlagSet( int flag ) const; + // Set flag + virtual void AddFlags( int flags ); + // Clear flag + virtual void RemoveFlags( int flags ); + + virtual int GetFlags() const; + + // Return name of cvar + virtual const char *GetName( void ) const; + + // Return help text for cvar + virtual const char *GetHelpText( void ) const; + + // Deal with next pointer + const ConCommandBase *GetNext( void ) const; + ConCommandBase *GetNext( void ); + + virtual bool IsRegistered( void ) const; + + // Returns the DLL identifier + virtual CVarDLLIdentifier_t GetDLLIdentifier() const; + +protected: + virtual void Create( const char *pName, const char *pHelpString = 0, + int flags = 0 ); + + // Used internally by OneTimeInit to initialize/shutdown + virtual void Init(); + void Shutdown(); + + // Internal copy routine ( uses new operator from correct module ) + char *CopyString( const char *from ); + +private: + // Next ConVar in chain + // Prior to register, it points to the next convar in the DLL. + // Once registered, though, m_pNext is reset to point to the next + // convar in the global list + ConCommandBase *m_pNext; + + // Has the cvar been added to the global list? + bool m_bRegistered; + + // Static data + const char *m_pszName; + const char *m_pszHelpString; + + // ConVar flags + int m_nFlags; + +protected: + // ConVars add themselves to this list for the executable. + // Then ConVar_Register runs through all the console variables + // and registers them into a global list stored in vstdlib.dll + static ConCommandBase *s_pConCommandBases; + + // ConVars in this executable use this 'global' to access values. + static IConCommandBaseAccessor *s_pAccessor; +}; + + +//----------------------------------------------------------------------------- +// Command tokenizer +//----------------------------------------------------------------------------- +class CCommand +{ +public: + CCommand(); + CCommand( int nArgC, const char **ppArgV ); + bool Tokenize( const char *pCommand, characterset_t *pBreakSet = NULL ); + void Reset(); + + int ArgC() const; + const char **ArgV() const; + const char *ArgS() const; // All args that occur after the 0th arg, in string form + const char *GetCommandString() const; // The entire command in string form, including the 0th arg + const char *operator[]( int nIndex ) const; // Gets at arguments + const char *Arg( int nIndex ) const; // Gets at arguments + + // Helper functions to parse arguments to commands. + const char* FindArg( const char *pName ) const; + int FindArgInt( const char *pName, int nDefaultVal ) const; + + static int MaxCommandLength(); + static characterset_t* DefaultBreakSet(); + +private: + enum + { + COMMAND_MAX_ARGC = 64, + COMMAND_MAX_LENGTH = 512, + }; + + int m_nArgc; + int m_nArgv0Size; + char m_pArgSBuffer[ COMMAND_MAX_LENGTH ]; + char m_pArgvBuffer[ COMMAND_MAX_LENGTH ]; + const char* m_ppArgv[ COMMAND_MAX_ARGC ]; +}; + +inline int CCommand::MaxCommandLength() +{ + return COMMAND_MAX_LENGTH - 1; +} + +inline int CCommand::ArgC() const +{ + return m_nArgc; +} + +inline const char **CCommand::ArgV() const +{ + return m_nArgc ? (const char**)m_ppArgv : NULL; +} + +inline const char *CCommand::ArgS() const +{ + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; +} + +inline const char *CCommand::GetCommandString() const +{ + return m_nArgc ? m_pArgSBuffer : ""; +} + +inline const char *CCommand::Arg( int nIndex ) const +{ + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if ( nIndex < 0 || nIndex >= m_nArgc ) + return ""; + return m_ppArgv[nIndex]; +} + +inline const char *CCommand::operator[]( int nIndex ) const +{ + return Arg( nIndex ); +} + + +//----------------------------------------------------------------------------- +// Purpose: The console invoked command +//----------------------------------------------------------------------------- +class ConCommand : public ConCommandBase +{ +friend class CCvar; + +public: + typedef ConCommandBase BaseClass; + + ConCommand( const char *pName, FnCommandCallbackV1_t callback, + const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ); + ConCommand( const char *pName, FnCommandCallback_t callback, + const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ); + ConCommand( const char *pName, ICommandCallback *pCallback, + const char *pHelpString = 0, int flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 ); + + virtual ~ConCommand( void ); + + virtual bool IsCommand( void ) const; + + virtual int AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands ); + + virtual bool CanAutoComplete( void ); + + // Invoke the function + virtual void Dispatch( const CCommand &command ); + +private: + // NOTE: To maintain backward compat, we have to be very careful: + // All public virtual methods must appear in the same order always + // since engine code will be calling into this code, which *does not match* + // in the mod code; it's using slightly different, but compatible versions + // of this class. Also: Be very careful about adding new fields to this class. + // Those fields will not exist in the version of this class that is instanced + // in mod code. + + // Call this function when executing the command + union + { + FnCommandCallbackV1_t m_fnCommandCallbackV1; + FnCommandCallback_t m_fnCommandCallback; + ICommandCallback *m_pCommandCallback; + }; + + union + { + FnCommandCompletionCallback m_fnCompletionCallback; + ICommandCompletionCallback *m_pCommandCompletionCallback; + }; + + bool m_bHasCompletionCallback : 1; + bool m_bUsingNewCommandCallback : 1; + bool m_bUsingCommandCallbackInterface : 1; +}; + + +//----------------------------------------------------------------------------- +// Purpose: A console variable +//----------------------------------------------------------------------------- +class ConVar : public ConCommandBase, public IConVar +{ +friend class CCvar; +friend class ConVarRef; +friend class SplitScreenConVarRef; + +public: + typedef ConCommandBase BaseClass; + + ConVar( const char *pName, const char *pDefaultValue, int flags = 0); + + ConVar( const char *pName, const char *pDefaultValue, int flags, + const char *pHelpString ); + ConVar( const char *pName, const char *pDefaultValue, int flags, + const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax ); + ConVar( const char *pName, const char *pDefaultValue, int flags, + const char *pHelpString, FnChangeCallback_t callback ); + ConVar( const char *pName, const char *pDefaultValue, int flags, + const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, + FnChangeCallback_t callback ); + + virtual ~ConVar( void ); + + virtual bool IsFlagSet( int flag ) const; + virtual const char* GetHelpText( void ) const; + virtual bool IsRegistered( void ) const; + virtual const char *GetName( void ) const; + // Return name of command (usually == GetName(), except in case of FCVAR_SS_ADDED vars + virtual const char *GetBaseName( void ) const; + virtual int GetSplitScreenPlayerSlot() const; + + virtual void AddFlags( int flags ); + virtual int GetFlags() const; + virtual bool IsCommand( void ) const; + + // Install a change callback (there shouldn't already be one....) + void InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke = true ); + void RemoveChangeCallback( FnChangeCallback_t callbackToRemove ); + + int GetChangeCallbackCount() const { return m_pParent->m_fnChangeCallbacks.Count(); } + FnChangeCallback_t GetChangeCallback( int slot ) const { return m_pParent->m_fnChangeCallbacks[ slot ]; } + + // Retrieve value + FORCEINLINE_CVAR float GetFloat( void ) const; + FORCEINLINE_CVAR int GetInt( void ) const; + FORCEINLINE_CVAR Color GetColor( void ) const; + FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); } + FORCEINLINE_CVAR char const *GetString( void ) const; + + // Compiler driven selection for template use + template T Get( void ) const; + template T Get( T * ) const; + + // Any function that allocates/frees memory needs to be virtual or else you'll have crashes + // from alloc/free across dll/exe boundaries. + + // These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue). + virtual void SetValue( const char *value ); + virtual void SetValue( float value ); + virtual void SetValue( int value ); + virtual void SetValue( Color value ); + + // Reset to default value + void Revert( void ); + + // True if it has a min/max setting + bool HasMin() const; + bool HasMax() const; + + bool GetMin( float& minVal ) const; + bool GetMax( float& maxVal ) const; + + float GetMinValue() const; + float GetMaxValue() const; + + const char *GetDefault( void ) const; + + // Value + struct CVValue_t + { + char *m_pszString; + int m_StringLength; + + // Values + float m_fValue; + int m_nValue; + }; + + FORCEINLINE_CVAR CVValue_t &GetRawValue() + { + return m_Value; + } + FORCEINLINE_CVAR const CVValue_t &GetRawValue() const + { + return m_Value; + } + +private: + bool InternalSetColorFromString( const char *value ); + // Called by CCvar when the value of a var is changing. + virtual void InternalSetValue(const char *value); + // For CVARs marked FCVAR_NEVER_AS_STRING + virtual void InternalSetFloatValue( float fNewValue ); + virtual void InternalSetIntValue( int nValue ); + virtual void InternalSetColorValue( Color value ); + + virtual bool ClampValue( float& value ); + virtual void ChangeStringValue( const char *tempVal, float flOldValue ); + + virtual void Create( const char *pName, const char *pDefaultValue, int flags = 0, + const char *pHelpString = 0, bool bMin = false, float fMin = 0.0, + bool bMax = false, float fMax = false, FnChangeCallback_t callback = 0 ); + + // Used internally by OneTimeInit to initialize. + virtual void Init(); + + + +protected: + + // This either points to "this" or it points to the original declaration of a ConVar. + // This allows ConVars to exist in separate modules, and they all use the first one to be declared. + // m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar). + ConVar *m_pParent; + + // Static data + const char *m_pszDefaultValue; + + CVValue_t m_Value; + + // Min/Max values + bool m_bHasMin; + float m_fMinVal; + bool m_bHasMax; + float m_fMaxVal; + + // Call this function when ConVar changes + CUtlVector< FnChangeCallback_t > m_fnChangeCallbacks; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a float +// Output : float +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR float ConVar::GetFloat( void ) const +{ +#ifdef CONVAR_TEST_MATERIAL_THREAD_CONVARS + Assert( ThreadInMainThread() || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ) ); +#endif + return m_pParent->m_Value.m_fValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as an int +// Output : int +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR int ConVar::GetInt( void ) const +{ +#ifdef CONVAR_TEST_MATERIAL_THREAD_CONVARS + Assert( ThreadInMainThread() || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ) ); +#endif + return m_pParent->m_Value.m_nValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +// Output : Color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVar::GetColor( void ) const +{ +#ifdef CONVAR_TEST_MATERIAL_THREAD_CONVARS + Assert( ThreadInMainThread() || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ) ); +#endif + unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_Value.m_nValue); + return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); +} + + +//----------------------------------------------------------------------------- + +template <> FORCEINLINE_CVAR float ConVar::Get( void ) const { return GetFloat(); } +template <> FORCEINLINE_CVAR int ConVar::Get( void ) const { return GetInt(); } +template <> FORCEINLINE_CVAR bool ConVar::Get( void ) const { return GetBool(); } +template <> FORCEINLINE_CVAR const char * ConVar::Get( void ) const { return GetString(); } +template <> FORCEINLINE_CVAR float ConVar::Get( float *p ) const { return ( *p = GetFloat() ); } +template <> FORCEINLINE_CVAR int ConVar::Get( int *p ) const { return ( *p = GetInt() ); } +template <> FORCEINLINE_CVAR bool ConVar::Get( bool *p ) const { return ( *p = GetBool() ); } +template <> FORCEINLINE_CVAR const char * ConVar::Get( char const **p ) const { return ( *p = GetString() ); } + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. +// Output : const char * +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR const char *ConVar::GetString( void ) const +{ +#ifdef CONVAR_TEST_MATERIAL_THREAD_CONVARS + Assert( ThreadInMainThread() || IsFlagSet( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS ) ); +#endif + if ( m_nFlags & FCVAR_NEVER_AS_STRING ) + return "FCVAR_NEVER_AS_STRING"; + + char const *str = m_pParent->m_Value.m_pszString; + return str ? str : ""; +} + +class CSplitScreenAddedConVar : public ConVar +{ + typedef ConVar BaseClass; +public: + CSplitScreenAddedConVar( int nSplitScreenSlot, const char *pName, const ConVar *pBaseVar ) : + BaseClass + ( + pName, + pBaseVar->GetDefault(), + // Keep basevar flags, except remove _SS and add _SS_ADDED instead + ( pBaseVar->GetFlags() & ~FCVAR_SS ) | FCVAR_SS_ADDED, + pBaseVar->GetHelpText(), + pBaseVar->HasMin(), + pBaseVar->GetMinValue(), + pBaseVar->HasMax(), + pBaseVar->GetMaxValue() + ), + m_pBaseVar( pBaseVar ), + m_nSplitScreenSlot( nSplitScreenSlot ) + { + for ( int i = 0; i < pBaseVar->GetChangeCallbackCount(); ++i ) + { + InstallChangeCallback( pBaseVar->GetChangeCallback( i ), false ); + } + Assert( nSplitScreenSlot >= 1 ); + Assert( nSplitScreenSlot < MAX_SPLITSCREEN_CLIENTS ); + Assert( m_pBaseVar ); + Assert( IsFlagSet( FCVAR_SS_ADDED ) ); + Assert( !IsFlagSet( FCVAR_SS ) ); + } + + const ConVar *GetBaseVar() const; + virtual const char *GetBaseName() const; + void SetSplitScreenPlayerSlot( int nSlot ); + virtual int GetSplitScreenPlayerSlot() const; + +protected: + + const ConVar *m_pBaseVar; + int m_nSplitScreenSlot; +}; + +FORCEINLINE_CVAR const ConVar *CSplitScreenAddedConVar::GetBaseVar() const +{ + Assert( m_pBaseVar ); + return m_pBaseVar; +} + +FORCEINLINE_CVAR const char *CSplitScreenAddedConVar::GetBaseName() const +{ + Assert( m_pBaseVar ); + return m_pBaseVar->GetName(); +} + +FORCEINLINE_CVAR void CSplitScreenAddedConVar::SetSplitScreenPlayerSlot( int nSlot ) +{ + m_nSplitScreenSlot = nSlot; +} + +FORCEINLINE_CVAR int CSplitScreenAddedConVar::GetSplitScreenPlayerSlot() const +{ + return m_nSplitScreenSlot; +} + +//----------------------------------------------------------------------------- +// Used to read/write convars that already exist (replaces the FindVar method) +//----------------------------------------------------------------------------- +class ConVarRef +{ +public: + ConVarRef( const char *pName ); + ConVarRef( const char *pName, bool bIgnoreMissing ); + ConVarRef( IConVar *pConVar ); + + void Init( const char *pName, bool bIgnoreMissing ); + bool IsValid() const; + bool IsFlagSet( int nFlags ) const; + IConVar *GetLinkedConVar(); + + // Get/Set value + float GetFloat( void ) const; + int GetInt( void ) const; + Color GetColor( void ) const; + bool GetBool() const { return !!GetInt(); } + const char *GetString( void ) const; + + void SetValue( const char *pValue ); + void SetValue( float flValue ); + void SetValue( int nValue ); + void SetValue( Color value ); + void SetValue( bool bValue ); + + const char *GetName() const; + + const char *GetDefault() const; + + const char *GetBaseName() const; + + int GetSplitScreenPlayerSlot() const; + +private: + // High-speed method to read convar data + IConVar *m_pConVar; + ConVar *m_pConVarState; +}; + + +//----------------------------------------------------------------------------- +// Did we find an existing convar of that name? +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR bool ConVarRef::IsFlagSet( int nFlags ) const +{ + return ( m_pConVar->IsFlagSet( nFlags ) != 0 ); +} + +FORCEINLINE_CVAR IConVar *ConVarRef::GetLinkedConVar() +{ + return m_pConVar; +} + +FORCEINLINE_CVAR const char *ConVarRef::GetName() const +{ + return m_pConVar->GetName(); +} + +FORCEINLINE_CVAR const char *ConVarRef::GetBaseName() const +{ + return m_pConVar->GetBaseName(); +} + +FORCEINLINE_CVAR int ConVarRef::GetSplitScreenPlayerSlot() const +{ + return m_pConVar->GetSplitScreenPlayerSlot(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a float +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR float ConVarRef::GetFloat( void ) const +{ + return m_pConVarState->m_Value.m_fValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as an int +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const +{ + return m_pConVarState->m_Value.m_nValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a color +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color ConVarRef::GetColor( void ) const +{ + return m_pConVarState->GetColor(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR const char *ConVarRef::GetString( void ) const +{ + Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) ); + return m_pConVarState->m_Value.m_pszString; +} + + +FORCEINLINE_CVAR void ConVarRef::SetValue( const char *pValue ) +{ + m_pConVar->SetValue( pValue ); +} + +FORCEINLINE_CVAR void ConVarRef::SetValue( float flValue ) +{ + m_pConVar->SetValue( flValue ); +} + +FORCEINLINE_CVAR void ConVarRef::SetValue( int nValue ) +{ + m_pConVar->SetValue( nValue ); +} + +FORCEINLINE_CVAR void ConVarRef::SetValue( Color value ) +{ + m_pConVar->SetValue( value ); +} + +FORCEINLINE_CVAR void ConVarRef::SetValue( bool bValue ) +{ + m_pConVar->SetValue( bValue ? 1 : 0 ); +} + +FORCEINLINE_CVAR const char *ConVarRef::GetDefault() const +{ + return m_pConVarState->m_pszDefaultValue; +} + +//----------------------------------------------------------------------------- +// Helper for referencing splitscreen convars (i.e., "name" and "name2") +//----------------------------------------------------------------------------- +class SplitScreenConVarRef +{ +public: + SplitScreenConVarRef( const char *pName ); + SplitScreenConVarRef( const char *pName, bool bIgnoreMissing ); + SplitScreenConVarRef( IConVar *pConVar ); + + void Init( const char *pName, bool bIgnoreMissing ); + bool IsValid() const; + bool IsFlagSet( int nFlags ) const; + + // Get/Set value + float GetFloat( int nSlot ) const; + int GetInt( int nSlot ) const; + Color GetColor( int nSlot ) const; + bool GetBool( int nSlot ) const { return !!GetInt( nSlot ); } + const char *GetString( int nSlot ) const; + + void SetValue( int nSlot, const char *pValue ); + void SetValue( int nSlot, float flValue ); + void SetValue( int nSlot, int nValue ); + void SetValue( int nSlot, Color value ); + void SetValue( int nSlot, bool bValue ); + + const char *GetName( int nSlot ) const; + + const char *GetDefault() const; + + const char *GetBaseName() const; + +private: + struct cv_t + { + IConVar *m_pConVar; + ConVar *m_pConVarState; + }; + + cv_t m_Info[ MAX_SPLITSCREEN_CLIENTS ]; +}; + +//----------------------------------------------------------------------------- +// Did we find an existing convar of that name? +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR bool SplitScreenConVarRef::IsFlagSet( int nFlags ) const +{ + return ( m_Info[ 0 ].m_pConVar->IsFlagSet( nFlags ) != 0 ); +} + +FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetName( int nSlot ) const +{ + return m_Info[ nSlot ].m_pConVar->GetName(); +} + +FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetBaseName() const +{ + return m_Info[ 0 ].m_pConVar->GetBaseName(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a float +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR float SplitScreenConVarRef::GetFloat( int nSlot ) const +{ + return m_Info[ nSlot ].m_pConVarState->m_Value.m_fValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as an int +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR int SplitScreenConVarRef::GetInt( int nSlot ) const +{ + return m_Info[ nSlot ].m_pConVarState->m_Value.m_nValue; +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as an int +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR Color SplitScreenConVarRef::GetColor( int nSlot ) const +{ + return m_Info[ nSlot ].m_pConVarState->GetColor(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. +//----------------------------------------------------------------------------- +FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetString( int nSlot ) const +{ + Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) ); + return m_Info[ nSlot ].m_pConVarState->m_Value.m_pszString; +} + + +FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, const char *pValue ) +{ + m_Info[ nSlot ].m_pConVar->SetValue( pValue ); +} + +FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, float flValue ) +{ + m_Info[ nSlot ].m_pConVar->SetValue( flValue ); +} + +FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, int nValue ) +{ + m_Info[ nSlot ].m_pConVar->SetValue( nValue ); +} + +FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, Color value ) +{ + m_Info[ nSlot ].m_pConVar->SetValue( value ); +} + +FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, bool bValue ) +{ + m_Info[ nSlot ].m_pConVar->SetValue( bValue ? 1 : 0 ); +} + +FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetDefault() const +{ + return m_Info[ 0 ].m_pConVarState->m_pszDefaultValue; +} + +//----------------------------------------------------------------------------- +// Called by the framework to register ConCommands with the ICVar +//----------------------------------------------------------------------------- +void ConVar_Register( int nCVarFlag = 0, IConCommandBaseAccessor *pAccessor = NULL ); +void ConVar_Unregister( ); + + +//----------------------------------------------------------------------------- +// Utility methods +//----------------------------------------------------------------------------- +void ConVar_PrintDescription( const ConCommandBase *pVar ); + + +//----------------------------------------------------------------------------- +// Purpose: Utility class to quickly allow ConCommands to call member methods +//----------------------------------------------------------------------------- +#pragma warning (disable : 4355 ) + +template< class T > +class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, public ICommandCompletionCallback +{ + typedef ConCommand BaseClass; + typedef void ( T::*FnMemberCommandCallback_t )( const CCommand &command ); + typedef int ( T::*FnMemberCommandCompletionCallback_t )( const char *pPartial, CUtlVector< CUtlString > &commands ); + +public: + CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString = 0, + int flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) : + BaseClass( pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL ) + { + m_pOwner = pOwner; + m_Func = callback; + m_CompletionFunc = completionFunc; + } + + ~CConCommandMemberAccessor() + { + Shutdown(); + } + + void SetOwner( T* pOwner ) + { + m_pOwner = pOwner; + } + + virtual void CommandCallback( const CCommand &command ) + { + Assert( m_pOwner && m_Func ); + (m_pOwner->*m_Func)( command ); + } + + virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) + { + Assert( m_pOwner && m_CompletionFunc ); + return (m_pOwner->*m_CompletionFunc)( pPartial, commands ); + } + +private: + T* m_pOwner; + FnMemberCommandCallback_t m_Func; + FnMemberCommandCompletionCallback_t m_CompletionFunc; +}; + +#pragma warning ( default : 4355 ) + + +//----------------------------------------------------------------------------- +// Purpose: Utility macros to quicky generate a simple console command +//----------------------------------------------------------------------------- +#define CON_COMMAND( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) + +#ifdef CLIENT_DLL + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_SHARED( name, description ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description ); \ + static void name( const CCommand &args ) +#endif + + +#define CON_COMMAND_F( name, description, flags ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description, flags ); \ + static void name( const CCommand &args ) + +#ifdef CLIENT_DLL + #define CON_COMMAND_F_SHARED( name, description, flags ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description, flags ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_F_SHARED( name, description, flags ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description, flags ); \ + static void name( const CCommand &args ) +#endif + + +#define CON_COMMAND_F_COMPLETION( name, description, flags, completion ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description, flags, completion ); \ + static void name( const CCommand &args ) + +#ifdef CLIENT_DLL + #define CON_COMMAND_F_COMPLETION_SHARED( name, description, flags, completion ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command_client( #name "_client", name, description, flags, completion ); \ + static void name( const CCommand &args ) +#else + #define CON_COMMAND_F_COMPLETION_SHARED( name, description, flags, completion ) \ + static void name( const CCommand &args ); \ + static ConCommand name##_command( #name, name, description, flags, completion ); \ + static void name( const CCommand &args ) +#endif + + +#define CON_COMMAND_EXTERN( name, _funcname, description ) \ + void _funcname( const CCommand &args ); \ + static ConCommand name##_command( #name, _funcname, description ); \ + void _funcname( const CCommand &args ) + +#define CON_COMMAND_EXTERN_F( name, _funcname, description, flags ) \ + void _funcname( const CCommand &args ); \ + static ConCommand name##_command( #name, _funcname, description, flags ); \ + void _funcname( const CCommand &args ) + +#define CON_COMMAND_MEMBER_F( _thisclass, name, _funcname, description, flags ) \ + void _funcname( const CCommand &args ); \ + friend class CCommandMemberInitializer_##_funcname; \ + class CCommandMemberInitializer_##_funcname \ + { \ + public: \ + CCommandMemberInitializer_##_funcname() : m_ConCommandAccessor( NULL, name, &_thisclass::_funcname, description, flags ) \ + { \ + m_ConCommandAccessor.SetOwner( GET_OUTER( _thisclass, m_##_funcname##_register ) ); \ + } \ + private: \ + CConCommandMemberAccessor< _thisclass > m_ConCommandAccessor; \ + }; \ + \ + CCommandMemberInitializer_##_funcname m_##_funcname##_register; \ + + +#endif // CONVAR_H diff --git a/public/tier1/convar_serverbounded.h b/public/tier1/convar_serverbounded.h new file mode 100644 index 0000000..3616239 --- /dev/null +++ b/public/tier1/convar_serverbounded.h @@ -0,0 +1,53 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Helper class for cvars that have restrictions on their value. +// +//=============================================================================// + +#ifndef CONVAR_SERVERBOUNDED_H +#define CONVAR_SERVERBOUNDED_H +#ifdef _WIN32 +#pragma once +#endif + + +// This class is used to virtualize a ConVar's value, so the client can restrict its +// value while connected to a server. When using this across modules, it's important +// to dynamic_cast it to a ConVar_ServerBounded or you won't get the restricted value. +// +// NOTE: FCVAR_USERINFO vars are not virtualized before they are sent to the server +// (we have no way to detect if the virtualized value would change), so +// if you want to use a bounded cvar's value on the server, you must rebound it +// the same way the client does. +class ConVar_ServerBounded : public ConVar +{ +public: + ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) + : ConVar( pName, pDefaultValue, flags, pHelpString ) + { + } + + ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, FnChangeCallback_t callback ) + : ConVar( pName, pDefaultValue, flags, pHelpString, callback ) + { + } + + ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) + : ConVar( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax ) {} + + // You must implement GetFloat. + virtual float GetFloat() const = 0; + + // You can optionally implement these. + virtual int GetInt() const { return (int)GetFloat(); } + virtual bool GetBool() const { return ( GetInt() != 0 ); } + + // Use this to get the underlying cvar's value. + float GetBaseFloatValue() const + { + return ConVar::GetFloat(); + } +}; + + +#endif // CONVAR_SERVERBOUNDED_H diff --git a/public/tier1/datamanager.h b/public/tier1/datamanager.h new file mode 100644 index 0000000..07dc90c --- /dev/null +++ b/public/tier1/datamanager.h @@ -0,0 +1,296 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef RESOURCEMANAGER_H +#define RESOURCEMANAGER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/threadtools.h" +#include "utlmultilist.h" +#include "utlvector.h" + +FORWARD_DECLARE_HANDLE( memhandle_t ); + +#define INVALID_MEMHANDLE ((memhandle_t)0xffffffff) + +class CDataManagerBase +{ +public: + + // public API + // ----------------------------------------------------------------------------- + // memhandle_t CreateResource( params ) // implemented by derived class + void DestroyResource( memhandle_t handle ); + + // type-safe implementation in derived class + //void *LockResource( memhandle_t handle ); + int UnlockResource( memhandle_t handle ); + void TouchResource( memhandle_t handle ); + void MarkAsStale( memhandle_t handle ); // move to head of LRU + + int LockCount( memhandle_t handle ); + int BreakLock( memhandle_t handle ); + int BreakAllLocks(); + + // HACKHACK: For convenience - offers no lock protection + // type-safe implementation in derived class + //void *GetResource_NoLock( memhandle_t handle ); + + unsigned int TargetSize(); + unsigned int AvailableSize(); + unsigned int UsedSize(); + + void NotifySizeChanged( memhandle_t handle, unsigned int oldSize, unsigned int newSize ); + + void SetTargetSize( unsigned int targetSize ); + + // NOTE: flush is equivalent to Destroy + unsigned int FlushAllUnlocked(); + unsigned int FlushToTargetSize(); + unsigned int FlushAll(); + unsigned int Purge( unsigned int nBytesToPurge ); + unsigned int EnsureCapacity( unsigned int size ); + + // Thread lock + virtual void Lock() {} + virtual bool TryLock() { return true; } + virtual void Unlock() {} + + // Iteration + + // ----------------------------------------------------------------------------- + + void SetFreeOnDestruct( bool value ) { m_freeOnDestruct = value; } + + // Debugging only!!!! + void GetLRUHandleList( CUtlVector< memhandle_t >& list ); + void GetLockHandleList( CUtlVector< memhandle_t >& list ); + + +protected: + // derived class must call these to implement public API + unsigned short CreateHandle( bool bCreateLocked ); + memhandle_t StoreResourceInHandle( unsigned short memoryIndex, void *pStore, unsigned int realSize ); + void *GetResource_NoLock( memhandle_t handle ); + void *GetResource_NoLockNoLRUTouch( memhandle_t handle ); + void *LockResource( memhandle_t handle ); + void *LockResourceReturnCount( int *pCount, memhandle_t handle ); + + // NOTE: you must call this from the destructor of the derived class! (will assert otherwise) + void FreeAllLists() { FlushAll(); m_listsAreFreed = true; } + + CDataManagerBase( unsigned int maxSize ); + virtual ~CDataManagerBase(); + + + inline unsigned int MemTotal_Inline() const { return m_targetMemorySize; } + inline unsigned int MemAvailable_Inline() const { return m_targetMemorySize - m_memUsed; } + inline unsigned int MemUsed_Inline() const { return m_memUsed; } + +// Implemented by derived class: + virtual void DestroyResourceStorage( void * ) = 0; + virtual unsigned int GetRealSize( void * ) = 0; + + memhandle_t ToHandle( unsigned short index ); + unsigned short FromHandle( memhandle_t handle ); + + void TouchByIndex( unsigned short memoryIndex ); + void * GetForFreeByIndex( unsigned short memoryIndex ); + + // One of these is stored per active allocation + struct resource_lru_element_t + { + resource_lru_element_t() + { + lockCount = 0; + serial = 1; + pStore = 0; + } + + unsigned short lockCount; + unsigned short serial; + void *pStore; + }; + + unsigned int m_targetMemorySize; + unsigned int m_memUsed; + + CUtlMultiList< resource_lru_element_t, unsigned short > m_memoryLists; + + unsigned short m_lruList; + unsigned short m_lockList; + unsigned short m_freeList; + unsigned short m_listsAreFreed : 1; + unsigned short m_freeOnDestruct : 1; + unsigned short m_unused : 14; + +}; + +template< class STORAGE_TYPE, class CREATE_PARAMS, class LOCK_TYPE = STORAGE_TYPE *, class MUTEX_TYPE = CThreadNullMutex> +class CDataManager : public CDataManagerBase +{ + typedef CDataManagerBase BaseClass; +public: + + CDataManager( unsigned int size = (unsigned)-1 ) : BaseClass(size) {} + + + ~CDataManager() + { + // NOTE: This must be called in all implementations of CDataManager + if ( m_freeOnDestruct ) + { + FreeAllLists(); + } + } + + // Use GetData() to translate pointer to LOCK_TYPE + LOCK_TYPE LockResource( memhandle_t hMem ) + { + void *pLock = BaseClass::LockResource( hMem ); + if ( pLock ) + { + return StoragePointer(pLock)->GetData(); + } + + return NULL; + } + + LOCK_TYPE LockResourceReturnCount( int *pCount, memhandle_t hMem ) + { + void *pLock = BaseClass::LockResourceReturnCount( pCount, hMem ); + if ( pLock ) + { + return StoragePointer(pLock)->GetData(); + } + + return NULL; + } + + // Use GetData() to translate pointer to LOCK_TYPE + LOCK_TYPE GetResource_NoLock( memhandle_t hMem ) + { + void *pLock = const_cast(BaseClass::GetResource_NoLock( hMem )); + if ( pLock ) + { + return StoragePointer(pLock)->GetData(); + } + return NULL; + } + + // Use GetData() to translate pointer to LOCK_TYPE + // Doesn't touch the memory LRU + LOCK_TYPE GetResource_NoLockNoLRUTouch( memhandle_t hMem ) + { + void *pLock = const_cast(BaseClass::GetResource_NoLockNoLRUTouch( hMem )); + if ( pLock ) + { + return StoragePointer(pLock)->GetData(); + } + return NULL; + } + + // Wrapper to match implementation of allocation with typed storage & alloc params. + memhandle_t CreateResource( const CREATE_PARAMS &createParams, bool bCreateLocked = false ) + { + BaseClass::EnsureCapacity(STORAGE_TYPE::EstimatedSize(createParams)); + STORAGE_TYPE *pStore = STORAGE_TYPE::CreateResource( createParams ); + AUTO_LOCK_( CDataManagerBase, *this ); + unsigned short memoryIndex = BaseClass::CreateHandle( bCreateLocked ); + return BaseClass::StoreResourceInHandle( memoryIndex, pStore, pStore->Size() ); + } + + // Iteration. Must lock first + memhandle_t GetFirstUnlocked() + { + unsigned node = m_memoryLists.Head(m_lruList); + if ( node == m_memoryLists.InvalidIndex() ) + { + return INVALID_MEMHANDLE; + } + return ToHandle( node ); + } + + memhandle_t GetFirstLocked() + { + unsigned node = m_memoryLists.Head(m_lockList); + if ( node == m_memoryLists.InvalidIndex() ) + { + return INVALID_MEMHANDLE; + } + return ToHandle( node ); + } + + memhandle_t GetNext( memhandle_t hPrev ) + { + if ( hPrev == INVALID_MEMHANDLE ) + { + return INVALID_MEMHANDLE; + } + + unsigned short iNext = m_memoryLists.Next( FromHandle( hPrev ) ); + if ( iNext == m_memoryLists.InvalidIndex() ) + { + return INVALID_MEMHANDLE; + } + + return ToHandle( iNext ); + } + + MUTEX_TYPE &AccessMutex() { return m_mutex; } + virtual void Lock() { m_mutex.Lock(); } + virtual bool TryLock() { return m_mutex.TryLock(); } + virtual void Unlock() { m_mutex.Unlock(); } + +private: + STORAGE_TYPE *StoragePointer( void *pMem ) + { + return static_cast(pMem); + } + + virtual void DestroyResourceStorage( void *pStore ) + { + StoragePointer(pStore)->DestroyResource(); + } + + virtual unsigned int GetRealSize( void *pStore ) + { + return StoragePointer(pStore)->Size(); + } + + MUTEX_TYPE m_mutex; +}; + +//----------------------------------------------------------------------------- + +inline unsigned short CDataManagerBase::FromHandle( memhandle_t handle ) +{ + unsigned int fullWord = (unsigned int)handle; + unsigned short serial = fullWord>>16; + unsigned short index = fullWord & 0xFFFF; + index--; + if ( m_memoryLists.IsValidIndex(index) && m_memoryLists[index].serial == serial ) + return index; + return m_memoryLists.InvalidIndex(); +} + +inline int CDataManagerBase::LockCount( memhandle_t handle ) +{ + Lock(); + int result = 0; + unsigned short memoryIndex = FromHandle(handle); + if ( memoryIndex != m_memoryLists.InvalidIndex() ) + { + result = m_memoryLists[memoryIndex].lockCount; + } + Unlock(); + return result; +} + + +#endif // RESOURCEMANAGER_H diff --git a/public/tier1/delegates.h b/public/tier1/delegates.h new file mode 100644 index 0000000..9b715c0 --- /dev/null +++ b/public/tier1/delegates.h @@ -0,0 +1,99 @@ +//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: Simple macros to generate delegation code +// +//============================================================================= + +#ifndef DELEGATES_H +#define DELEGATES_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#define DELEGATE_TO_OBJECT_0( RetType, FuncName, pDelegated ) RetType FuncName() { return (pDelegated)->FuncName(); } +#define DELEGATE_TO_OBJECT_0V( FuncName, pDelegated ) void FuncName() { (pDelegated)->FuncName(); } +#define DELEGATE_TO_OBJECT_1( RetType, FuncName, ArgType1, pDelegated ) RetType FuncName( ArgType1 a1 ) { return (pDelegated)->FuncName( a1 ); } +#define DELEGATE_TO_OBJECT_1V( FuncName, ArgType1, pDelegated ) void FuncName( ArgType1 a1 ) { (pDelegated)->FuncName( a1 ); } +#define DELEGATE_TO_OBJECT_2( RetType, FuncName, ArgType1, ArgType2, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2 ) { return (pDelegated)->FuncName( a1, a2 ); } +#define DELEGATE_TO_OBJECT_2V( FuncName, ArgType1, ArgType2, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2 ) { (pDelegated)->FuncName( a1, a2 ); } +#define DELEGATE_TO_OBJECT_3( RetType, FuncName, ArgType1, ArgType2, ArgType3, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) { return (pDelegated)->FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_OBJECT_3V( FuncName, ArgType1, ArgType2, ArgType3, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) { (pDelegated)->FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_OBJECT_4( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) { return (pDelegated)->FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_OBJECT_4V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) { (pDelegated)->FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_OBJECT_5( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) { return (pDelegated)->FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_OBJECT_5V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_OBJECT_6( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_OBJECT_6V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_OBJECT_7( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_OBJECT_7V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_OBJECT_8( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_OBJECT_8V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_OBJECT_9( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } +#define DELEGATE_TO_OBJECT_9V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } +#define DELEGATE_TO_OBJECT_11V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, ArgType10, ArgType11, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9, ArgType10 a10, ArgType11 a11 ) { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 ); } + +#define DELEGATE_TO_OBJECT_0C( RetType, FuncName, pDelegated ) RetType FuncName() const { return (pDelegated)->FuncName(); } +#define DELEGATE_TO_OBJECT_0VC( FuncName, pDelegated ) void FuncName() const { (pDelegated)->FuncName(); } +#define DELEGATE_TO_OBJECT_1C( RetType, FuncName, ArgType1, pDelegated ) RetType FuncName( ArgType1 a1 ) const { return (pDelegated)->FuncName( a1 ); } +#define DELEGATE_TO_OBJECT_1VC( FuncName, ArgType1, pDelegated ) void FuncName( ArgType1 a1 ) const { (pDelegated)->FuncName( a1 ); } +#define DELEGATE_TO_OBJECT_2C( RetType, FuncName, ArgType1, ArgType2, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2 ) const { return (pDelegated)->FuncName( a1, a2 ); } +#define DELEGATE_TO_OBJECT_2VC( FuncName, ArgType1, ArgType2, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2 ) const { (pDelegated)->FuncName( a1, a2 ); } +#define DELEGATE_TO_OBJECT_3C( RetType, FuncName, ArgType1, ArgType2, ArgType3, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) const { return (pDelegated)->FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_OBJECT_3VC( FuncName, ArgType1, ArgType2, ArgType3, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) const { (pDelegated)->FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_OBJECT_4C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_OBJECT_4VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) const { (pDelegated)->FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_OBJECT_5C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_OBJECT_5VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) const { (pDelegated)->FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_OBJECT_6C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_OBJECT_6VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) const { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_OBJECT_7C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_OBJECT_7VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) const { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_OBJECT_8C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_OBJECT_8VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) const { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_OBJECT_9C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, pDelegated ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) const { return (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } +#define DELEGATE_TO_OBJECT_9VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, pDelegated ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) const { (pDelegated)->FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } + +#define DELEGATE_TO_BASE_0( RetType, FuncName, BaseClass ) RetType FuncName() { return BaseClass::FuncName(); } +#define DELEGATE_TO_BASE_0V( FuncName, BaseClass ) void FuncName() { BaseClass::FuncName(); } +#define DELEGATE_TO_BASE_1( RetType, FuncName, ArgType1, BaseClass ) RetType FuncName( ArgType1 a1 ) { return BaseClass::FuncName( a1 ); } +#define DELEGATE_TO_BASE_1V( FuncName, ArgType1, BaseClass ) void FuncName( ArgType1 a1 ) { BaseClass::FuncName( a1 ); } +#define DELEGATE_TO_BASE_2( RetType, FuncName, ArgType1, ArgType2, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2 ) { return BaseClass::FuncName( a1, a2 ); } +#define DELEGATE_TO_BASE_2V( FuncName, ArgType1, ArgType2, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2 ) { BaseClass::FuncName( a1, a2 ); } +#define DELEGATE_TO_BASE_3( RetType, FuncName, ArgType1, ArgType2, ArgType3, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) { return BaseClass::FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_BASE_3V( FuncName, ArgType1, ArgType2, ArgType3, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) { BaseClass::FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_BASE_4( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) { return BaseClass::FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_BASE_4V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) { BaseClass::FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_BASE_5( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) { return BaseClass::FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_BASE_5V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) { BaseClass::FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_BASE_6( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_BASE_6V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) { BaseClass::FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_BASE_7( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_BASE_7V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_BASE_8( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_BASE_8V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_BASE_9( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } +#define DELEGATE_TO_BASE_9V( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } + +#define DELEGATE_TO_BASE_0C( RetType, FuncName, BaseClass ) RetType FuncName() const { return BaseClass::FuncName(); } +#define DELEGATE_TO_BASE_0VC( FuncName, BaseClass ) void FuncName() const { BaseClass::FuncName(); } +#define DELEGATE_TO_BASE_1C( RetType, FuncName, ArgType1, BaseClass ) RetType FuncName( ArgType1 a1 ) const { return BaseClass::FuncName( a1 ); } +#define DELEGATE_TO_BASE_1VC( FuncName, ArgType1, BaseClass ) void FuncName( ArgType1 a1 ) const { BaseClass::FuncName( a1 ); } +#define DELEGATE_TO_BASE_2C( RetType, FuncName, ArgType1, ArgType2, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2 ) const { return BaseClass::FuncName( a1, a2 ); } +#define DELEGATE_TO_BASE_2VC( FuncName, ArgType1, ArgType2, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2 ) const { BaseClass::FuncName( a1, a2 ); } +#define DELEGATE_TO_BASE_3C( RetType, FuncName, ArgType1, ArgType2, ArgType3, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) const { return BaseClass::FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_BASE_3VC( FuncName, ArgType1, ArgType2, ArgType3, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3 ) const { BaseClass::FuncName( a1, a2, a3 ); } +#define DELEGATE_TO_BASE_4C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) const { return BaseClass::FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_BASE_4VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4 ) const { BaseClass::FuncName( a1, a2, a3, a4 ); } +#define DELEGATE_TO_BASE_5C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) const { return BaseClass::FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_BASE_5VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5 ) const { BaseClass::FuncName( a1, a2, a3, a4, a5 ); } +#define DELEGATE_TO_BASE_6C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) const { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_BASE_6VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6 ) const { BaseClass::FuncName( a1, a2, a3, a4, a5, a6 ); } +#define DELEGATE_TO_BASE_7C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) const { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_BASE_7VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7 ) const { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7 ); } +#define DELEGATE_TO_BASE_8C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) const { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_BASE_8VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8 ) const { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8 ); } +#define DELEGATE_TO_BASE_9C( RetType, FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, BaseClass ) RetType FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) const { return BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } +#define DELEGATE_TO_BASE_9VC( FuncName, ArgType1, ArgType2, ArgType3, ArgType4, ArgType5, ArgType6, ArgType7, ArgType8, ArgType9, BaseClass ) void FuncName( ArgType1 a1, ArgType2 a2, ArgType3 a3, ArgType4 a4, ArgType5 a5, ArgType6 a6, ArgType7 a7, ArgType8 a8, ArgType9 a9 ) const { BaseClass::FuncName( a1, a2, a3, a4, a5, a6, a7, a8, a9 ); } + +#endif // DELEGATES_H diff --git a/public/tier1/diff.h b/public/tier1/diff.h new file mode 100644 index 0000000..2348945 --- /dev/null +++ b/public/tier1/diff.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef DIFF_H +#define DIFF_H +#pragma once + +int FindDiffs(uint8 const *NewBlock, uint8 const *OldBlock, + int NewSize, int OldSize, int &DiffListSize,uint8 *Output,uint32 OutSize); + +int FindDiffsForLargeFiles(uint8 const *NewBlock, uint8 const *OldBlock, + int NewSize, int OldSize, int &DiffListSize,uint8 *Output, + uint32 OutSize, + int hashsize=65536); + +void ApplyDiffs(uint8 const *OldBlock, uint8 const *DiffList, + int OldSize, int DiffListSize, int &ResultListSize,uint8 *Output,uint32 OutSize); + +int FindDiffsLowMemory(uint8 const *NewBlock, uint8 const *OldBlock, + int NewSize, int OldSize, int &DiffListSize,uint8 *Output,uint32 OutSize); + +#endif + diff --git a/public/tier1/exprevaluator.h b/public/tier1/exprevaluator.h new file mode 100644 index 0000000..89dffc6 --- /dev/null +++ b/public/tier1/exprevaluator.h @@ -0,0 +1,74 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: ExprSimplifier builds a binary tree from an infix expression (in the +// form of a character array). +// +//===========================================================================// + +#ifndef EXPREVALUATOR_H +#define EXPREVALUATOR_H + +#if defined( _WIN32 ) +#pragma once +#endif + +static const char OR_OP = '|'; +static const char AND_OP = '&'; +static const char NOT_OP = '!'; + +#define MAX_IDENTIFIER_LEN 128 +enum Kind {CONDITIONAL, NOT, LITERAL}; + +struct ExprNode +{ + ExprNode *left; // left sub-expression + ExprNode *right; // right sub-expression + Kind kind; // kind of node this is + union + { + char cond; // the conditional + bool value; // the value + } data; +}; + +typedef ExprNode *ExprTree; + +// callback to evaluate a $ during evaluation, return true or false +typedef bool (*GetSymbolProc_t)( const char *pKey ); +typedef void (*SyntaxErrorProc_t)( const char *pReason ); + +class CExpressionEvaluator +{ +public: + CExpressionEvaluator(); + ~CExpressionEvaluator(); + bool Evaluate( bool &result, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc = 0, SyntaxErrorProc_t pSyntaxErrorProc = 0 ); + +private: + CExpressionEvaluator( CExpressionEvaluator& ); // prevent copy constructor being used + + char GetNextToken( void ); + void FreeNode( ExprNode *pNode ); + ExprNode *AllocateNode( void ); + void FreeTree( ExprTree &node ); + bool IsConditional( bool &bCondition, const char token ); + bool IsNotOp( const char token ); + bool IsIdentifierOrConstant( const char token ); + bool MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right ); + bool MakeFactor( ExprTree &tree ); + bool MakeTerm( ExprTree &tree ); + bool MakeExpression( ExprTree &tree ); + bool BuildExpression( void ); + bool SimplifyNode( ExprTree &node ); + + ExprTree m_ExprTree; // Tree representation of the expression + char m_CurToken; // Current token read from the input expression + const char *m_pExpression; // Array of the expression characters + int m_CurPosition; // Current position in the input expression + char m_Identifier[MAX_IDENTIFIER_LEN]; // Stores the identifier string + GetSymbolProc_t m_pGetSymbolProc; + SyntaxErrorProc_t m_pSyntaxErrorProc; + bool m_bSetup; +}; + +#endif diff --git a/public/tier1/fmtstr.h b/public/tier1/fmtstr.h new file mode 100644 index 0000000..59c7e8e --- /dev/null +++ b/public/tier1/fmtstr.h @@ -0,0 +1,128 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A simple class for performing safe and in-expression sprintf-style +// string formatting +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef FMTSTR_H +#define FMTSTR_H + +#include +#include +#include "tier0/platform.h" +#include "tier1/strtools.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//============================================================================= + +// using macro to be compatable with GCC +#define FmtStrVSNPrintf( szBuf, nBufSize, ppszFormat ) \ + do \ + { \ + int result; \ + va_list arg_ptr; \ + \ + va_start(arg_ptr, (*(ppszFormat))); \ + result = Q_vsnprintf((szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr); \ + va_end(arg_ptr); \ + \ + (szBuf)[(nBufSize)-1] = 0; \ + } \ + while (0) + + +//----------------------------------------------------------------------------- +// +// Purpose: String formatter with specified size +// + +template +class CFmtStrN +{ +public: + CFmtStrN() { m_szBuf[0] = 0; } + + // Standard C formatting + CFmtStrN(const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) + { + FmtStrVSNPrintf(m_szBuf, SIZE_BUF, &pszFormat); + } + + // Use this for pass-through formatting + CFmtStrN(const char ** ppszFormat, ...) + { + FmtStrVSNPrintf(m_szBuf, SIZE_BUF, ppszFormat); + } + + // Explicit reformat + const char *sprintf(const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) + { + FmtStrVSNPrintf(m_szBuf, SIZE_BUF, &pszFormat); + return m_szBuf; + } + + // Use this for pass-through formatting + void VSprintf(const char **ppszFormat, ...) + { + FmtStrVSNPrintf(m_szBuf, SIZE_BUF, ppszFormat); + } + + // Use for access + operator const char *() const { return m_szBuf; } + char *Access() { return m_szBuf; } + CFmtStrN & operator=( const char *pchValue ) { sprintf( pchValue ); return *this; } + CFmtStrN & operator+=( const char *pchValue ) { Append( pchValue ); return *this; } + int Length() const { return V_strlen( m_szBuf ); } + + void Clear() { m_szBuf[0] = 0; } + + void AppendFormat( const char *pchFormat, ... ) { int nLength = Length(); char *pchEnd = m_szBuf + nLength; FmtStrVSNPrintf( pchEnd, SIZE_BUF - nLength, &pchFormat ); } + void AppendFormatV( const char *pchFormat, va_list args ); + void Append( const char *pchValue ) { AppendFormat( pchValue ); } + + void AppendIndent( uint32 unCount, char chIndent = '\t' ); +private: + char m_szBuf[SIZE_BUF]; +}; + + +template< int SIZE_BUF > +void CFmtStrN::AppendIndent( uint32 unCount, char chIndent ) +{ + int nLength = Length(); + if( nLength + unCount >= SIZE_BUF ) + unCount = SIZE_BUF - (1+nLength); + for ( uint32 x = 0; x < unCount; x++ ) + { + m_szBuf[ nLength++ ] = chIndent; + } + m_szBuf[ nLength ] = '\0'; +} + +template< int SIZE_BUF > +void CFmtStrN::AppendFormatV( const char *pchFormat, va_list args ) +{ + int nLength = Length(); + V_vsnprintf( m_szBuf+nLength, SIZE_BUF - nLength, pchFormat, args ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Default-sized string formatter +// + +#define FMTSTR_STD_LEN 256 + +typedef CFmtStrN CFmtStr; +typedef CFmtStrN<1024> CFmtStr1024; +typedef CFmtStrN<8192> CFmtStrMax; + +//============================================================================= + +#endif // FMTSTR_H diff --git a/public/tier1/functors.h b/public/tier1/functors.h new file mode 100644 index 0000000..1a451dc --- /dev/null +++ b/public/tier1/functors.h @@ -0,0 +1,903 @@ +//========== Copyright © 2006, Valve Corporation, All rights reserved. ======== +// +// Purpose: Implements a generic infrastucture for functors combining +// a number of techniques to provide transparent parameter type +// deduction and packaging. Supports both member and non-member functions. +// +// See also: http://en.wikipedia.org/wiki/Function_object +// +// Note that getting into the guts of this file is not for the +// feint of heart. The core concept here is that the compiler can +// figure out all the parameter types. +// +// E.g.: +// +// struct CMyClass +// { +// void foo( int i) {} +// }; +// +// int bar(int i) { return i; } +// +// CMyClass myInstance; +// +// CFunctor *pFunctor = CreateFunctor( &myInstance, CMyClass::foo, 8675 ); +// CFunctor *pFunctor2 = CreateFunctor( &bar, 309 ); +// +// void CallEm() +// { +// (*pFunctor)(); +// (*pFunctor2)(); +// } +// +//============================================================================= + +#ifndef FUNCTORS_H +#define FUNCTORS_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier1/refcount.h" +#include "tier1/utlenvelope.h" +#include + + +//----------------------------------------------------------------------------- +// +// Macros used as basis for template generation. Just ignore the man behind the +// curtain +// +//----------------------------------------------------------------------------- + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_0 +#define FUNC_TEMPLATE_ARG_PARAMS_0 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_0 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_0 +#define FUNC_ARG_MEMBERS_0 +#define FUNC_ARG_FORMAL_PARAMS_0 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_0 +#define FUNC_CALL_ARGS_INIT_0 +#define FUNC_SOLO_CALL_ARGS_INIT_0 +#define FUNC_CALL_MEMBER_ARGS_0 +#define FUNC_CALL_ARGS_0 +#define FUNC_CALL_DATA_ARGS_0( _var ) +#define FUNC_FUNCTOR_CALL_ARGS_0 +#define FUNC_TEMPLATE_FUNC_PARAMS_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_0 +#define FUNC_VALIDATION_STRING_0 Q_snprintf( pString, nBufLen, "method( void )" ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_1 typename ARG_TYPE_1 +#define FUNC_TEMPLATE_ARG_PARAMS_1 , typename ARG_TYPE_1 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_1 , ARG_TYPE_1 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_1 ARG_TYPE_1 +#define FUNC_ARG_MEMBERS_1 ARG_TYPE_1 m_arg1 +#define FUNC_ARG_FORMAL_PARAMS_1 , const ARG_TYPE_1 &arg1 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_1 const ARG_TYPE_1 &arg1 +#define FUNC_CALL_ARGS_INIT_1 , m_arg1( arg1 ) +#define FUNC_SOLO_CALL_ARGS_INIT_1 : m_arg1( arg1 ) +#define FUNC_CALL_MEMBER_ARGS_1 m_arg1 +#define FUNC_CALL_ARGS_1 arg1 +#define FUNC_CALL_DATA_ARGS_1( _var ) _var->m_arg1 +#define FUNC_FUNCTOR_CALL_ARGS_1 , arg1 +#define FUNC_TEMPLATE_FUNC_PARAMS_1 , typename FUNC_ARG_TYPE_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 FUNC_ARG_TYPE_1 +#define FUNC_VALIDATION_STRING_1 Q_snprintf( pString, nBufLen, "method( %s )", typeid( ARG_TYPE_1 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_2 typename ARG_TYPE_1, typename ARG_TYPE_2 +#define FUNC_TEMPLATE_ARG_PARAMS_2 , typename ARG_TYPE_1, typename ARG_TYPE_2 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_2 , ARG_TYPE_1, ARG_TYPE_2 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_2 ARG_TYPE_1, ARG_TYPE_2 +#define FUNC_ARG_MEMBERS_2 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2 +#define FUNC_ARG_FORMAL_PARAMS_2 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_2 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2 +#define FUNC_CALL_ARGS_INIT_2 , m_arg1( arg1 ), m_arg2( arg2 ) +#define FUNC_SOLO_CALL_ARGS_INIT_2 : m_arg1( arg1 ), m_arg2( arg2 ) +#define FUNC_CALL_MEMBER_ARGS_2 m_arg1, m_arg2 +#define FUNC_CALL_ARGS_2 arg1, arg2 +#define FUNC_CALL_DATA_ARGS_2( _var ) _var->m_arg1, _var->m_arg2 +#define FUNC_FUNCTOR_CALL_ARGS_2 , arg1, arg2 +#define FUNC_TEMPLATE_FUNC_PARAMS_2 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2 +#define FUNC_VALIDATION_STRING_2 Q_snprintf( pString, nBufLen, "method( %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_3 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3 +#define FUNC_TEMPLATE_ARG_PARAMS_3 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_3 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_3 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3 +#define FUNC_ARG_MEMBERS_3 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3 +#define FUNC_ARG_FORMAL_PARAMS_3 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_3 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3 +#define FUNC_CALL_ARGS_INIT_3 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ) +#define FUNC_SOLO_CALL_ARGS_INIT_3 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ) +#define FUNC_CALL_MEMBER_ARGS_3 m_arg1, m_arg2, m_arg3 +#define FUNC_CALL_ARGS_3 arg1, arg2, arg3 +#define FUNC_CALL_DATA_ARGS_3( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3 +#define FUNC_FUNCTOR_CALL_ARGS_3 , arg1, arg2, arg3 +#define FUNC_TEMPLATE_FUNC_PARAMS_3 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3 +#define FUNC_VALIDATION_STRING_3 Q_snprintf( pString, nBufLen, "method( %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_4 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4 +#define FUNC_TEMPLATE_ARG_PARAMS_4 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_4 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_4 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4 +#define FUNC_ARG_MEMBERS_4 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4 +#define FUNC_ARG_FORMAL_PARAMS_4 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_4 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4 +#define FUNC_CALL_ARGS_INIT_4 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ) +#define FUNC_SOLO_CALL_ARGS_INIT_4 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ) +#define FUNC_CALL_MEMBER_ARGS_4 m_arg1, m_arg2, m_arg3, m_arg4 +#define FUNC_CALL_ARGS_4 arg1, arg2, arg3, arg4 +#define FUNC_CALL_DATA_ARGS_4( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4 +#define FUNC_FUNCTOR_CALL_ARGS_4 , arg1, arg2, arg3, arg4 +#define FUNC_TEMPLATE_FUNC_PARAMS_4 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4 +#define FUNC_VALIDATION_STRING_4 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_5 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5 +#define FUNC_TEMPLATE_ARG_PARAMS_5 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_5 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_5 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5 +#define FUNC_ARG_MEMBERS_5 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5 +#define FUNC_ARG_FORMAL_PARAMS_5 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_5 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5 +#define FUNC_CALL_ARGS_INIT_5 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ) +#define FUNC_SOLO_CALL_ARGS_INIT_5 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ) +#define FUNC_CALL_MEMBER_ARGS_5 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5 +#define FUNC_CALL_ARGS_5 arg1, arg2, arg3, arg4, arg5 +#define FUNC_CALL_DATA_ARGS_5( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5 +#define FUNC_FUNCTOR_CALL_ARGS_5 , arg1, arg2, arg3, arg4, arg5 +#define FUNC_TEMPLATE_FUNC_PARAMS_5 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5 +#define FUNC_VALIDATION_STRING_5 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_6 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6 +#define FUNC_TEMPLATE_ARG_PARAMS_6 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_6 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_6 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6 +#define FUNC_ARG_MEMBERS_6 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6 +#define FUNC_ARG_FORMAL_PARAMS_6 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_6 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6 +#define FUNC_CALL_ARGS_INIT_6 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ) +#define FUNC_SOLO_CALL_ARGS_INIT_6 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ) +#define FUNC_CALL_MEMBER_ARGS_6 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6 +#define FUNC_CALL_ARGS_6 arg1, arg2, arg3, arg4, arg5, arg6 +#define FUNC_CALL_DATA_ARGS_6( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6 +#define FUNC_FUNCTOR_CALL_ARGS_6 , arg1, arg2, arg3, arg4, arg5, arg6 +#define FUNC_TEMPLATE_FUNC_PARAMS_6 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6 +#define FUNC_VALIDATION_STRING_6 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_7 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7 +#define FUNC_TEMPLATE_ARG_PARAMS_7 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_7 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_7 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7 +#define FUNC_ARG_MEMBERS_7 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; +#define FUNC_ARG_FORMAL_PARAMS_7 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_7 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7 +#define FUNC_CALL_ARGS_INIT_7 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ) +#define FUNC_SOLO_CALL_ARGS_INIT_7 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ) +#define FUNC_CALL_MEMBER_ARGS_7 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7 +#define FUNC_CALL_ARGS_7 arg1, arg2, arg3, arg4, arg5, arg6, arg7 +#define FUNC_CALL_DATA_ARGS_7( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7 +#define FUNC_FUNCTOR_CALL_ARGS_7 , arg1, arg2, arg3, arg4, arg5, arg6, arg7 +#define FUNC_TEMPLATE_FUNC_PARAMS_7 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7 +#define FUNC_VALIDATION_STRING_7 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_8 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8 +#define FUNC_TEMPLATE_ARG_PARAMS_8 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_8 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_8 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8 +#define FUNC_ARG_MEMBERS_8 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; +#define FUNC_ARG_FORMAL_PARAMS_8 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_8 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8 +#define FUNC_CALL_ARGS_INIT_8 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ) +#define FUNC_SOLO_CALL_ARGS_INIT_8 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ) +#define FUNC_CALL_MEMBER_ARGS_8 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8 +#define FUNC_CALL_ARGS_8 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 +#define FUNC_CALL_DATA_ARGS_8( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8 +#define FUNC_FUNCTOR_CALL_ARGS_8 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 +#define FUNC_TEMPLATE_FUNC_PARAMS_8 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8 +#define FUNC_VALIDATION_STRING_8 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_9 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9 +#define FUNC_TEMPLATE_ARG_PARAMS_9 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_9 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_9 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9 +#define FUNC_ARG_MEMBERS_9 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; +#define FUNC_ARG_FORMAL_PARAMS_9 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_9 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9 +#define FUNC_CALL_ARGS_INIT_9 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ) +#define FUNC_SOLO_CALL_ARGS_INIT_9 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ) +#define FUNC_CALL_MEMBER_ARGS_9 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9 +#define FUNC_CALL_ARGS_9 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 +#define FUNC_CALL_DATA_ARGS_9( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9 +#define FUNC_FUNCTOR_CALL_ARGS_9 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 +#define FUNC_TEMPLATE_FUNC_PARAMS_9 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9 +#define FUNC_VALIDATION_STRING_9 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_10 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10 +#define FUNC_TEMPLATE_ARG_PARAMS_10 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_10 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_10 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10 +#define FUNC_ARG_MEMBERS_10 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; ARG_TYPE_10 m_arg10; +#define FUNC_ARG_FORMAL_PARAMS_10 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_10 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10 +#define FUNC_CALL_ARGS_INIT_10 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ) +#define FUNC_SOLO_CALL_ARGS_INIT_10 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ) +#define FUNC_CALL_MEMBER_ARGS_10 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10 +#define FUNC_CALL_ARGS_10 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 +#define FUNC_CALL_DATA_ARGS_10( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9, _var->m_arg10 +#define FUNC_FUNCTOR_CALL_ARGS_10 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 +#define FUNC_TEMPLATE_FUNC_PARAMS_10 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9, typename FUNC_ARG_TYPE_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9, FUNC_ARG_TYPE_10 +#define FUNC_VALIDATION_STRING_10 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name(), typeid( ARG_TYPE_10 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_11 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11 +#define FUNC_TEMPLATE_ARG_PARAMS_11 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_11 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_11 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11 +#define FUNC_ARG_MEMBERS_11 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; ARG_TYPE_10 m_arg10; ARG_TYPE_11 m_arg11 +#define FUNC_ARG_FORMAL_PARAMS_11 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_11 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11 +#define FUNC_CALL_ARGS_INIT_11 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ) +#define FUNC_SOLO_CALL_ARGS_INIT_11 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ) +#define FUNC_CALL_MEMBER_ARGS_11 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10, m_arg11 +#define FUNC_CALL_ARGS_11 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 +#define FUNC_CALL_DATA_ARGS_11( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9, _var->m_arg10, _var->m_arg11 +#define FUNC_FUNCTOR_CALL_ARGS_11 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 +#define FUNC_TEMPLATE_FUNC_PARAMS_11 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9, typename FUNC_ARG_TYPE_10, typename FUNC_ARG_TYPE_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9, FUNC_ARG_TYPE_10, FUNC_ARG_TYPE_11 +#define FUNC_VALIDATION_STRING_11 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name(), typeid( ARG_TYPE_10 ).name(), typeid( ARG_TYPE_11 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_12 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12 +#define FUNC_TEMPLATE_ARG_PARAMS_12 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_12 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_12 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12 +#define FUNC_ARG_MEMBERS_12 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; ARG_TYPE_10 m_arg10; ARG_TYPE_11 m_arg11; ARG_TYPE_12 m_arg12 +#define FUNC_ARG_FORMAL_PARAMS_12 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_12 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12 +#define FUNC_CALL_ARGS_INIT_12 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ) +#define FUNC_SOLO_CALL_ARGS_INIT_12 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ) +#define FUNC_CALL_MEMBER_ARGS_12 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10, m_arg11, m_arg12 +#define FUNC_CALL_ARGS_12 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 +#define FUNC_CALL_DATA_ARGS_12( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9, _var->m_arg10, _var->m_arg11, _var->m_arg12 +#define FUNC_FUNCTOR_CALL_ARGS_12 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12 +#define FUNC_TEMPLATE_FUNC_PARAMS_12 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9, typename FUNC_ARG_TYPE_10, typename FUNC_ARG_TYPE_11, typename FUNC_ARG_TYPE_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9, FUNC_ARG_TYPE_10, FUNC_ARG_TYPE_11, FUNC_ARG_TYPE_12 +#define FUNC_VALIDATION_STRING_12 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name(), typeid( ARG_TYPE_10 ).name(), typeid( ARG_TYPE_11 ).name(), typeid( ARG_TYPE_12 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_13 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12, typename ARG_TYPE_13 +#define FUNC_TEMPLATE_ARG_PARAMS_13 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12, typename ARG_TYPE_13 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_13 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12, ARG_TYPE_13 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_13 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12, ARG_TYPE_13 +#define FUNC_ARG_MEMBERS_13 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; ARG_TYPE_10 m_arg10; ARG_TYPE_11 m_arg11; ARG_TYPE_12 m_arg12; ARG_TYPE_13 m_arg13 +#define FUNC_ARG_FORMAL_PARAMS_13 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12, const ARG_TYPE_13 &arg13 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_13 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12, const ARG_TYPE_13 &arg13 +#define FUNC_CALL_ARGS_INIT_13 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ), m_arg13( arg13 ) +#define FUNC_SOLO_CALL_ARGS_INIT_13 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ), m_arg13( arg13 ) +#define FUNC_CALL_MEMBER_ARGS_13 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10, m_arg11, m_arg12, m_arg13 +#define FUNC_CALL_ARGS_13 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13 +#define FUNC_CALL_DATA_ARGS_13( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9, _var->m_arg10, _var->m_arg11, _var->m_arg12, _var->m_arg13 +#define FUNC_FUNCTOR_CALL_ARGS_13 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13 +#define FUNC_TEMPLATE_FUNC_PARAMS_13 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9, typename FUNC_ARG_TYPE_10, typename FUNC_ARG_TYPE_11, typename FUNC_ARG_TYPE_12, typename FUNC_ARG_TYPE_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9, FUNC_ARG_TYPE_10, FUNC_ARG_TYPE_11, FUNC_ARG_TYPE_12, FUNC_ARG_TYPE_13 +#define FUNC_VALIDATION_STRING_13 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name(), typeid( ARG_TYPE_10 ).name(), typeid( ARG_TYPE_11 ).name(), typeid( ARG_TYPE_12 ).name(), typeid( ARG_TYPE_13 ).name() ); + +#define FUNC_SOLO_TEMPLATE_ARG_PARAMS_14 typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12, typename ARG_TYPE_13, typename ARG_TYPE_14 +#define FUNC_TEMPLATE_ARG_PARAMS_14 , typename ARG_TYPE_1, typename ARG_TYPE_2, typename ARG_TYPE_3, typename ARG_TYPE_4, typename ARG_TYPE_5, typename ARG_TYPE_6, typename ARG_TYPE_7, typename ARG_TYPE_8, typename ARG_TYPE_9, typename ARG_TYPE_10, typename ARG_TYPE_11, typename ARG_TYPE_12, typename ARG_TYPE_13, typename ARG_TYPE_14 +#define FUNC_BASE_TEMPLATE_ARG_PARAMS_14 , ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12, ARG_TYPE_13, ARG_TYPE_14 +#define FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_14 ARG_TYPE_1, ARG_TYPE_2, ARG_TYPE_3, ARG_TYPE_4, ARG_TYPE_5, ARG_TYPE_6, ARG_TYPE_7, ARG_TYPE_8, ARG_TYPE_9, ARG_TYPE_10, ARG_TYPE_11, ARG_TYPE_12, ARG_TYPE_13, ARG_TYPE_14 +#define FUNC_ARG_MEMBERS_14 ARG_TYPE_1 m_arg1; ARG_TYPE_2 m_arg2; ARG_TYPE_3 m_arg3; ARG_TYPE_4 m_arg4; ARG_TYPE_5 m_arg5; ARG_TYPE_6 m_arg6; ARG_TYPE_7 m_arg7; ARG_TYPE_8 m_arg8; ARG_TYPE_9 m_arg9; ARG_TYPE_10 m_arg10; ARG_TYPE_11 m_arg11; ARG_TYPE_12 m_arg12; ARG_TYPE_13 m_arg13; ARG_TYPE_14 m_arg14 +#define FUNC_ARG_FORMAL_PARAMS_14 , const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12, const ARG_TYPE_13 &arg13, const ARG_TYPE_14 &arg14 +#define FUNC_PROXY_ARG_FORMAL_PARAMS_14 const ARG_TYPE_1 &arg1, const ARG_TYPE_2 &arg2, const ARG_TYPE_3 &arg3, const ARG_TYPE_4 &arg4, const ARG_TYPE_5 &arg5, const ARG_TYPE_6 &arg6, const ARG_TYPE_7 &arg7, const ARG_TYPE_8 &arg8, const ARG_TYPE_9 &arg9, const ARG_TYPE_10 &arg10, const ARG_TYPE_11 &arg11, const ARG_TYPE_12 &arg12, const ARG_TYPE_13 &arg13, const ARG_TYPE_14 &arg14 +#define FUNC_CALL_ARGS_INIT_14 , m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ), m_arg13( arg13 ), m_arg14( arg14 ) +#define FUNC_SOLO_CALL_ARGS_INIT_14 : m_arg1( arg1 ), m_arg2( arg2 ), m_arg3( arg3 ), m_arg4( arg4 ), m_arg5( arg5 ), m_arg6( arg6 ), m_arg7( arg7 ), m_arg8( arg8 ), m_arg9( arg9 ), m_arg10( arg10 ), m_arg11( arg11 ), m_arg12( arg12 ), m_arg13( arg13 ), m_arg14( arg14 ) +#define FUNC_CALL_MEMBER_ARGS_14 m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6, m_arg7, m_arg8, m_arg9, m_arg10, m_arg11, m_arg12, m_arg13, m_arg14 +#define FUNC_CALL_ARGS_14 arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14 +#define FUNC_CALL_DATA_ARGS_14( _var ) _var->m_arg1, _var->m_arg2, _var->m_arg3, _var->m_arg4, _var->m_arg5, _var->m_arg6, _var->m_arg7, _var->m_arg8, _var->m_arg9, _var->m_arg10, _var->m_arg11, _var->m_arg12, _var->m_arg13, _var->m_arg14 +#define FUNC_FUNCTOR_CALL_ARGS_14 , arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14 +#define FUNC_TEMPLATE_FUNC_PARAMS_14 , typename FUNC_ARG_TYPE_1, typename FUNC_ARG_TYPE_2, typename FUNC_ARG_TYPE_3, typename FUNC_ARG_TYPE_4, typename FUNC_ARG_TYPE_5, typename FUNC_ARG_TYPE_6, typename FUNC_ARG_TYPE_7, typename FUNC_ARG_TYPE_8, typename FUNC_ARG_TYPE_9, typename FUNC_ARG_TYPE_10, typename FUNC_ARG_TYPE_11, typename FUNC_ARG_TYPE_12, typename FUNC_ARG_TYPE_13, typename FUNC_ARG_TYPE_14 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 FUNC_ARG_TYPE_1, FUNC_ARG_TYPE_2, FUNC_ARG_TYPE_3, FUNC_ARG_TYPE_4, FUNC_ARG_TYPE_5, FUNC_ARG_TYPE_6, FUNC_ARG_TYPE_7, FUNC_ARG_TYPE_8, FUNC_ARG_TYPE_9, FUNC_ARG_TYPE_10, FUNC_ARG_TYPE_11, FUNC_ARG_TYPE_12, FUNC_ARG_TYPE_13, FUNC_ARG_TYPE_14 +#define FUNC_VALIDATION_STRING_14 Q_snprintf( pString, nBufLen, "method( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )", typeid( ARG_TYPE_1 ).name(), typeid( ARG_TYPE_2 ).name(), typeid( ARG_TYPE_3 ).name(), typeid( ARG_TYPE_4 ).name(), typeid( ARG_TYPE_5 ).name(), typeid( ARG_TYPE_6 ).name(), typeid( ARG_TYPE_7 ).name(), typeid( ARG_TYPE_8 ).name(), typeid( ARG_TYPE_9 ).name(), typeid( ARG_TYPE_10 ).name(), typeid( ARG_TYPE_11 ).name(), typeid( ARG_TYPE_12 ).name(), typeid( ARG_TYPE_13 ).name(), typeid( ARG_TYPE_14 ).name() ); + +#define FUNC_GENERATE_ALL_BUT0( INNERMACRONAME ) \ + INNERMACRONAME(1); \ + INNERMACRONAME(2); \ + INNERMACRONAME(3); \ + INNERMACRONAME(4); \ + INNERMACRONAME(5); \ + INNERMACRONAME(6); \ + INNERMACRONAME(7); \ + INNERMACRONAME(8); \ + INNERMACRONAME(9); \ + INNERMACRONAME(10);\ + INNERMACRONAME(11);\ + INNERMACRONAME(12);\ + INNERMACRONAME(13);\ + INNERMACRONAME(14) + +#define FUNC_GENERATE_ALL( INNERMACRONAME ) \ + INNERMACRONAME(0); \ + FUNC_GENERATE_ALL_BUT0( INNERMACRONAME ) + + +//----------------------------------------------------------------------------- +// +// Purpose: Base class of all function objects +// +//----------------------------------------------------------------------------- +abstract_class CFunctor : public IRefCounted +{ +public: + CFunctor() + { +#ifdef DEBUG + m_nUserID = 0; +#endif + } + virtual void operator()() = 0; + + unsigned m_nUserID; // For debugging +}; + + +//----------------------------------------------------------------------------- +// NOTE: Functor data + functor callback are tied together +// The basic idea is that someone creates the functor data. At a later point, +// the functor data is passed to a functor callback. Validation strings +// are compared in debug builds to ensure the data matches the callback +//----------------------------------------------------------------------------- +abstract_class CFunctorData : public IRefCounted +{ +public: + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const = 0; +}; + +abstract_class CFunctorCallback : public IRefCounted +{ +public: + virtual bool IsEqual( CFunctorCallback *pSrc ) const = 0; + virtual void operator()( CFunctorData *pData ) = 0; + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const = 0; + virtual const char *GetImplClassName() const = 0; + virtual const void *GetTarget() const = 0; +}; + + +//----------------------------------------------------------------------------- +// When calling through a functor, care needs to be taken to not pass objects that might go away. +// Since this code determines the type to store in the functor based on the actual arguments, +// this is achieved by changing the point of call. +// +// See also CUtlEnvelope +//----------------------------------------------------------------------------- +// convert a reference to a passable value +template +inline T RefToVal(const T &item) +{ + return item; +} + + +//----------------------------------------------------------------------------- +// This class can be used to pass into a functor a proxy object for a pointer +// to be resolved later. For example, you can execute a "call" on a resource +// whose actual value is not known until a later time +//----------------------------------------------------------------------------- +template +class CLateBoundPtr +{ +public: + CLateBoundPtr( T **ppObject ) + : m_ppObject( ppObject ) + { + } + + T *operator->() { return *m_ppObject; } + T &operator *() { return **m_ppObject; } + operator T *() const { return (T*)(*m_ppObject); } + operator void *() { return *m_ppObject; } + +private: + T **m_ppObject; +}; + + +//----------------------------------------------------------------------------- +// +// Purpose: Classes to define memory management policies when operating +// on pointers to members +// +//----------------------------------------------------------------------------- +class CFuncMemPolicyNone +{ +public: + static void OnAcquire(void *pObject) {} + static void OnRelease(void *pObject) {} +}; + +template +class CFuncMemPolicyRefCount +{ +public: + static void OnAcquire(OBJECT_TYPE_PTR pObject) { pObject->AddRef(); } + static void OnRelease(OBJECT_TYPE_PTR pObject) { pObject->Release(); } +}; + + +//----------------------------------------------------------------------------- +// +// Purpose: Function proxy is a generic facility for holding a function +// pointer. Can be used on own, though primarily for use +// by this file +// +//----------------------------------------------------------------------------- +template +class CMemberFuncProxyBase +{ +public: + bool operator==( const CMemberFuncProxyBase &src ) const + { + return m_pfnProxied == src.m_pfnProxied && m_pObject == src.m_pObject; + } + + const void *GetTarget() const + { + return m_pObject; + } + +protected: + CMemberFuncProxyBase( OBJECT_TYPE_PTR pObject, FUNCTION_TYPE pfnProxied ) + : m_pObject( pObject ), + m_pfnProxied( pfnProxied ) + { + MEM_POLICY::OnAcquire(m_pObject); + } + + ~CMemberFuncProxyBase() + { + MEM_POLICY::OnRelease(m_pObject); + } + + void Set( OBJECT_TYPE_PTR pObject, FUNCTION_TYPE pfnProxied ) + { + m_pfnProxied = pfnProxied; + m_pObject = pObject; + } + + void OnCall() + { + Assert( (void *)m_pObject != NULL ); + } + + FUNCTION_TYPE m_pfnProxied; + OBJECT_TYPE_PTR m_pObject; +}; + + +#define DEFINE_MEMBER_FUNC_PROXY( N ) \ + template \ + class CMemberFuncProxy##N : public CMemberFuncProxyBase \ + { \ + public: \ + CMemberFuncProxy##N( OBJECT_TYPE_PTR pObject = NULL, FUNCTION_TYPE pfnProxied = NULL ) \ + : CMemberFuncProxyBase( pObject, pfnProxied ) \ + { \ + } \ + \ + void operator()( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + this->OnCall(); \ + ((*this->m_pObject).*this->m_pfnProxied)( FUNC_CALL_ARGS_##N ); \ + } \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_PROXY ); + + +//----------------------------------------------------------------------------- +// +// The actual functor implementation +// +//----------------------------------------------------------------------------- + +#include "tier0/memdbgon.h" + +typedef CRefCounted1 CFunctorBase; + +#define DEFINE_FUNCTOR_TEMPLATE(N) \ + template \ + class CFunctor##N : public CFunctorBase \ + { \ + public: \ + CFunctor##N( FUNC_TYPE pfnProxied FUNC_ARG_FORMAL_PARAMS_##N ) : m_pfnProxied( pfnProxied ) FUNC_CALL_ARGS_INIT_##N {} \ + void operator()() { m_pfnProxied(FUNC_CALL_MEMBER_ARGS_##N); } \ + \ + private: \ + FUNC_TYPE m_pfnProxied; \ + FUNC_ARG_MEMBERS_##N; \ + } + +FUNC_GENERATE_ALL( DEFINE_FUNCTOR_TEMPLATE ); + +#define DEFINE_MEMBER_FUNCTOR( N ) \ + template \ + class CMemberFunctor##N : public FUNCTOR_BASE \ + { \ + public: \ + CMemberFunctor##N( OBJECT_TYPE_PTR pObject, FUNCTION_TYPE pfnProxied FUNC_ARG_FORMAL_PARAMS_##N ) : m_Proxy( pObject, pfnProxied ) FUNC_CALL_ARGS_INIT_##N {} \ + void operator()() { m_Proxy(FUNC_CALL_MEMBER_ARGS_##N); } \ + \ + private: \ + CMemberFuncProxy##N m_Proxy; \ + FUNC_ARG_MEMBERS_##N; \ + }; + + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR ); + +typedef CRefCounted1 CFunctorDataBase; +class CFunctorCallbackBase : public CRefCounted1 +{ +protected: + virtual void ValidateFunctorData( CFunctorData *pData ) + { +#ifdef _DEBUG + char pDataString[1024]; + char pCallbackString[1024]; + ComputeValidationString( pCallbackString, sizeof(pCallbackString) ); + pData->ComputeValidationString( pDataString, sizeof(pDataString) ); + bool bMatch = !Q_stricmp( pDataString, pCallbackString ); + if ( !bMatch ) + { + Warning( "Functor doesn't match data!\n\tExpected:\t%s\n\tEncountered:\t%s\n", + pCallbackString, pDataString ); + Assert( 0 ); + } +#endif + } +}; + +#define DEFINE_FUNCTOR_DATA_TEMPLATE(N) \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N > \ + class CFunctorData##N : public CFunctorDataBase \ + { \ + public: \ + CFunctorData##N( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) FUNC_SOLO_CALL_ARGS_INIT_##N {} \ + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_##N } \ + FUNC_ARG_MEMBERS_##N; \ + } + +class CFunctorData0 : public CFunctorDataBase +{ +public: + CFunctorData0( ) {} + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_0 } +}; + +FUNC_GENERATE_ALL_BUT0( DEFINE_FUNCTOR_DATA_TEMPLATE ); + +#define DEFINE_FUNCTOR_CALLBACK_TEMPLATE(N) \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N > \ + class CFunctorCallback##N : public CFunctorCallbackBase \ + { \ + typedef void (*Callback_t)( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ); \ + public: \ + CFunctorCallback##N( Callback_t pfnProxied ) : m_pfnProxied( pfnProxied ) {} \ + void operator()( CFunctorData *pFunctorDataBase ) \ + { \ + ValidateFunctorData( pFunctorDataBase ); \ + CFunctorData##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N > *pFunctorData = static_cast< CFunctorData##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N >* >( pFunctorDataBase ); \ + m_pfnProxied( FUNC_CALL_DATA_ARGS_##N(pFunctorData) ); \ + } \ + virtual bool IsEqual( CFunctorCallback *pSrc ) const { return !Q_stricmp( GetImplClassName(), pSrc->GetImplClassName() ) && ( m_pfnProxied == static_cast< CFunctorCallback##N * >( pSrc )->m_pfnProxied ); } \ + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_##N } \ + virtual const char *GetImplClassName() const { return "CFunctorCallback" #N; } \ + virtual const void *GetTarget() const { return m_pfnProxied; } \ + private: \ + Callback_t m_pfnProxied; \ + } + +class CFunctorCallback0 : public CFunctorCallbackBase +{ + typedef void (*Callback_t)( ); +public: + CFunctorCallback0( Callback_t pfnProxied ) : m_pfnProxied( pfnProxied ) {} + void operator()( CFunctorData *pFunctorDataBase ) + { + ValidateFunctorData( pFunctorDataBase ); + m_pfnProxied( ); + } + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_0 } + virtual bool IsEqual( CFunctorCallback *pSrc ) const + { + if ( Q_stricmp( GetImplClassName(), pSrc->GetImplClassName() ) ) + return false; + return m_pfnProxied == static_cast< CFunctorCallback0* >( pSrc )->m_pfnProxied; + } + virtual const char *GetImplClassName() const { return "CFunctorCallback0"; } + virtual const void *GetTarget() const { return (const void *) m_pfnProxied; } +private: + Callback_t m_pfnProxied; +}; + +FUNC_GENERATE_ALL_BUT0( DEFINE_FUNCTOR_CALLBACK_TEMPLATE ); + +#define DEFINE_MEMBER_FUNCTOR_CALLBACK_TEMPLATE( N ) \ + template < class FUNCTION_CLASS FUNC_TEMPLATE_ARG_PARAMS_##N, class MEM_POLICY = CFuncMemPolicyNone > \ + class CMemberFunctorCallback##N : public CFunctorCallbackBase \ + { \ + typedef void (FUNCTION_CLASS::*MemberCallback_t)( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ); \ + public: \ + CMemberFunctorCallback##N( FUNCTION_CLASS *pObject, MemberCallback_t pfnProxied ) : m_Proxy( pObject, pfnProxied ) {} \ + void operator()( CFunctorData *pFunctorDataBase ) \ + { \ + ValidateFunctorData( pFunctorDataBase ); \ + CFunctorData##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N > *pFunctorData = static_cast< CFunctorData##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N >* >( pFunctorDataBase ); \ + m_Proxy( FUNC_CALL_DATA_ARGS_##N( pFunctorData ) ); \ + } \ + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_##N } \ + virtual bool IsEqual( CFunctorCallback *pSrc ) const { return !Q_stricmp( GetImplClassName(), pSrc->GetImplClassName() ) && ( m_Proxy == static_cast< CMemberFunctorCallback##N* >( pSrc )->m_Proxy ); } \ + virtual const char *GetImplClassName() const { return "CMemberFunctorCallback" #N; } \ + virtual const void *GetTarget() const { return m_Proxy.GetTarget(); } \ + private: \ + CMemberFuncProxy##N< FUNCTION_CLASS *, MemberCallback_t FUNC_BASE_TEMPLATE_ARG_PARAMS_##N, MEM_POLICY> m_Proxy; \ + } + +template < class FUNCTION_CLASS, class MEM_POLICY = CFuncMemPolicyNone > +class CMemberFunctorCallback0 : public CFunctorCallbackBase +{ + typedef void (FUNCTION_CLASS::*MemberCallback_t)( ); +public: + CMemberFunctorCallback0( FUNCTION_CLASS *pObject, MemberCallback_t pfnProxied ) : m_Proxy( pObject, pfnProxied ) {} + void operator()( CFunctorData *pFunctorDataBase ) + { + ValidateFunctorData( pFunctorDataBase ); + m_Proxy( ); + } + virtual void ComputeValidationString( char *pString, size_t nBufLen ) const { FUNC_VALIDATION_STRING_0 } + virtual bool IsEqual( CFunctorCallback *pSrc ) const + { + if ( Q_stricmp( GetImplClassName(), pSrc->GetImplClassName() ) ) + return false; + return m_Proxy == static_cast< CMemberFunctorCallback0 * >( pSrc )->m_Proxy; + } + virtual const char *GetImplClassName() const { return "CMemberFunctorCallback0"; } + virtual const void *GetTarget() const { return m_Proxy.GetTarget(); } +private: + CMemberFuncProxy0< FUNCTION_CLASS *, MemberCallback_t, MEM_POLICY > m_Proxy; +}; + +FUNC_GENERATE_ALL_BUT0( DEFINE_MEMBER_FUNCTOR_CALLBACK_TEMPLATE ); + + +//----------------------------------------------------------------------------- +// +// The real magic, letting the compiler figure out all the right template parameters +// +//----------------------------------------------------------------------------- + +#define DEFINE_NONMEMBER_FUNCTOR_FACTORY(N) \ + template \ + inline CFunctor *CreateFunctor(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return new CFunctor##N( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNCTOR_FACTORY ); + +//------------------------------------- + +#define DEFINE_MEMBER_FUNCTOR_FACTORY(N) \ + template \ + inline CFunctor *CreateFunctor(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new CMemberFunctor##N(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR_FACTORY ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNCTOR_FACTORY(N) \ + template \ + inline CFunctor *CreateFunctor(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new CMemberFunctor##N(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNCTOR_FACTORY ); + +//------------------------------------- + +#define DEFINE_REF_COUNTING_MEMBER_FUNCTOR_FACTORY(N) \ + template \ + inline CFunctor *CreateRefCountingFunctor(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new CMemberFunctor##N >(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + +FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_FUNCTOR_FACTORY ); + +//------------------------------------- + +#define DEFINE_REF_COUNTING_CONST_MEMBER_FUNCTOR_FACTORY(N) \ + template \ + inline CFunctor *CreateRefCountingFunctor(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new CMemberFunctor##N >(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + +FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_FUNCTOR_FACTORY ); + +#define DEFINE_FUNCTOR_DATA_FACTORY(N) \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N > \ + inline CFunctorData *CreateFunctorData( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new CFunctorData##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N >( FUNC_CALL_ARGS_##N ); \ + } + +inline CFunctorData *CreateFunctorData() +{ + return new CFunctorData0(); +} + +FUNC_GENERATE_ALL_BUT0( DEFINE_FUNCTOR_DATA_FACTORY ); + +#define DEFINE_FUNCTOR_CALLBACK_FACTORY(N) \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N > \ + inline CFunctorCallback *CreateFunctorCallback( void (*pfnProxied)( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) ) \ + { \ + return new CFunctorCallback##N< FUNC_SOLO_BASE_TEMPLATE_ARG_PARAMS_##N >( pfnProxied ); \ + } + +inline CFunctorCallback *CreateFunctorCallback( void (*pfnProxied)() ) +{ + return new CFunctorCallback0( pfnProxied ); +} + +FUNC_GENERATE_ALL_BUT0( DEFINE_FUNCTOR_CALLBACK_FACTORY ); + +#define DEFINE_MEMBER_FUNCTOR_CALLBACK_FACTORY(N) \ + template < typename FUNCTION_CLASS FUNC_TEMPLATE_ARG_PARAMS_##N > \ + inline CFunctorCallback *CreateFunctorCallback( FUNCTION_CLASS *pObject, void ( FUNCTION_CLASS::*pfnProxied )( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) ) \ + { \ + return new CMemberFunctorCallback##N< FUNCTION_CLASS FUNC_BASE_TEMPLATE_ARG_PARAMS_##N >( pObject, pfnProxied ); \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR_CALLBACK_FACTORY ); + + +//----------------------------------------------------------------------------- +// +// Templates to assist early-out direct call code +// +//----------------------------------------------------------------------------- + +#define DEFINE_NONMEMBER_FUNCTOR_DIRECT(N) \ + template \ + inline void FunctorDirectCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ +{ \ + (*pfnProxied)( FUNC_CALL_ARGS_##N ); \ +} + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNCTOR_DIRECT ); + + +//------------------------------------- + +#define DEFINE_MEMBER_FUNCTOR_DIRECT(N) \ + template \ + inline void FunctorDirectCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ +{ \ + ((*pObject).*pfnProxied)(FUNC_CALL_ARGS_##N); \ +} + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR_DIRECT ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNCTOR_DIRECT(N) \ + template \ + inline void FunctorDirectCall(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ +{ \ + ((*pObject).*pfnProxied)(FUNC_CALL_ARGS_##N); \ +} + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNCTOR_DIRECT ); + +#include "tier0/memdbgoff.h" + +//----------------------------------------------------------------------------- +// Factory class useable as templated traits +//----------------------------------------------------------------------------- + +class CDefaultFunctorFactory +{ +public: + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNCTOR_FACTORY ); + FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR_FACTORY ); + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNCTOR_FACTORY ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_FUNCTOR_FACTORY ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_FUNCTOR_FACTORY ); +}; + +template +class CCustomizedFunctorFactory +{ +public: + void SetAllocator( CAllocator *pAllocator ) + { + m_pAllocator = pAllocator; + } + + #define DEFINE_NONMEMBER_FUNCTOR_FACTORY_CUSTOM(N) \ + template \ + inline CFunctor *CreateFunctor( FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return new (m_pAllocator->Alloc( sizeof(CFunctor##N) )) CFunctor##N( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } + + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNCTOR_FACTORY_CUSTOM ); + + //------------------------------------- + + #define DEFINE_MEMBER_FUNCTOR_FACTORY_CUSTOM(N) \ + template \ + inline CFunctor *CreateFunctor(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new (m_pAllocator->Alloc( sizeof(CMemberFunctor##N) )) CMemberFunctor##N(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + + FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNCTOR_FACTORY_CUSTOM ); + + //------------------------------------- + + #define DEFINE_CONST_MEMBER_FUNCTOR_FACTORY_CUSTOM(N) \ + template \ + inline CFunctor *CreateFunctor( OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new (m_pAllocator->Alloc( sizeof(CMemberFunctor##N) )) CMemberFunctor##N(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNCTOR_FACTORY_CUSTOM ); + + //------------------------------------- + + #define DEFINE_REF_COUNTING_MEMBER_FUNCTOR_FACTORY_CUSTOM(N) \ + template \ + inline CFunctor *CreateRefCountingFunctor( OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new (m_pAllocator->Alloc( sizeof(CMemberFunctor##N >) )) CMemberFunctor##N >(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_FUNCTOR_FACTORY_CUSTOM ); + + //------------------------------------- + + #define DEFINE_REF_COUNTING_CONST_MEMBER_FUNCTOR_FACTORY_CUSTOM(N) \ + template \ + inline CFunctor *CreateRefCountingFunctor( OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + return new (m_pAllocator->Alloc( sizeof(CMemberFunctor##N >) )) CMemberFunctor##N >(pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N); \ + } + + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_FUNCTOR_FACTORY_CUSTOM ); + +private: + CAllocator *m_pAllocator; + +}; + +//----------------------------------------------------------------------------- + +#endif // FUNCTORS_H diff --git a/public/tier1/generichash.h b/public/tier1/generichash.h new file mode 100644 index 0000000..44733d6 --- /dev/null +++ b/public/tier1/generichash.h @@ -0,0 +1,116 @@ +//======= Copyright © 2005, , Valve Corporation, All rights reserved. ========= +// +// Purpose: Variant Pearson Hash general purpose hashing algorithm described +// by Cargill in C++ Report 1994. Generates a 16-bit result. +// +//============================================================================= + +#ifndef GENERICHASH_H +#define GENERICHASH_H + +#if defined(_WIN32) +#pragma once +#endif + +//----------------------------------------------------------------------------- + +unsigned FASTCALL HashString( const char *pszKey ); +unsigned FASTCALL HashStringCaseless( const char *pszKey ); +unsigned FASTCALL HashStringCaselessConventional( const char *pszKey ); +unsigned FASTCALL Hash4( const void *pKey ); +unsigned FASTCALL Hash8( const void *pKey ); +unsigned FASTCALL Hash12( const void *pKey ); +unsigned FASTCALL Hash16( const void *pKey ); +unsigned FASTCALL HashBlock( const void *pKey, unsigned size ); + +unsigned FASTCALL HashInt( const int key ); + +// hash a uint32 into a uint32 +FORCEINLINE uint32 HashIntAlternate( uint32 n) +{ + n = ( n + 0x7ed55d16 ) + ( n << 12 ); + n = ( n ^ 0xc761c23c ) ^ ( n >> 19 ); + n = ( n + 0x165667b1 ) + ( n << 5 ); + n = ( n + 0xd3a2646c ) ^ ( n << 9 ); + n = ( n + 0xfd7046c5 ) + ( n << 3 ); + n = ( n ^ 0xb55a4f09 ) ^ ( n >> 16 ); + return n; +} + +inline unsigned HashIntConventional( const int n ) // faster but less effective +{ + // first byte + unsigned hash = 0xAAAAAAAA + (n & 0xFF); + // second byte + hash = ( hash << 5 ) + hash + ( (n >> 8) & 0xFF ); + // third byte + hash = ( hash << 5 ) + hash + ( (n >> 16) & 0xFF ); + // fourth byte + hash = ( hash << 5 ) + hash + ( (n >> 24) & 0xFF ); + + return hash; + + /* this is the old version, which would cause a load-hit-store on every + line on a PowerPC, and therefore took hundreds of clocks to execute! + + byte *p = (byte *)&n; + unsigned hash = 0xAAAAAAAA + *p++; + hash = ( ( hash << 5 ) + hash ) + *p++; + hash = ( ( hash << 5 ) + hash ) + *p++; + return ( ( hash << 5 ) + hash ) + *p; + */ +} + +//----------------------------------------------------------------------------- + +template +inline unsigned HashItem( const T &item ) +{ + // TODO: Confirm comiler optimizes out unused paths + if ( sizeof(item) == 4 ) + return Hash4( &item ); + else if ( sizeof(item) == 8 ) + return Hash8( &item ); + else if ( sizeof(item) == 12 ) + return Hash12( &item ); + else if ( sizeof(item) == 16 ) + return Hash16( &item ); + else + return HashBlock( &item, sizeof(item) ); +} + +template <> inline unsigned HashItem(const int &key ) +{ + return HashInt( key ); +} + +template <> inline unsigned HashItem(const unsigned &key ) +{ + return HashInt( (int)key ); +} + +template<> inline unsigned HashItem(const char * const &pszKey ) +{ + return HashString( pszKey ); +} + +template<> inline unsigned HashItem(char * const &pszKey ) +{ + return HashString( pszKey ); +} + +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Murmur hash +//----------------------------------------------------------------------------- +uint32 MurmurHash2( const void * key, int len, uint32 seed ); + +// return murmurhash2 of a downcased string +uint32 MurmurHash2LowerCase( char const *pString, uint32 nSeed ); + +uint64 MurmurHash64( const void * key, int len, uint32 seed ); + + +#endif /* !GENERICHASH_H */ diff --git a/public/tier1/iconvar.h b/public/tier1/iconvar.h new file mode 100644 index 0000000..a9d8033 --- /dev/null +++ b/public/tier1/iconvar.h @@ -0,0 +1,121 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $NoKeywords: $ +//===========================================================================// + +#ifndef ICONVAR_H +#define ICONVAR_H + +#if _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "tier1/strtools.h" +#include "color.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IConVar; +class CCommand; + + +//----------------------------------------------------------------------------- +// ConVar flags +//----------------------------------------------------------------------------- +// The default, no flags at all +#define FCVAR_NONE 0 + +// Command to ConVars and ConCommands +// ConVar Systems +#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. +#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. +#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL +#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL +#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. + +// ConVar only +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc +#define FCVAR_NOTIFY (1<<8) // notifies players when changed +#define FCVAR_USERINFO (1<<9) // changes the client's info string + +#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). +#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log +#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar + +// It's a ConVar that's shared between the client and the server. +// At signon, the values of all such ConVars are sent from the server to the client (skipped for local +// client, of course ) +// If a change is requested it must come from the console (i.e., no remote client changes) +// If a value is changed while a server is active, it's replicated to all connected clients +#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time +#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats +#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated +#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file +#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles +#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players +#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers +#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload +#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload + +#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server +#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread +#define FCVAR_ARCHIVE_XBOX (1<<24) // cvar written to config.cfg on the Xbox + +#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. +#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). +#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. + // Note: IVEngineClient::ClientCmd_Unrestricted can run any client command. + +#define FCVAR_ACCESSIBLE_FROM_THREADS (1<<25) // used as a debugging tool necessary to check material system thread convars +// #define FCVAR_AVAILABLE (1<<26) +// #define FCVAR_AVAILABLE (1<<27) +// #define FCVAR_AVAILABLE (1<<31) + +#define FCVAR_MATERIAL_THREAD_MASK ( FCVAR_RELOAD_MATERIALS | FCVAR_RELOAD_TEXTURES | FCVAR_MATERIAL_SYSTEM_THREAD ) + +//----------------------------------------------------------------------------- +// Called when a ConVar changes value +// NOTE: For FCVAR_NEVER_AS_STRING ConVars, pOldValue == NULL +//----------------------------------------------------------------------------- +typedef void ( *FnChangeCallback_t )( IConVar *var, const char *pOldValue, float flOldValue ); + + +//----------------------------------------------------------------------------- +// Abstract interface for ConVars +//----------------------------------------------------------------------------- +abstract_class IConVar +{ +public: + // Value set + virtual void SetValue( const char *pValue ) = 0; + virtual void SetValue( float flValue ) = 0; + virtual void SetValue( int nValue ) = 0; + virtual void SetValue( Color value ) = 0; + + // Return name of command + virtual const char *GetName( void ) const = 0; + + // Return name of command (usually == GetName(), except in case of FCVAR_SS_ADDED vars + virtual const char *GetBaseName( void ) const = 0; + + // Accessors.. not as efficient as using GetState()/GetInfo() + // if you call these methods multiple times on the same IConVar + virtual bool IsFlagSet( int nFlag ) const = 0; + + virtual int GetSplitScreenPlayerSlot() const = 0; +}; + + +#endif // ICONVAR_H diff --git a/public/tier1/interface.h b/public/tier1/interface.h new file mode 100644 index 0000000..fd749c9 --- /dev/null +++ b/public/tier1/interface.h @@ -0,0 +1,220 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// This header defines the interface convention used in the valve engine. +// To make an interface and expose it: +// 1. The interface must be ALL pure virtuals, and have no data members. +// 2. Define a name for it. +// 3. In its implementation file, use EXPOSE_INTERFACE or EXPOSE_SINGLE_INTERFACE. + +// Versioning +// There are two versioning cases that are handled by this: +// 1. You add functions to the end of an interface, so it is binary compatible with the previous interface. In this case, +// you need two EXPOSE_INTERFACEs: one to expose your class as the old interface and one to expose it as the new interface. +// 2. You update an interface so it's not compatible anymore (but you still want to be able to expose the old interface +// for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and +// expose it for the old interface. + +// Static Linking: +// Must mimic unique seperate class 'InterfaceReg' constructors per subsystem. +// Each subsystem can then import and export interfaces as expected. +// This is achieved through unique namespacing 'InterfaceReg' via symbol _SUBSYSTEM. +// Static Linking also needs to generate unique symbols per interface so as to +// provide a 'stitching' method whereby these interface symbols can be referenced +// via the lib's primary module (usually the lib's interface exposure) +// therby stitching all of that lib's code/data together for eventual final exe link inclusion. + +#ifndef INTERFACE_H +#define INTERFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#ifdef POSIX +#include // dlopen,dlclose, et al +#include + +#define GetProcAddress dlsym + +#endif + +// TODO: move interface.cpp into tier0 library. +#include "tier0/platform.h" + +// All interfaces derive from this. +class IBaseInterface +{ +public: + virtual ~IBaseInterface() {} +}; + +#if !defined( _X360 ) +#define CREATEINTERFACE_PROCNAME "CreateInterface" +#else +// x360 only allows ordinal exports, .def files export "CreateInterface" at 1 +#define CREATEINTERFACE_PROCNAME ((const char*)1) +#endif + +typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); +typedef void* (*InstantiateInterfaceFn)(); + +// Used internally to register classes. +class InterfaceReg +{ +public: + InterfaceReg(InstantiateInterfaceFn fn, const char *pName); + +public: + InstantiateInterfaceFn m_CreateFn; + const char *m_pName; + + InterfaceReg *m_pNext; // For the global list. +}; + +// Use this to expose an interface that can have multiple instances. +// e.g.: +// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// This will expose a class called CInterfaceImp that implements IInterface (a pure class) +// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// +// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") +// so that each component can use these names/vtables to communicate +// +// A single class can support multiple interfaces through multiple inheritance +// +// Use this if you want to write the factory function. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); +#else +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ + } +#endif + +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + static void* __Create##className##_interface() {return static_cast( new className );} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); +#else +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static void* __Create##className##_interface() {return static_cast( new className );} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ + } +#endif + +// Use this to expose a singleton interface with a global variable you've created. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + static void* __Create##className##interfaceName##_interface() {return static_cast( &globalVarName );} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); +#else +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + namespace _SUBSYSTEM \ + { \ + static void* __Create##className##interfaceName##_interface() {return static_cast( &globalVarName );} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ + } +#endif + +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#if !defined(_STATIC_LINKED) || !defined(_SUBSYSTEM) +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + static className __g_##className##_singleton; \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) +#else +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + namespace _SUBSYSTEM \ + { \ + static className __g_##className##_singleton; \ + } \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) +#endif + +// load/unload components +class CSysModule; + +// interface return status +enum +{ + IFACE_OK = 0, + IFACE_FAILED +}; + +//----------------------------------------------------------------------------- +// This function is automatically exported and allows you to access any interfaces exposed with the above macros. +// if pReturnCode is set, it will return one of the following values (IFACE_OK, IFACE_FAILED) +// extend this for other error conditions/code +//----------------------------------------------------------------------------- +DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode); + +#if defined( _X360 ) +DLL_EXPORT void *CreateInterfaceThunk( const char *pName, int *pReturnCode ); +#endif + +//----------------------------------------------------------------------------- +// UNDONE: This is obsolete, use the module load/unload/get instead!!! +//----------------------------------------------------------------------------- +extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); +extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); +extern CreateInterfaceFn Sys_GetFactoryThis( void ); + +//----------------------------------------------------------------------------- +// Load & Unload should be called in exactly one place for each module +// The factory for that module should be passed on to dependent components for +// proper versioning. +//----------------------------------------------------------------------------- +extern CSysModule *Sys_LoadModule( const char *pModuleName ); +extern void Sys_UnloadModule( CSysModule *pModule ); + +// Determines if current process is running with any debug modules +extern bool Sys_RunningWithDebugModules(); + +// This is a helper function to load a module, get its factory, and get a specific interface. +// You are expected to free all of these things. +// Returns false and cleans up if any of the steps fail. +bool Sys_LoadInterface( + const char *pModuleName, + const char *pInterfaceVersionName, + CSysModule **pOutModule, + void **pOutInterface ); + +bool Sys_IsDebuggerPresent(); + +//----------------------------------------------------------------------------- +// Purpose: Place this as a singleton at module scope (e.g.) and use it to get the factory from the specified module name. +// +// When the singleton goes out of scope (.dll unload if at module scope), +// then it'll call Sys_UnloadModule on the module so that the refcount is decremented +// and the .dll actually can unload from memory. +//----------------------------------------------------------------------------- +class CDllDemandLoader +{ +public: + CDllDemandLoader( char const *pchModuleName ); + virtual ~CDllDemandLoader(); + CreateInterfaceFn GetFactory(); + void Unload(); + +private: + + char const *m_pchModuleName; + CSysModule *m_hModule; + bool m_bLoadAttempted; +}; + + +#endif + + + diff --git a/public/tier1/interpolatedvar.cpp b/public/tier1/interpolatedvar.cpp new file mode 100644 index 0000000..8f06c25 --- /dev/null +++ b/public/tier1/interpolatedvar.cpp @@ -0,0 +1,28 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" + +#include "tier1/interpolatedvar.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// warning C4660: template-class specialization 'CInterpolatedVar' is already instantiated +#pragma warning( disable : 4660 ) + +template class CInterpolatedVar; +template class CInterpolatedVar; +template class CInterpolatedVar; + +CInterpolationContext *CInterpolationContext::s_pHead = NULL; +bool CInterpolationContext::s_bAllowExtrapolation = false; +float CInterpolationContext::s_flLastTimeStamp = 0; + +float g_flLastPacketTimestamp = 0; + + +ConVar cl_extrapolate_amount( "cl_extrapolate_amount", "0.25", FCVAR_CHEAT, "Set how many seconds the client will extrapolate entities for." ); diff --git a/public/tier1/interpolatedvar.h b/public/tier1/interpolatedvar.h new file mode 100644 index 0000000..6f0e1be --- /dev/null +++ b/public/tier1/interpolatedvar.h @@ -0,0 +1,1562 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef INTERPOLATEDVAR_H +#define INTERPOLATEDVAR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utllinkedlist.h" +#include "tier1/rangecheckedvar.h" +#include "tier1/lerp_functions.h" +#include "tier1/convar.h" + + +#include "tier0/memdbgon.h" + +#define COMPARE_HISTORY(a,b) \ + ( memcmp( m_VarHistory[a].GetValue(), m_VarHistory[b].GetValue(), sizeof(Type)*GetMaxCount() ) == 0 ) + +// Define this to have it measure whether or not the interpolated entity list +// is accurate. +//#define INTERPOLATEDVAR_PARANOID_MEASUREMENT + +#define LATCH_ANIMATION_VAR (1<<0) // use AnimTime as sample basis +#define LATCH_SIMULATION_VAR (1<<1) // use SimulationTime as sample basis + +#define EXCLUDE_AUTO_LATCH (1<<2) +#define EXCLUDE_AUTO_INTERPOLATE (1<<3) + +#define INTERPOLATE_LINEAR_ONLY (1<<4) // don't do hermite interpolation +#define INTERPOLATE_OMIT_UPDATE_LAST_NETWORKED (1<<5) + + + +#define EXTRA_INTERPOLATION_HISTORY_STORED 0.05f // It stores this much extra interpolation history, + // so you can always call Interpolate() this far + // in the past from your last call and be able to + // get an interpolated value. + +// this global keeps the last known server packet tick (to avoid calling engine->GetLastTimestamp() all the time) +extern float g_flLastPacketTimestamp; + +inline void Interpolation_SetLastPacketTimeStamp( float timestamp) +{ + Assert( timestamp > 0 ); + g_flLastPacketTimestamp = timestamp; +} + + +// Before calling Interpolate(), you can use this use this to setup the context if +// you want to enable extrapolation. +class CInterpolationContext +{ +public: + + CInterpolationContext() + { + m_bOldAllowExtrapolation = s_bAllowExtrapolation; + m_flOldLastTimeStamp = s_flLastTimeStamp; + + // By default, disable extrapolation unless they call EnableExtrapolation. + s_bAllowExtrapolation = false; + + // this is the context stack + m_pNext = s_pHead; + s_pHead = this; + } + + ~CInterpolationContext() + { + // restore values from prev stack element + s_bAllowExtrapolation = m_bOldAllowExtrapolation; + s_flLastTimeStamp = m_flOldLastTimeStamp; + + Assert( s_pHead == this ); + s_pHead = m_pNext; + } + + static void EnableExtrapolation(bool state) + { + s_bAllowExtrapolation = state; + } + + static bool IsThereAContext() + { + return s_pHead != NULL; + } + + static bool IsExtrapolationAllowed() + { + return s_bAllowExtrapolation; + } + + static void SetLastTimeStamp(float timestamp) + { + s_flLastTimeStamp = timestamp; + } + + static float GetLastTimeStamp() + { + return s_flLastTimeStamp; + } + + +private: + + CInterpolationContext *m_pNext; + bool m_bOldAllowExtrapolation; + float m_flOldLastTimeStamp; + + static CInterpolationContext *s_pHead; + static bool s_bAllowExtrapolation; + static float s_flLastTimeStamp; +}; + + +extern ConVar cl_extrapolate_amount; + + +template< class T > +inline T ExtrapolateInterpolatedVarType( const T &oldVal, const T &newVal, float divisor, float flExtrapolationAmount ) +{ + return newVal; +} + +inline Vector ExtrapolateInterpolatedVarType( const Vector &oldVal, const Vector &newVal, float divisor, float flExtrapolationAmount ) +{ + return Lerp( 1.0f + flExtrapolationAmount * divisor, oldVal, newVal ); +} + +inline float ExtrapolateInterpolatedVarType( const float &oldVal, const float &newVal, float divisor, float flExtrapolationAmount ) +{ + return Lerp( 1.0f + flExtrapolationAmount * divisor, oldVal, newVal ); +} + +inline QAngle ExtrapolateInterpolatedVarType( const QAngle &oldVal, const QAngle &newVal, float divisor, float flExtrapolationAmount ) +{ + return Lerp( 1.0f + flExtrapolationAmount * divisor, oldVal, newVal ); +} + + +// -------------------------------------------------------------------------------------------------------------- // +// IInterpolatedVar interface. +// -------------------------------------------------------------------------------------------------------------- // + +abstract_class IInterpolatedVar +{ +public: + virtual ~IInterpolatedVar() {} + + virtual void Setup( void *pValue, int type ) = 0; + virtual void SetInterpolationAmount( float seconds ) = 0; + + // Returns true if the new value is different from the prior most recent value. + virtual void NoteLastNetworkedValue() = 0; + virtual bool NoteChanged( float flCurrentTime, float flChangeTime, bool bUpdateLastNetworkedValue ) = 0; + virtual void Reset( float flCurrentTime ) = 0; + + // Returns 1 if the value will always be the same if currentTime is always increasing. + virtual int Interpolate( float currentTime ) = 0; + + virtual int GetType() const = 0; + virtual void RestoreToLastNetworked() = 0; + virtual void Copy( IInterpolatedVar *pSrc ) = 0; + + virtual const char *GetDebugName() = 0; + virtual void SetDebugName( const char* pName ) = 0; +}; + +template< typename Type, bool IS_ARRAY > +struct CInterpolatedVarEntryBase +{ + CInterpolatedVarEntryBase() + { + value = NULL; + count = 0; + flChangeTime = 0; + } + ~CInterpolatedVarEntryBase() + { + delete[] value; + value = NULL; + } + + // This will transfer the data from another varentry. This is used to avoid allocation + // pointers can be transferred (only one varentry has a copy), but not trivially copied + void FastTransferFrom( CInterpolatedVarEntryBase &src ) + { + Assert(!value); + value = src.value; + count = src.count; + flChangeTime = src.flChangeTime; + src.value = 0; + src.count = 0; + } + + CInterpolatedVarEntryBase& operator=( const CInterpolatedVarEntryBase& src ) + { + delete[] value; + value = NULL; + count = 0; + if ( src.value ) + { + count = src.count; + value = new Type[count]; + for ( int i = 0; i < count; i++ ) + { + value[i] = src.value[i]; + } + } + return *this; + } + + Type *GetValue() { return value; } + const Type *GetValue() const { return value; } + + void Init(int maxCount) + { + if ( !maxCount ) + { + DeleteEntry(); + } + else + { + // resize + if ( maxCount != count ) + { + DeleteEntry(); + } + + if ( !value ) + { + count = maxCount; + value = new Type[maxCount]; + } + } + Assert(count==maxCount); + } + Type *NewEntry( const Type *pValue, int maxCount, float time ) + { + flChangeTime = time; + Init(maxCount); + if ( value && maxCount) + { + memcpy( value, pValue, maxCount*sizeof(Type) ); + } + return value; + } + + void DeleteEntry() + { + delete[] value; + value = NULL; + count = 0; + } + + float flChangeTime; + int count; + Type * value; + +private: + CInterpolatedVarEntryBase( const CInterpolatedVarEntryBase &src ); +}; + +template +struct CInterpolatedVarEntryBase +{ + CInterpolatedVarEntryBase() {} + ~CInterpolatedVarEntryBase() {} + + const Type *GetValue() const { return &value; } + Type *GetValue() { return &value; } + + void Init(int maxCount) + { + Assert(maxCount==1); + } + Type *NewEntry( const Type *pValue, int maxCount, float time ) + { + Assert(maxCount==1); + flChangeTime = time; + memcpy( &value, pValue, maxCount*sizeof(Type) ); + return &value; + } + void FastTransferFrom( CInterpolatedVarEntryBase &src ) + { + *this = src; + } + + void DeleteEntry() {} + + float flChangeTime; + Type value; +}; + +template +class CSimpleRingBuffer +{ +public: + CSimpleRingBuffer( int startSize = 4 ) + { + m_pElements = 0; + m_maxElement = 0; + m_firstElement = 0; + m_count = 0; + m_growSize = 16; + EnsureCapacity(startSize); + } + ~CSimpleRingBuffer() + { + delete[] m_pElements; + m_pElements = NULL; + } + + inline int Count() const { return m_count; } + + int Head() const { return (m_count>0) ? 0 : InvalidIndex(); } + + bool IsIdxValid( int i ) const { return (i >= 0 && i < m_count) ? true : false; } + bool IsValidIndex(int i) const { return IsIdxValid(i); } + static int InvalidIndex() { return -1; } + + T& operator[]( int i ) + { + Assert( IsIdxValid(i) ); + i += m_firstElement; + i = WrapRange(i); + return m_pElements[i]; + } + + const T& operator[]( int i ) const + { + Assert( IsIdxValid(i) ); + i += m_firstElement; + i = WrapRange(i); + return m_pElements[i]; + } + + void EnsureCapacity( int capSize ) + { + if ( capSize > m_maxElement ) + { + int newMax = m_maxElement + ((capSize+m_growSize-1)/m_growSize) * m_growSize; + T *pNew = new T[newMax]; + for ( int i = 0; i < m_maxElement; i++ ) + { + // ------------ + // If you wanted to make this a more generic container you'd probably want this code + // instead - since FastTransferFrom() is an optimization dependent on types stored + // here defining this operation. + //pNew[i] = m_pElements[WrapRange(i+m_firstElement)]; + pNew[i].FastTransferFrom( m_pElements[WrapRange(i+m_firstElement)] ); + // ------------ + } + m_firstElement = 0; + m_maxElement = newMax; + delete[] m_pElements; + m_pElements = pNew; + } + } + + int AddToHead() + { + EnsureCapacity( m_count + 1 ); + int i = m_firstElement + m_maxElement - 1; + m_count++; + i = WrapRange(i); + m_firstElement = i; + return 0; + } + + int AddToHead( const T &elem ) + { + AddToHead(); + m_pElements[m_firstElement] = elem; + return 0; + } + + int AddToTail() + { + EnsureCapacity( m_count + 1 ); + m_count++; + return WrapRange(m_firstElement+m_count-1); + } + + void RemoveAll() + { + m_count = 0; + m_firstElement = 0; + } + + void RemoveAtHead() + { + if ( m_count > 0 ) + { + m_firstElement = WrapRange(m_firstElement+1); + m_count--; + } + } + + void Truncate( int newLength ) + { + if ( newLength < m_count ) + { + Assert(newLength>=0); + m_count = newLength; + } + } + +private: + inline int WrapRange( int i ) const + { + return ( i >= m_maxElement ) ? (i - m_maxElement) : i; + } + + T *m_pElements; + unsigned short m_maxElement; + unsigned short m_firstElement; + unsigned short m_count; + unsigned short m_growSize; +}; + +// -------------------------------------------------------------------------------------------------------------- // +// CInterpolatedVarArrayBase - the main implementation of IInterpolatedVar. +// -------------------------------------------------------------------------------------------------------------- // + +template< typename Type, bool IS_ARRAY> +class CInterpolatedVarArrayBase : public IInterpolatedVar +{ +public: + friend class CInterpolatedVarPrivate; + + CInterpolatedVarArrayBase( const char *pDebugName="no debug name" ); + virtual ~CInterpolatedVarArrayBase(); + + + // IInterpolatedVar overrides. +public: + + virtual void Setup( void *pValue, int type ); + virtual void SetInterpolationAmount( float seconds ); + virtual void NoteLastNetworkedValue(); + virtual bool NoteChanged( float flCurrentTime, float flChangeTime, bool bUpdateLastNetworkedValue ); + virtual void Reset( float flCurrentTime ); + virtual int Interpolate( float currentTime ); + virtual int GetType() const; + virtual void RestoreToLastNetworked(); + virtual void Copy( IInterpolatedVar *pInSrc ); + virtual const char *GetDebugName() { return m_pDebugName; } + + +public: + + // Just like the IInterpolatedVar functions, but you can specify an interpolation amount. + bool NoteChanged( float flCurrentTime, float flChangeTime, float interpolation_amount, bool bUpdateLastNetworkedValue ); + int Interpolate( float currentTime, float interpolation_amount ); + + void DebugInterpolate( Type *pOut, float currentTime ); + + void GetDerivative( Type *pOut, float currentTime ); + void GetDerivative_SmoothVelocity( Type *pOut, float currentTime ); // See notes on ::Derivative_HermiteLinearVelocity for info. + + void ClearHistory(); + void AddToHead( float changeTime, const Type* values, bool bFlushNewer ); + const Type& GetPrev( int iArrayIndex=0 ) const; + const Type& GetCurrent( int iArrayIndex=0 ) const; + + // Returns the time difference betweem the most recent sample and its previous sample. + float GetInterval() const; + bool IsValidIndex( int i ); + Type *GetHistoryValue( int index, float& flChangeTime, int iArrayIndex=0 ); + int GetHead() { return 0; } + int GetNext( int i ) + { + int next = i + 1; + if ( !m_VarHistory.IsValidIndex(next) ) + return m_VarHistory.InvalidIndex(); + return next; + } + + void SetHistoryValuesForItem( int item, Type& value ); + void SetLooping( bool looping, int iArrayIndex=0 ); + + void SetMaxCount( float flCurrentTime, int newmax ); + int GetMaxCount() const; + + // Get the time of the oldest entry. + float GetOldestEntry(); + + // set a debug name (if not provided by constructor) + void SetDebugName(const char *pName ) { m_pDebugName = pName; } + + bool GetInterpolationInfo( float currentTime, int *pNewer, int *pOlder, int *pOldest ); + +protected: + + typedef CInterpolatedVarEntryBase CInterpolatedVarEntry; + typedef CSimpleRingBuffer< CInterpolatedVarEntry > CVarHistory; + friend class CInterpolationInfo; + + class CInterpolationInfo + { + public: + bool m_bHermite; + int oldest; // Only set if using hermite. + int older; + int newer; + float frac; + }; + + +protected: + + void RemoveOldEntries( float oldesttime ); + void RemoveEntriesPreviousTo( float flTime ); + + bool GetInterpolationInfo( + CInterpolationInfo *pInfo, + float currentTime, + float interpolation_amount, + int *pNoMoreChanges ); + + void TimeFixup_Hermite( + CInterpolatedVarEntry &fixup, + CInterpolatedVarEntry*& prev, + CInterpolatedVarEntry*& start, + CInterpolatedVarEntry*& end ); + + // Force the time between prev and start to be dt (and extend prev out farther if necessary). + void TimeFixup2_Hermite( + CInterpolatedVarEntry &fixup, + CInterpolatedVarEntry*& prev, + CInterpolatedVarEntry*& start, + float dt + ); + + void _Extrapolate( + Type *pOut, + CInterpolatedVarEntry *pOld, + CInterpolatedVarEntry *pNew, + float flDestinationTime, + float flMaxExtrapolationAmount + ); + + void _Interpolate( Type *out, float frac, CInterpolatedVarEntry *start, CInterpolatedVarEntry *end ); + void _Interpolate_Hermite( Type *out, float frac, CInterpolatedVarEntry *pOriginalPrev, CInterpolatedVarEntry *start, CInterpolatedVarEntry *end, bool looping = false ); + + void _Derivative_Hermite( Type *out, float frac, CInterpolatedVarEntry *pOriginalPrev, CInterpolatedVarEntry *start, CInterpolatedVarEntry *end ); + void _Derivative_Hermite_SmoothVelocity( Type *out, float frac, CInterpolatedVarEntry *b, CInterpolatedVarEntry *c, CInterpolatedVarEntry *d ); + void _Derivative_Linear( Type *out, CInterpolatedVarEntry *start, CInterpolatedVarEntry *end ); + + bool ValidOrder(); + +protected: + // The underlying data element + Type *m_pValue; + CVarHistory m_VarHistory; + // Store networked values so when we latch we can detect which values were changed via networking + Type * m_LastNetworkedValue; + float m_LastNetworkedTime; + byte m_fType; + byte m_nMaxCount; + byte * m_bLooping; + float m_InterpolationAmount; + const char * m_pDebugName; +}; + + +template< typename Type, bool IS_ARRAY > +inline CInterpolatedVarArrayBase::CInterpolatedVarArrayBase( const char *pDebugName ) +{ + m_pDebugName = pDebugName; + m_pValue = NULL; + m_fType = LATCH_ANIMATION_VAR; + m_InterpolationAmount = 0.0f; + m_nMaxCount = 0; + m_LastNetworkedTime = 0; + m_LastNetworkedValue = NULL; + m_bLooping = NULL; +} + +template< typename Type, bool IS_ARRAY > +inline CInterpolatedVarArrayBase::~CInterpolatedVarArrayBase() +{ + ClearHistory(); + delete [] m_bLooping; + delete [] m_LastNetworkedValue; +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::Setup( void *pValue, int type ) +{ + m_pValue = ( Type * )pValue; + m_fType = type; +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::SetInterpolationAmount( float seconds ) +{ + m_InterpolationAmount = seconds; +} + +template< typename Type, bool IS_ARRAY > +inline int CInterpolatedVarArrayBase::GetType() const +{ + return m_fType; +} + +template< typename Type, bool IS_ARRAY > +void CInterpolatedVarArrayBase::NoteLastNetworkedValue() +{ + memcpy( m_LastNetworkedValue, m_pValue, m_nMaxCount * sizeof( Type ) ); + m_LastNetworkedTime = g_flLastPacketTimestamp; +} + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::NoteChanged( float flCurrentTime, float flChangeTime, float interpolation_amount, bool bUpdateLastNetworkedValue ) +{ + Assert( m_pValue ); + + // This is a big optimization where it can potentially avoid expensive interpolation + // involving this variable if it didn't get an actual new value in here. + bool bRet = true; + if ( m_VarHistory.Count() ) + { + if ( memcmp( m_pValue, m_VarHistory[0].GetValue(), sizeof( Type ) * m_nMaxCount ) == 0 ) + { + bRet = false; + } + } + + AddToHead( flChangeTime, m_pValue, true ); + + if ( bUpdateLastNetworkedValue ) + { + NoteLastNetworkedValue(); + } + +#if 0 + // Since we don't clean out the old entries until Interpolate(), make sure that there + // aren't any super old entries hanging around. + RemoveOldEntries( flCurrentTime - interpolation_amount - 2.0f ); +#else + // JAY: It doesn't seem like the above code is correct. This is keeping more than two seconds of history + // for variables that aren't being interpolated for some reason. For example, the player model isn't drawn + // in first person, so the history is only truncated here and will accumulate ~40 entries instead of 2 or 3 + // changing over to the method in Interpolate() means that we always have a 3-sample neighborhood around + // any data we're going to need. Unless flCurrentTime is different when samples are added vs. when + // they are interpolated I can't see this having any ill effects. + RemoveEntriesPreviousTo( flCurrentTime - interpolation_amount - EXTRA_INTERPOLATION_HISTORY_STORED ); +#endif + + return bRet; +} + + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::NoteChanged( float flCurrentTime, float flChangeTime, bool bUpdateLastNetworkedValue ) +{ + return NoteChanged( flCurrentTime, flChangeTime, m_InterpolationAmount, bUpdateLastNetworkedValue ); +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::RestoreToLastNetworked() +{ + Assert( m_pValue ); + memcpy( m_pValue, m_LastNetworkedValue, m_nMaxCount * sizeof( Type ) ); +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::ClearHistory() +{ + for ( int i = 0; i < m_VarHistory.Count(); i++ ) + { + m_VarHistory[i].DeleteEntry(); + } + m_VarHistory.RemoveAll(); +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::AddToHead( float changeTime, const Type* values, bool bFlushNewer ) +{ + MEM_ALLOC_CREDIT_CLASS(); + int newslot; + + if ( bFlushNewer ) + { + // Get rid of anything that has a timestamp after this sample. The server might have + // corrected our clock and moved us back, so our current changeTime is less than a + // changeTime we added samples during previously. + while ( m_VarHistory.Count() ) + { + if ( (m_VarHistory[0].flChangeTime+0.0001f) > changeTime ) + { + m_VarHistory.RemoveAtHead(); + } + else + { + break; + } + } + + newslot = m_VarHistory.AddToHead(); + } + else + { + newslot = m_VarHistory.AddToHead(); + for ( int i = 1; i < m_VarHistory.Count(); i++ ) + { + if ( m_VarHistory[i].flChangeTime <= changeTime ) + break; + m_VarHistory[newslot].FastTransferFrom( m_VarHistory[i] ); + newslot = i; + } + } + + CInterpolatedVarEntry *e = &m_VarHistory[ newslot ]; + e->NewEntry( values, m_nMaxCount, changeTime ); +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::Reset( float flCurrentTime ) +{ + ClearHistory(); + + if ( m_pValue ) + { + AddToHead( flCurrentTime, m_pValue, false ); + AddToHead( flCurrentTime, m_pValue, false ); + AddToHead( flCurrentTime, m_pValue, false ); + + memcpy( m_LastNetworkedValue, m_pValue, m_nMaxCount * sizeof( Type ) ); + } +} + + +template< typename Type, bool IS_ARRAY > +inline float CInterpolatedVarArrayBase::GetOldestEntry() +{ + float lastVal = 0; + if ( m_VarHistory.Count() ) + { + lastVal = m_VarHistory[m_VarHistory.Count()-1].flChangeTime; + } + return lastVal; +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::RemoveOldEntries( float oldesttime ) +{ + int newCount = m_VarHistory.Count(); + for ( int i = m_VarHistory.Count(); --i > 2; ) + { + if ( m_VarHistory[i].flChangeTime > oldesttime ) + break; + newCount = i; + } + m_VarHistory.Truncate(newCount); +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::RemoveEntriesPreviousTo( float flTime ) +{ + for ( int i = 0; i < m_VarHistory.Count(); i++ ) + { + if ( m_VarHistory[i].flChangeTime < flTime ) + { + // We need to preserve this sample (ie: the one right before this timestamp) + // and the sample right before it (for hermite blending), and we can get rid + // of everything else. + m_VarHistory.Truncate( i + 3 ); + break; + } + } +} + + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::GetInterpolationInfo( + typename CInterpolatedVarArrayBase::CInterpolationInfo *pInfo, + float currentTime, + float interpolation_amount, + int *pNoMoreChanges + ) +{ + Assert( m_pValue ); + + CVarHistory &varHistory = m_VarHistory; + + float targettime = currentTime - interpolation_amount; + + pInfo->m_bHermite = false; + pInfo->frac = 0; + pInfo->oldest = pInfo->older = pInfo->newer = varHistory.InvalidIndex(); + + for ( int i = 0; i < varHistory.Count(); i++ ) + { + pInfo->older = i; + + float older_change_time = m_VarHistory[ i ].flChangeTime; + if ( older_change_time == 0.0f ) + break; + + if ( targettime < older_change_time ) + { + pInfo->newer = pInfo->older; + continue; + } + + if ( pInfo->newer == varHistory.InvalidIndex() ) + { + // Have it linear interpolate between the newest 2 entries. + pInfo->newer = pInfo->older; + + // Since the time given is PAST all of our entries, then as long + // as time continues to increase, we'll be returning the same value. + if ( pNoMoreChanges ) + *pNoMoreChanges = 1; + return true; + } + + float newer_change_time = varHistory[ pInfo->newer ].flChangeTime; + float dt = newer_change_time - older_change_time; + if ( dt > 0.0001f ) + { + pInfo->frac = ( targettime - older_change_time ) / ( newer_change_time - older_change_time ); + pInfo->frac = MIN( pInfo->frac, 2.0f ); + + int oldestindex = i+1; + + if ( !(m_fType & INTERPOLATE_LINEAR_ONLY) && varHistory.IsIdxValid(oldestindex) ) + { + pInfo->oldest = oldestindex; + float oldest_change_time = varHistory[ oldestindex ].flChangeTime; + float dt2 = older_change_time - oldest_change_time; + if ( dt2 > 0.0001f ) + { + pInfo->m_bHermite = true; + } + } + + // If pInfo->newer is the most recent entry we have, and all 2 or 3 other + // entries are identical, then we're always going to return the same value + // if currentTime increases. + if ( pNoMoreChanges && pInfo->newer == m_VarHistory.Head() ) + { + if ( COMPARE_HISTORY( pInfo->newer, pInfo->older ) ) + { + if ( !pInfo->m_bHermite || COMPARE_HISTORY( pInfo->newer, pInfo->oldest ) ) + *pNoMoreChanges = 1; + } + } + } + return true; + } + + // Didn't find any, return last entry??? + if ( pInfo->newer != varHistory.InvalidIndex() ) + { + pInfo->older = pInfo->newer; + return true; + } + + + // This is the single-element case + pInfo->newer = pInfo->older; + return (pInfo->older != varHistory.InvalidIndex()); +} + + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::GetInterpolationInfo( float currentTime, int *pNewer, int *pOlder, int *pOldest ) +{ + CInterpolationInfo info; + bool result = GetInterpolationInfo( &info, currentTime, m_InterpolationAmount, NULL ); + + if (pNewer) + *pNewer = (int)info.newer; + + if (pOlder) + *pOlder = (int)info.older; + + if (pOldest) + *pOldest = (int)info.oldest; + + return result; +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::DebugInterpolate( Type *pOut, float currentTime ) +{ + float interpolation_amount = m_InterpolationAmount; + + int noMoreChanges = 0; + + CInterpolationInfo info; + GetInterpolationInfo( &info, currentTime, interpolation_amount, &noMoreChanges ); + + CVarHistory &history = m_VarHistory; + + if ( info.m_bHermite ) + { + // base cast, we have 3 valid sample point + _Interpolate_Hermite( pOut, info.frac, &history[info.oldest], &history[info.older], &history[info.newer] ); + } + else if ( info.newer == info.older ) + { + // This means the server clock got way behind the client clock. Extrapolate the value here based on its + // previous velocity (out to a certain amount). + int realOlder = info.newer+1; + if ( CInterpolationContext::IsExtrapolationAllowed() && + IsValidIndex( realOlder ) && + history[realOlder].flChangeTime != 0.0 && + interpolation_amount > 0.000001f && + CInterpolationContext::GetLastTimeStamp() <= m_LastNetworkedTime ) + { + // At this point, we know we're out of data and we have the ability to get a velocity to extrapolate with. + // + // However, we only want to extraploate if the server is choking. We don't want to extrapolate if + // the object legimately stopped moving and the server stopped sending updates for it. + // + // The way we know that the server is choking is if we haven't heard ANYTHING from it for a while. + // The server's update interval should be at least as often as our interpolation amount (otherwise, + // we wouldn't have the ability to interpolate). + // + // So right here, if we see that we haven't gotten any server updates since the last interpolation + // history update to this entity (and since we're in here, we know that we're out of interpolation data), + // then we can assume that the server is choking and decide to extrapolate. + // + // The End + + // Use the velocity here (extrapolate up to 1/4 of a second). + _Extrapolate( pOut, &history[realOlder], &history[info.newer], currentTime - interpolation_amount, cl_extrapolate_amount.GetFloat() ); + } + else + { + _Interpolate( pOut, info.frac, &history[info.older], &history[info.newer] ); + } + } + else + { + _Interpolate( pOut, info.frac, &history[info.older], &history[info.newer] ); + } +} + +template< typename Type, bool IS_ARRAY > +inline int CInterpolatedVarArrayBase::Interpolate( float currentTime, float interpolation_amount ) +{ + int noMoreChanges = 0; + + CInterpolationInfo info; + if (!GetInterpolationInfo( &info, currentTime, interpolation_amount, &noMoreChanges )) + return noMoreChanges; + + + CVarHistory &history = m_VarHistory; + +#ifdef INTERPOLATEDVAR_PARANOID_MEASUREMENT + Type *backupValues = (Type*)_alloca( m_nMaxCount * sizeof(Type) ); + memcpy( backupValues, m_pValue, sizeof( Type ) * m_nMaxCount ); +#endif + + if ( info.m_bHermite ) + { + // base cast, we have 3 valid sample point + _Interpolate_Hermite( m_pValue, info.frac, &history[info.oldest], &history[info.older], &history[info.newer] ); + } + else if ( info.newer == info.older ) + { + // This means the server clock got way behind the client clock. Extrapolate the value here based on its + // previous velocity (out to a certain amount). + int realOlder = info.newer+1; + if ( CInterpolationContext::IsExtrapolationAllowed() && + IsValidIndex( realOlder ) && + history[realOlder].flChangeTime != 0.0 && + interpolation_amount > 0.000001f && + CInterpolationContext::GetLastTimeStamp() <= m_LastNetworkedTime ) + { + // At this point, we know we're out of data and we have the ability to get a velocity to extrapolate with. + // + // However, we only want to extraploate if the server is choking. We don't want to extrapolate if + // the object legimately stopped moving and the server stopped sending updates for it. + // + // The way we know that the server is choking is if we haven't heard ANYTHING from it for a while. + // The server's update interval should be at least as often as our interpolation amount (otherwise, + // we wouldn't have the ability to interpolate). + // + // So right here, if we see that we haven't gotten any server updates since the last interpolation + // history update to this entity (and since we're in here, we know that we're out of interpolation data), + // then we can assume that the server is choking and decide to extrapolate. + // + // The End + + // Use the velocity here (extrapolate up to 1/4 of a second). + _Extrapolate( m_pValue, &history[realOlder], &history[info.newer], currentTime - interpolation_amount, cl_extrapolate_amount.GetFloat() ); + } + else + { + _Interpolate( m_pValue, info.frac, &history[info.older], &history[info.newer] ); + } + } + else + { + _Interpolate( m_pValue, info.frac, &history[info.older], &history[info.newer] ); + } + +#ifdef INTERPOLATEDVAR_PARANOID_MEASUREMENT + if ( memcmp( backupValues, m_pValue, sizeof( Type ) * m_nMaxCount ) != 0 ) + { + extern int g_nInterpolatedVarsChanged; + extern bool g_bRestoreInterpolatedVarValues; + + ++g_nInterpolatedVarsChanged; + + // This undoes the work that we do in here so if someone is in the debugger, they + // can find out which variable changed. + if ( g_bRestoreInterpolatedVarValues ) + { + memcpy( m_pValue, backupValues, sizeof( Type ) * m_nMaxCount ); + return noMoreChanges; + } + } +#endif + + // Clear out all entries before the oldest since we should never access them again. + // Usually, Interpolate() calls never go backwards in time, but C_BaseAnimating::BecomeRagdollOnClient for one + // goes slightly back in time + RemoveEntriesPreviousTo( currentTime - interpolation_amount - EXTRA_INTERPOLATION_HISTORY_STORED ); + return noMoreChanges; +} + + +template< typename Type, bool IS_ARRAY > +void CInterpolatedVarArrayBase::GetDerivative( Type *pOut, float currentTime ) +{ + CInterpolationInfo info; + if (!GetInterpolationInfo( &info, currentTime, m_InterpolationAmount, NULL )) + return; + + if ( info.m_bHermite ) + { + _Derivative_Hermite( pOut, info.frac, &m_VarHistory[info.oldest], &m_VarHistory[info.older], &m_VarHistory[info.newer] ); + } + else + { + _Derivative_Linear( pOut, &m_VarHistory[info.older], &m_VarHistory[info.newer] ); + } +} + + +template< typename Type, bool IS_ARRAY > +void CInterpolatedVarArrayBase::GetDerivative_SmoothVelocity( Type *pOut, float currentTime ) +{ + CInterpolationInfo info; + if (!GetInterpolationInfo( &info, currentTime, m_InterpolationAmount, NULL )) + return; + + CVarHistory &history = m_VarHistory; + bool bExtrapolate = false; + int realOlder = 0; + + if ( info.m_bHermite ) + { + _Derivative_Hermite_SmoothVelocity( pOut, info.frac, &history[info.oldest], &history[info.older], &history[info.newer] ); + return; + } + else if ( info.newer == info.older && CInterpolationContext::IsExtrapolationAllowed() ) + { + // This means the server clock got way behind the client clock. Extrapolate the value here based on its + // previous velocity (out to a certain amount). + realOlder = info.newer+1; + if ( IsValidIndex( realOlder ) && history[realOlder].flChangeTime != 0.0 ) + { + // At this point, we know we're out of data and we have the ability to get a velocity to extrapolate with. + // + // However, we only want to extraploate if the server is choking. We don't want to extrapolate if + // the object legimately stopped moving and the server stopped sending updates for it. + // + // The way we know that the server is choking is if we haven't heard ANYTHING from it for a while. + // The server's update interval should be at least as often as our interpolation amount (otherwise, + // we wouldn't have the ability to interpolate). + // + // So right here, if we see that we haven't gotten any server updates for a whole interpolation + // interval, then we know the server is choking. + // + // The End + if ( m_InterpolationAmount > 0.000001f && + CInterpolationContext::GetLastTimeStamp() <= (currentTime - m_InterpolationAmount) ) + { + bExtrapolate = true; + } + } + } + + if ( bExtrapolate ) + { + // Get the velocity from the last segment. + _Derivative_Linear( pOut, &history[realOlder], &history[info.newer] ); + + // Now ramp it to zero after cl_extrapolate_amount.. + float flDestTime = currentTime - m_InterpolationAmount; + float diff = flDestTime - history[info.newer].flChangeTime; + diff = clamp( diff, 0, cl_extrapolate_amount.GetFloat() * 2 ); + if ( diff > cl_extrapolate_amount.GetFloat() ) + { + float scale = 1 - (diff - cl_extrapolate_amount.GetFloat()) / cl_extrapolate_amount.GetFloat(); + for ( int i=0; i < m_nMaxCount; i++ ) + { + pOut[i] *= scale; + } + } + } + else + { + _Derivative_Linear( pOut, &history[info.older], &history[info.newer] ); + } + +} + + +template< typename Type, bool IS_ARRAY > +inline int CInterpolatedVarArrayBase::Interpolate( float currentTime ) +{ + return Interpolate( currentTime, m_InterpolationAmount ); +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::Copy( IInterpolatedVar *pInSrc ) +{ + CInterpolatedVarArrayBase *pSrc = dynamic_cast< CInterpolatedVarArrayBase* >( pInSrc ); + + if ( !pSrc || pSrc->m_nMaxCount != m_nMaxCount ) + { + Assert( false ); + return; + } + + Assert( (m_fType & ~EXCLUDE_AUTO_INTERPOLATE) == (pSrc->m_fType & ~EXCLUDE_AUTO_INTERPOLATE) ); + Assert( m_pDebugName == pSrc->GetDebugName() ); + + for ( int i=0; i < m_nMaxCount; i++ ) + { + m_LastNetworkedValue[i] = pSrc->m_LastNetworkedValue[i]; + m_bLooping[i] = pSrc->m_bLooping[i]; + } + + m_LastNetworkedTime = pSrc->m_LastNetworkedTime; + + // Copy the entries. + m_VarHistory.RemoveAll(); + + for ( int i = 0; i < pSrc->m_VarHistory.Count(); i++ ) + { + int newslot = m_VarHistory.AddToTail(); + + CInterpolatedVarEntry *dest = &m_VarHistory[newslot]; + CInterpolatedVarEntry *src = &pSrc->m_VarHistory[i]; + dest->NewEntry( src->GetValue(), m_nMaxCount, src->flChangeTime ); + } +} + +template< typename Type, bool IS_ARRAY > +inline const Type& CInterpolatedVarArrayBase::GetPrev( int iArrayIndex ) const +{ + Assert( m_pValue ); + Assert( iArrayIndex >= 0 && iArrayIndex < m_nMaxCount ); + + if ( m_VarHistory.Count() > 1 ) + { + return m_VarHistory[1].GetValue()[iArrayIndex]; + } + return m_pValue[ iArrayIndex ]; +} + +template< typename Type, bool IS_ARRAY > +inline const Type& CInterpolatedVarArrayBase::GetCurrent( int iArrayIndex ) const +{ + Assert( m_pValue ); + Assert( iArrayIndex >= 0 && iArrayIndex < m_nMaxCount ); + + if ( m_VarHistory.Count() > 0 ) + { + return m_VarHistory[0].GetValue()[iArrayIndex]; + } + return m_pValue[ iArrayIndex ]; +} + +template< typename Type, bool IS_ARRAY > +inline float CInterpolatedVarArrayBase::GetInterval() const +{ + if ( m_VarHistory.Count() > 1 ) + { + return m_VarHistory[0].flChangeTime - m_VarHistory[1].flChangeTime; + } + + return 0.0f; +} + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::IsValidIndex( int i ) +{ + return m_VarHistory.IsValidIndex( i ); +} + +template< typename Type, bool IS_ARRAY > +inline Type *CInterpolatedVarArrayBase::GetHistoryValue( int index, float& flChangeTime, int iArrayIndex ) +{ + Assert( iArrayIndex >= 0 && iArrayIndex < m_nMaxCount ); + if ( m_VarHistory.IsIdxValid(index) ) + { + CInterpolatedVarEntry *entry = &m_VarHistory[ index ]; + flChangeTime = entry->flChangeTime; + return &entry->GetValue()[ iArrayIndex ]; + } + else + { + flChangeTime = 0.0f; + return NULL; + } +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::SetHistoryValuesForItem( int item, Type& value ) +{ + Assert( item >= 0 && item < m_nMaxCount ); + + for ( int i = 0; i < m_VarHistory.Count(); i++ ) + { + CInterpolatedVarEntry *entry = &m_VarHistory[ i ]; + entry->GetValue()[ item ] = value; + } +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::SetLooping( bool looping, int iArrayIndex ) +{ + Assert( iArrayIndex >= 0 && iArrayIndex < m_nMaxCount ); + m_bLooping[ iArrayIndex ] = looping; +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::SetMaxCount( float flCurrentTime, int newmax ) +{ + bool changed = ( newmax != m_nMaxCount ) ? true : false; + + // BUGBUG: Support 0 length properly? + newmax = MAX(1,newmax); + + m_nMaxCount = newmax; + // Wipe everything any time this changes!!! + if ( changed ) + { + delete [] m_bLooping; + delete [] m_LastNetworkedValue; + m_bLooping = new byte[m_nMaxCount]; + m_LastNetworkedValue = new Type[m_nMaxCount]; + memset( m_bLooping, 0, sizeof(byte) * m_nMaxCount); + memset( m_LastNetworkedValue, 0, sizeof(Type) * m_nMaxCount); + + Reset( flCurrentTime ); + } +} + + +template< typename Type, bool IS_ARRAY > +inline int CInterpolatedVarArrayBase::GetMaxCount() const +{ + return m_nMaxCount; +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Interpolate( Type *out, float frac, CInterpolatedVarEntry *start, CInterpolatedVarEntry *end ) +{ + Assert( start ); + Assert( end ); + + if ( start == end ) + { + // quick exit + for ( int i = 0; i < m_nMaxCount; i++ ) + { + out[i] = end->GetValue()[i]; + Lerp_Clamp( out[i] ); + } + return; + } + + Assert( frac >= 0.0f && frac <= 1.0f ); + + // Note that QAngle has a specialization that will do quaternion interpolation here... + for ( int i = 0; i < m_nMaxCount; i++ ) + { + if ( m_bLooping[ i ] ) + { + out[i] = LoopingLerp( frac, start->GetValue()[i], end->GetValue()[i] ); + } + else + { + out[i] = Lerp( frac, start->GetValue()[i], end->GetValue()[i] ); + } + Lerp_Clamp( out[i] ); + } +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Extrapolate( + Type *pOut, + CInterpolatedVarEntry *pOld, + CInterpolatedVarEntry *pNew, + float flDestinationTime, + float flMaxExtrapolationAmount + ) +{ + if ( fabs( pOld->flChangeTime - pNew->flChangeTime ) < 0.001f || flDestinationTime <= pNew->flChangeTime ) + { + for ( int i=0; i < m_nMaxCount; i++ ) + pOut[i] = pNew->GetValue()[i]; + } + else + { + float flExtrapolationAmount = MIN( flDestinationTime - pNew->flChangeTime, flMaxExtrapolationAmount ); + + float divisor = 1.0f / (pNew->flChangeTime - pOld->flChangeTime); + for ( int i=0; i < m_nMaxCount; i++ ) + { + pOut[i] = ExtrapolateInterpolatedVarType( pOld->GetValue()[i], pNew->GetValue()[i], divisor, flExtrapolationAmount ); + } + } +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::TimeFixup2_Hermite( + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry &fixup, + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& prev, + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& start, + float dt1 + ) +{ + float dt2 = start->flChangeTime - prev->flChangeTime; + + // If times are not of the same interval renormalize the earlier sample to allow for uniform hermite spline interpolation + if ( fabs( dt1 - dt2 ) > 0.0001f && + dt2 > 0.0001f ) + { + // Renormalize + float frac = dt1 / dt2; + + // Fixed interval into past + fixup.flChangeTime = start->flChangeTime - dt1; + + for ( int i = 0; i < m_nMaxCount; i++ ) + { + if ( m_bLooping[i] ) + { + fixup.GetValue()[i] = LoopingLerp( 1-frac, prev->GetValue()[i], start->GetValue()[i] ); + } + else + { + fixup.GetValue()[i] = Lerp( 1-frac, prev->GetValue()[i], start->GetValue()[i] ); + } + } + + // Point previous sample at fixed version + prev = &fixup; + } +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::TimeFixup_Hermite( + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry &fixup, + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& prev, + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& start, + typename CInterpolatedVarArrayBase::CInterpolatedVarEntry*& end ) +{ + TimeFixup2_Hermite( fixup, prev, start, end->flChangeTime - start->flChangeTime ); +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Interpolate_Hermite( + Type *out, + float frac, + CInterpolatedVarEntry *prev, + CInterpolatedVarEntry *start, + CInterpolatedVarEntry *end, + bool looping ) +{ + Assert( start ); + Assert( end ); + + // Disable range checks because we can produce weird values here and it's not an error. + // After interpolation, we will clamp the values. + CDisableRangeChecks disableRangeChecks; + + CInterpolatedVarEntry fixup; + fixup.Init(m_nMaxCount); + TimeFixup_Hermite( fixup, prev, start, end ); + + for( int i = 0; i < m_nMaxCount; i++ ) + { + // Note that QAngle has a specialization that will do quaternion interpolation here... + if ( m_bLooping[ i ] ) + { + out[ i ] = LoopingLerp_Hermite( out[ i ], frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i] ); + } + else + { + out[ i ] = Lerp_Hermite( out[ i ], frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i] ); + } + + // Clamp the output from interpolation. There are edge cases where something like m_flCycle + // can get set to a really high or low value when we set it to zero after a really small + // time interval (the hermite blender will think it's got a really high velocity and + // skyrocket it off into la-la land). + Lerp_Clamp( out[i] ); + } +} + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Derivative_Hermite( + Type *out, + float frac, + CInterpolatedVarEntry *prev, + CInterpolatedVarEntry *start, + CInterpolatedVarEntry *end ) +{ + Assert( start ); + Assert( end ); + + // Disable range checks because we can produce weird values here and it's not an error. + // After interpolation, we will clamp the values. + CDisableRangeChecks disableRangeChecks; + + CInterpolatedVarEntry fixup; + fixup.value = (Type*)_alloca( sizeof(Type) * m_nMaxCount ); + TimeFixup_Hermite( fixup, prev, start, end ); + + float divisor = 1.0f / (end->flChangeTime - start->flChangeTime); + + for( int i = 0; i < m_nMaxCount; i++ ) + { + Assert( !m_bLooping[ i ] ); + out[i] = Derivative_Hermite( frac, prev->GetValue()[i], start->GetValue()[i], end->GetValue()[i] ); + out[i] *= divisor; + } +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Derivative_Hermite_SmoothVelocity( + Type *out, + float frac, + CInterpolatedVarEntry *b, + CInterpolatedVarEntry *c, + CInterpolatedVarEntry *d ) +{ + CInterpolatedVarEntry fixup; + fixup.Init(m_nMaxCount); + TimeFixup_Hermite( fixup, b, c, d ); + for ( int i=0; i < m_nMaxCount; i++ ) + { + Type prevVel = (c->GetValue()[i] - b->GetValue()[i]) / (c->flChangeTime - b->flChangeTime); + Type curVel = (d->GetValue()[i] - c->GetValue()[i]) / (d->flChangeTime - c->flChangeTime); + out[i] = Lerp( frac, prevVel, curVel ); + } +} + + +template< typename Type, bool IS_ARRAY > +inline void CInterpolatedVarArrayBase::_Derivative_Linear( + Type *out, + CInterpolatedVarEntry *start, + CInterpolatedVarEntry *end ) +{ + if ( start == end || fabs( start->flChangeTime - end->flChangeTime ) < 0.0001f ) + { + for( int i = 0; i < m_nMaxCount; i++ ) + { + out[ i ] = start->GetValue()[i] * 0; + } + } + else + { + float divisor = 1.0f / (end->flChangeTime - start->flChangeTime); + for( int i = 0; i < m_nMaxCount; i++ ) + { + out[ i ] = (end->GetValue()[i] - start->GetValue()[i]) * divisor; + } + } +} + + +template< typename Type, bool IS_ARRAY > +inline bool CInterpolatedVarArrayBase::ValidOrder() +{ + float newestchangetime = 0.0f; + bool first = true; + for ( int i = 0; i < m_VarHistory.Count(); i++ ) + { + CInterpolatedVarEntry *entry = &m_VarHistory[ i ]; + if ( first ) + { + first = false; + newestchangetime = entry->flChangeTime; + continue; + } + + // They should get older as wel walk backwards + if ( entry->flChangeTime > newestchangetime ) + { + Assert( 0 ); + return false; + } + + newestchangetime = entry->flChangeTime; + } + + return true; +} + +template< typename Type, int COUNT > +class CInterpolatedVarArray : public CInterpolatedVarArrayBase +{ +public: + CInterpolatedVarArray( const char *pDebugName = "no debug name" ) + : CInterpolatedVarArrayBase( pDebugName ) + { + SetMaxCount( 0.0f, COUNT ); + } +}; + + +// -------------------------------------------------------------------------------------------------------------- // +// CInterpolatedVar. +// -------------------------------------------------------------------------------------------------------------- // + +template< typename Type > +class CInterpolatedVar : public CInterpolatedVarArrayBase< Type, false > +{ +public: + CInterpolatedVar( const char *pDebugName = NULL ) + : CInterpolatedVarArrayBase< Type, false >(pDebugName) + { + SetMaxCount( 0.0f, 1 ); + } +}; + +#include "tier0/memdbgoff.h" + +#endif // INTERPOLATEDVAR_H diff --git a/public/tier1/lerp_functions.h b/public/tier1/lerp_functions.h new file mode 100644 index 0000000..1729be2 --- /dev/null +++ b/public/tier1/lerp_functions.h @@ -0,0 +1,170 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef LERP_FUNCTIONS_H +#define LERP_FUNCTIONS_H +#ifdef _WIN32 +#pragma once +#endif + + +template +inline T LoopingLerp( float flPercent, T flFrom, T flTo ) +{ + T s = flTo * flPercent + flFrom * (1.0f - flPercent); + return s; +} + +template <> +inline float LoopingLerp( float flPercent, float flFrom, float flTo ) +{ + if ( fabs( flTo - flFrom ) >= 0.5f ) + { + if (flFrom < flTo) + flFrom += 1.0f; + else + flTo += 1.0f; + } + + float s = flTo * flPercent + flFrom * (1.0f - flPercent); + + s = s - (int)(s); + if (s < 0.0f) + s = s + 1.0f; + + return s; +} + +template +inline T Lerp_Hermite( const T& /*current*/, float t, const T& p0, const T& p1, const T& p2 ) +{ + T d1 = p1 - p0; + T d2 = p2 - p1; + + T output; + float tSqr = t*t; + float tCube = t*tSqr; + + output = p1 * (2*tCube-3*tSqr+1); + output += p2 * (-2*tCube+3*tSqr); + output += d1 * (tCube-2*tSqr+t); + output += d2 * (tCube-tSqr); + + return output; +} + + +template +inline T Derivative_Hermite( float t, const T& p0, const T& p1, const T& p2 ) +{ + T d1 = p1 - p0; + T d2 = p2 - p1; + + T output; + float tSqr = t*t; + + output = p1 * (6*tSqr - 6*t); + output += p2 * (-6*tSqr + 6*t); + output += d1 * (3*tSqr - 4*t + 1); + output += d2 * (3*tSqr - 2*t); + + return output; +} + + +inline void Lerp_Clamp( int val ) +{ +} + +inline void Lerp_Clamp( float val ) +{ +} + +inline void Lerp_Clamp( const Vector &val ) +{ +} + +inline void Lerp_Clamp( const QAngle &val ) +{ +} + + +// If we have a range checked var, then we can clamp to its limits. +template< class T, int minValue, int maxValue, int startValue > +inline void Lerp_Clamp( CRangeCheckedVar &val ) +{ + val.Clamp(); +} + + +template<> +inline QAngle Lerp_Hermite( const QAngle &/*current*/, float t, const QAngle& p0, const QAngle& p1, const QAngle& p2 ) +{ + // Can't do hermite with QAngles, get discontinuities, just do a regular interpolation + return Lerp( t, p1, p2 ); +} + +template +inline T LoopingLerp_Hermite( T current, float t, T p0, T p1, T p2 ) +{ + return Lerp_Hermite( current, t, p0, p1, p2 ); +} + +template <> +inline float LoopingLerp_Hermite( float /*current*/, float t, float p0, float p1, float p2 ) +{ + if ( fabs( p1 - p0 ) > 0.5f ) + { + if ( p0 < p1 ) + p0 += 1.0f; + else + p1 += 1.0f; + } + + if ( fabs( p2 - p1 ) > 0.5f ) + { + if ( p1 < p2 ) + { + p1 += 1.0f; + + // see if we need to fix up p0 + // important for vars that are decreasing from p0->p1->p2 where + // p1 is fixed up relative to p2, eg p0 = 0.2, p1 = 0.1, p2 = 0.9 + if ( abs( p1 - p0 ) > 0.5 ) + { + if ( p0 < p1 ) + p0 += 1.0f; + else + p1 += 1.0f; + } + } + else + { + p2 += 1.0f; + } + } + + float s = Lerp_Hermite( /*current*/ 0.0f, t, p0, p1, p2 ); + + s = s - (int)(s); + if (s < 0.0f) + { + s = s + 1.0f; + } + + return s; +} + +template< int minValue, int maxValue, int startValue > +inline CRangeCheckedVar LoopingLerp_Hermite( CRangeCheckedVar current, float t, CRangeCheckedVar p0, CRangeCheckedVar p1, CRangeCheckedVar p2 ) +{ + return LoopingLerp_Hermite( (float)current, t, (float)p0, (float)p1, (float)p2 ); +} + +// NOTE: C_AnimationLayer has its own versions of these functions in animationlayer.h. + + +#endif // LERP_FUNCTIONS_H diff --git a/public/tier1/lzmaDecoder.h b/public/tier1/lzmaDecoder.h new file mode 100644 index 0000000..8cb0187 --- /dev/null +++ b/public/tier1/lzmaDecoder.h @@ -0,0 +1,44 @@ +//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ============// +// +// LZMA Decoder. Designed for run time decoding. +// +// LZMA SDK 4.43 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) +// http://www.7-zip.org/ +// +//=====================================================================================// + +#ifndef _LZMADECODER_H +#define _LZMADECODER_H +#pragma once + +#if !defined( _X360 ) +#define LZMA_ID (('A'<<24)|('M'<<16)|('Z'<<8)|('L')) +#else +#define LZMA_ID (('L'<<24)|('Z'<<16)|('M'<<8)|('A')) +#endif + +// bind the buffer for correct identification +#pragma pack(1) +struct lzma_header_t +{ + unsigned int id; + unsigned int actualSize; // always little endian + unsigned int lzmaSize; // always little endian + unsigned char properties[5]; +}; +#pragma pack() + +typedef void ( *LZMAReadProgressCallbackFunc_t )(); + +class CLZMA +{ +public: + unsigned int Uncompress( unsigned char *pInput, unsigned char *pOutput, LZMAReadProgressCallbackFunc_t pCallback = NULL ); + bool IsCompressed( unsigned char *pInput ); + unsigned int GetActualSize( unsigned char *pInput ); + +private: +}; + +#endif + diff --git a/public/tier1/lzss.h b/public/tier1/lzss.h new file mode 100644 index 0000000..5fdd371 --- /dev/null +++ b/public/tier1/lzss.h @@ -0,0 +1,71 @@ +//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ============// +// +// LZSS Codec. Designed for fast cheap gametime encoding/decoding. Compression results +// are not aggresive as other alogrithms, but gets 2:1 on most arbitrary uncompressed data. +// +//=====================================================================================// + +#ifndef _LZSS_H +#define _LZSS_H +#pragma once + +#if !defined( _X360 ) +#define LZSS_ID (('S'<<24)|('S'<<16)|('Z'<<8)|('L')) +#else +#define LZSS_ID (('L'<<24)|('Z'<<16)|('S'<<8)|('S')) +#endif + +// bind the buffer for correct identification +struct lzss_header_t +{ + unsigned int id; + unsigned int actualSize; // always little endian +}; + +class CUtlBuffer; + +#define DEFAULT_LZSS_WINDOW_SIZE 4096 + +class CLZSS +{ +public: + unsigned char* Compress( unsigned char *pInput, int inputlen, unsigned int *pOutputSize ); + unsigned char* CompressNoAlloc( unsigned char *pInput, int inputlen, unsigned char *pOutput, unsigned int *pOutputSize ); + unsigned int Uncompress( unsigned char *pInput, unsigned char *pOutput ); + //unsigned int Uncompress( unsigned char *pInput, CUtlBuffer &buf ); + unsigned int SafeUncompress( unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize ); + bool IsCompressed( unsigned char *pInput ); + unsigned int GetActualSize( unsigned char *pInput ); + + // windowsize must be a power of two. + FORCEINLINE CLZSS( int nWindowSize = DEFAULT_LZSS_WINDOW_SIZE ); + +private: + // expected to be sixteen bytes + struct lzss_node_t + { + unsigned char *pData; + lzss_node_t *pPrev; + lzss_node_t *pNext; + char empty[4]; + }; + + struct lzss_list_t + { + lzss_node_t *pStart; + lzss_node_t *pEnd; + }; + + void BuildHash( unsigned char *pData ); + lzss_list_t *m_pHashTable; + lzss_node_t *m_pHashTarget; + int m_nWindowSize; + +}; + +FORCEINLINE CLZSS::CLZSS( int nWindowSize ) +{ + m_nWindowSize = nWindowSize; +} +#endif + diff --git a/public/tier1/mempool.h b/public/tier1/mempool.h new file mode 100644 index 0000000..e5872c9 --- /dev/null +++ b/public/tier1/mempool.h @@ -0,0 +1,628 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef MEMPOOL_H +#define MEMPOOL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/memalloc.h" +#include "tier0/tslist.h" +#include "tier0/platform.h" +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" + +//----------------------------------------------------------------------------- +// Purpose: Optimized pool memory allocator +//----------------------------------------------------------------------------- + +typedef void (*MemoryPoolReportFunc_t)( char const* pMsg, ... ); + +class CUtlMemoryPool +{ +public: + // Ways the memory pool can grow when it needs to make a new blob. + enum MemoryPoolGrowType_t + { + GROW_NONE=0, // Don't allow new blobs. + GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates + // get larger and larger each time it allocates one). + GROW_SLOW=2 // New blob size is numElements. + }; + + CUtlMemoryPool( int blockSize, int numElements, int growMode = GROW_FAST, const char *pszAllocOwner = NULL, int nAlignment = 0 ); + ~CUtlMemoryPool(); + + void* Alloc(); // Allocate the element size you specified in the constructor. + void* Alloc( size_t amount ); + void* AllocZero(); // Allocate the element size you specified in the constructor, zero the memory before construction + void* AllocZero( size_t amount ); + void Free(void *pMem); + + // Frees everything + void Clear(); + + // Error reporting... + static void SetErrorReportFunc( MemoryPoolReportFunc_t func ); + + // returns number of allocated blocks + int Count() const { return m_BlocksAllocated; } + int PeakCount() const { return m_PeakAlloc; } + int BlockSize() const { return m_BlockSize; } + int Size() const { return m_NumBlobs * m_BlocksPerBlob * m_BlockSize; } + + bool IsAllocationWithinPool( void *pMem ) const; + +protected: + class CBlob + { + public: + CBlob *m_pPrev, *m_pNext; + int m_NumBytes; // Number of bytes in this blob. + char m_Data[1]; + char m_Padding[3]; // to int align the struct + }; + + // Resets the pool + void Init(); + void AddNewBlob(); + void ReportLeaks(); + + int m_BlockSize; + int m_BlocksPerBlob; + + int m_GrowMode; // GROW_ enum. + + // FIXME: Change m_ppMemBlob into a growable array? + void *m_pHeadOfFreeList; + int m_BlocksAllocated; + int m_PeakAlloc; + unsigned short m_nAlignment; + unsigned short m_NumBlobs; + const char * m_pszAllocOwner; + // CBlob could be not a multiple of 4 bytes so stuff it at the end here to keep us otherwise aligned + CBlob m_BlobHead; + + static MemoryPoolReportFunc_t g_ReportFunc; +}; + + +//----------------------------------------------------------------------------- +// Multi-thread/Thread Safe Memory Class +//----------------------------------------------------------------------------- +class CMemoryPoolMT : public CUtlMemoryPool +{ +public: + CMemoryPoolMT( int blockSize, int numElements, int growMode = GROW_FAST, const char *pszAllocOwner = NULL, int nAlignment = 0) : CUtlMemoryPool( blockSize, numElements, growMode, pszAllocOwner, nAlignment ) {} + + + void* Alloc() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc(); } + void* Alloc( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc( amount ); } + void* AllocZero() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero(); } + void* AllocZero( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero( amount ); } + void Free(void *pMem) { AUTO_LOCK( m_mutex ); CUtlMemoryPool::Free( pMem ); } + + // Frees everything + void Clear() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Clear(); } +private: + CThreadFastMutex m_mutex; // @TODO: Rework to use tslist (toml 7/6/2007) +}; + + +//----------------------------------------------------------------------------- +// Wrapper macro to make an allocator that returns particular typed allocations +// and construction and destruction of objects. +//----------------------------------------------------------------------------- +template< class T > +class CClassMemoryPool : public CUtlMemoryPool +{ +public: + CClassMemoryPool(int numElements, int growMode = GROW_FAST, int nAlignment = 0 ) : + CUtlMemoryPool( sizeof(T), numElements, growMode, MEM_ALLOC_CLASSNAME(T), nAlignment ) {} + + T* Alloc(); + T* AllocZero(); + void Free( T *pMem ); + + void Clear(); +}; + +//----------------------------------------------------------------------------- +// Specialized pool for aligned data management (e.g., Xbox textures) +//----------------------------------------------------------------------------- +template +class CAlignedMemPool +{ + enum + { + BLOCK_SIZE = COMPILETIME_MAX( ALIGN_VALUE( ITEM_SIZE, ALIGNMENT ), 8 ), + }; + +public: + CAlignedMemPool(); + + void *Alloc(); + void Free( void *p ); + + static int __cdecl CompareChunk( void * const *ppLeft, void * const *ppRight ); + void Compact(); + + int NumTotal() { AUTO_LOCK( m_mutex ); return m_Chunks.Count() * ( CHUNK_SIZE / BLOCK_SIZE ); } + int NumAllocated() { AUTO_LOCK( m_mutex ); return NumTotal() - m_nFree; } + int NumFree() { AUTO_LOCK( m_mutex ); return m_nFree; } + + int BytesTotal() { AUTO_LOCK( m_mutex ); return NumTotal() * BLOCK_SIZE; } + int BytesAllocated() { AUTO_LOCK( m_mutex ); return NumAllocated() * BLOCK_SIZE; } + int BytesFree() { AUTO_LOCK( m_mutex ); return NumFree() * BLOCK_SIZE; } + + int ItemSize() { return ITEM_SIZE; } + int BlockSize() { return BLOCK_SIZE; } + int ChunkSize() { return CHUNK_SIZE; } + +private: + struct FreeBlock_t + { + FreeBlock_t *pNext; + byte reserved[ BLOCK_SIZE - sizeof( FreeBlock_t *) ]; + }; + + CUtlVector m_Chunks; // Chunks are tracked outside blocks (unlike CUtlMemoryPool) to simplify alignment issues + FreeBlock_t * m_pFirstFree; + int m_nFree; + CAllocator m_Allocator; + float m_TimeLastCompact; + + CThreadFastMutex m_mutex; +}; + +//----------------------------------------------------------------------------- +// Pool variant using standard allocation +//----------------------------------------------------------------------------- +template +class CObjectPool +{ +public: + CObjectPool() + { + int i = nInitialCount; + while ( i-- > 0 ) + { + m_AvailableObjects.PushItem( new T ); + } + } + + ~CObjectPool() + { + Purge(); + } + + int NumAvailable() + { + return m_AvailableObjects.Count(); + } + + void Purge() + { + T *p; + while ( m_AvailableObjects.PopItem( &p ) ) + { + delete p; + } + } + + T *GetObject( bool bCreateNewIfEmpty = bDefCreateNewIfEmpty ) + { + T *p; + if ( !m_AvailableObjects.PopItem( &p ) ) + { + p = ( bCreateNewIfEmpty ) ? new T : NULL; + } + return p; + } + + void PutObject( T *p ) + { + m_AvailableObjects.PushItem( p ); + } + +private: + CTSList m_AvailableObjects; +}; + +//----------------------------------------------------------------------------- +// Fixed budget pool with overflow to malloc +//----------------------------------------------------------------------------- +template +class CFixedBudgetMemoryPool +{ +public: + CFixedBudgetMemoryPool() + { + m_pBase = m_pLimit = 0; + COMPILE_TIME_ASSERT( ITEM_SIZE % 4 == 0 ); + } + + bool Owns( void *p ) + { + return ( p >= m_pBase && p < m_pLimit ); + } + + void *Alloc() + { + MEM_ALLOC_CREDIT_CLASS(); +#ifndef USE_MEM_DEBUG + if ( !m_pBase ) + { + LOCAL_THREAD_LOCK(); + if ( !m_pBase ) + { + byte *pMemory = m_pBase = (byte *)malloc( ITEM_COUNT * ITEM_SIZE ); + m_pLimit = m_pBase + ( ITEM_COUNT * ITEM_SIZE ); + + for ( int i = 0; i < ITEM_COUNT; i++ ) + { + m_freeList.Push( (TSLNodeBase_t *)pMemory ); + pMemory += ITEM_SIZE; + } + } + } + + void *p = m_freeList.Pop(); + if ( p ) + return p; +#endif + return malloc( ITEM_SIZE ); + } + + void Free( void *p ) + { +#ifndef USE_MEM_DEBUG + if ( Owns( p ) ) + m_freeList.Push( (TSLNodeBase_t *)p ); + else +#endif + free( p ); + } + + enum + { + ITEM_SIZE = ALIGN_VALUE( PROVIDED_ITEM_SIZE, 4 ) + }; + + CTSListBase m_freeList; + byte *m_pBase; + byte *m_pLimit; +}; + +#define BIND_TO_FIXED_BUDGET_POOL( poolName ) \ + inline void* operator new( size_t size ) { return poolName.Alloc(); } \ + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { return poolName.Alloc(); } \ + inline void operator delete( void* p ) { poolName.Free(p); } \ + inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { poolName.Free(p); } + +//----------------------------------------------------------------------------- + + +template< class T > +inline T* CClassMemoryPool::Alloc() +{ + T *pRet; + + { + MEM_ALLOC_CREDIT_CLASS(); + pRet = (T*)CUtlMemoryPool::Alloc(); + } + + if ( pRet ) + { + Construct( pRet ); + } + return pRet; +} + +template< class T > +inline T* CClassMemoryPool::AllocZero() +{ + T *pRet; + + { + MEM_ALLOC_CREDIT_CLASS(); + pRet = (T*)CUtlMemoryPool::AllocZero(); + } + + if ( pRet ) + { + Construct( pRet ); + } + return pRet; +} + +template< class T > +inline void CClassMemoryPool::Free(T *pMem) +{ + if ( pMem ) + { + Destruct( pMem ); + } + + CUtlMemoryPool::Free( pMem ); +} + +template< class T > +inline void CClassMemoryPool::Clear() +{ + CUtlRBTree freeBlocks; + SetDefLessFunc( freeBlocks ); + + void *pCurFree = m_pHeadOfFreeList; + while ( pCurFree != NULL ) + { + freeBlocks.Insert( pCurFree ); + pCurFree = *((void**)pCurFree); + } + + for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) + { + T *p = (T *)pCur->m_Data; + T *pLimit = (T *)(pCur->m_Data + pCur->m_NumBytes); + while ( p < pLimit ) + { + if ( freeBlocks.Find( p ) == freeBlocks.InvalidIndex() ) + { + Destruct( p ); + } + p++; + } + } + + CUtlMemoryPool::Clear(); +} + + +//----------------------------------------------------------------------------- +// Macros that make it simple to make a class use a fixed-size allocator +// Put DECLARE_FIXEDSIZE_ALLOCATOR in the private section of a class, +// Put DEFINE_FIXEDSIZE_ALLOCATOR in the CPP file +//----------------------------------------------------------------------------- +#define DECLARE_FIXEDSIZE_ALLOCATOR( _class ) \ + public: \ + inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ + inline void operator delete( void* p ) { s_Allocator.Free(p); } \ + inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \ + private: \ + static CUtlMemoryPool s_Allocator + +#define DEFINE_FIXEDSIZE_ALLOCATOR( _class, _initsize, _grow ) \ + CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool") + +#define DEFINE_FIXEDSIZE_ALLOCATOR_ALIGNED( _class, _initsize, _grow, _alignment ) \ + CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool", _alignment ) + +#define DECLARE_FIXEDSIZE_ALLOCATOR_MT( _class ) \ + public: \ + inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ + inline void operator delete( void* p ) { s_Allocator.Free(p); } \ + inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \ + private: \ + static CMemoryPoolMT s_Allocator + +#define DEFINE_FIXEDSIZE_ALLOCATOR_MT( _class, _initsize, _grow ) \ + CMemoryPoolMT _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool") + +//----------------------------------------------------------------------------- +// Macros that make it simple to make a class use a fixed-size allocator +// This version allows us to use a memory pool which is externally defined... +// Put DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the private section of a class, +// Put DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the CPP file +//----------------------------------------------------------------------------- + +#define DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class ) \ + public: \ + inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \ + inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \ + inline void operator delete( void* p ) { s_pAllocator->Free(p); } \ + private: \ + static CUtlMemoryPool* s_pAllocator + +#define DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class, _allocator ) \ + CUtlMemoryPool* _class::s_pAllocator = _allocator + + +template +inline CAlignedMemPool::CAlignedMemPool() + : m_pFirstFree( 0 ), + m_nFree( 0 ), + m_TimeLastCompact( 0 ) +{ + COMPILE_TIME_ASSERT( sizeof( FreeBlock_t ) >= BLOCK_SIZE ); + COMPILE_TIME_ASSERT( ALIGN_VALUE( sizeof( FreeBlock_t ), ALIGNMENT ) == sizeof( FreeBlock_t ) ); +} + +template +inline void *CAlignedMemPool::Alloc() +{ + AUTO_LOCK( m_mutex ); + + if ( !m_pFirstFree ) + { + if ( !GROWMODE && m_Chunks.Count() ) + { + return NULL; + } + + FreeBlock_t *pNew = (FreeBlock_t *)m_Allocator.Alloc( CHUNK_SIZE ); + Assert( (unsigned)pNew % ALIGNMENT == 0 ); + m_Chunks.AddToTail( pNew ); + m_nFree = CHUNK_SIZE / BLOCK_SIZE; + m_pFirstFree = pNew; + for ( int i = 0; i < m_nFree - 1; i++ ) + { + pNew->pNext = pNew + 1; + pNew++; + } + pNew->pNext = NULL; + } + + void *p = m_pFirstFree; + m_pFirstFree = m_pFirstFree->pNext; + m_nFree--; + + return p; +} + +template +inline void CAlignedMemPool::Free( void *p ) +{ + AUTO_LOCK( m_mutex ); + + // Insertion sort to encourage allocation clusters in chunks + FreeBlock_t *pFree = ((FreeBlock_t *)p); + FreeBlock_t *pCur = m_pFirstFree; + FreeBlock_t *pPrev = NULL; + + while ( pCur && pFree > pCur ) + { + pPrev = pCur; + pCur = pCur->pNext; + } + + pFree->pNext = pCur; + + if ( pPrev ) + { + pPrev->pNext = pFree; + } + else + { + m_pFirstFree = pFree; + } + m_nFree++; + + if ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD ) + { + float time = Plat_FloatTime(); + float compactTime = ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD * 4 ) ? 15.0 : 30.0; + if ( m_TimeLastCompact > time || m_TimeLastCompact + compactTime < Plat_FloatTime() ) + { + Compact(); + m_TimeLastCompact = time; + } + } +} + +template +inline int __cdecl CAlignedMemPool::CompareChunk( void * const *ppLeft, void * const *ppRight ) +{ + return ((unsigned)*ppLeft) - ((unsigned)*ppRight); +} + +template +inline void CAlignedMemPool::Compact() +{ + FreeBlock_t *pCur = m_pFirstFree; + FreeBlock_t *pPrev = NULL; + + m_Chunks.Sort( CompareChunk ); + +#ifdef VALIDATE_ALIGNED_MEM_POOL + { + FreeBlock_t *p = m_pFirstFree; + while ( p ) + { + if ( p->pNext && p > p->pNext ) + { + __asm { int 3 } + } + p = p->pNext; + } + + for ( int i = 0; i < m_Chunks.Count(); i++ ) + { + if ( i + 1 < m_Chunks.Count() ) + { + if ( m_Chunks[i] > m_Chunks[i + 1] ) + { + __asm { int 3 } + } + } + } + } +#endif + + int i; + + for ( i = 0; i < m_Chunks.Count(); i++ ) + { + int nBlocksPerChunk = CHUNK_SIZE / BLOCK_SIZE; + FreeBlock_t *pChunkLimit = ((FreeBlock_t *)m_Chunks[i]) + nBlocksPerChunk; + int nFromChunk = 0; + if ( pCur == m_Chunks[i] ) + { + FreeBlock_t *pFirst = pCur; + while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit ) + { + pCur = pCur->pNext; + nFromChunk++; + } + pCur = pFirst; + + } + + while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit ) + { + if ( nFromChunk != nBlocksPerChunk ) + { + if ( pPrev ) + { + pPrev->pNext = pCur; + } + else + { + m_pFirstFree = pCur; + } + pPrev = pCur; + } + else if ( pPrev ) + { + pPrev->pNext = NULL; + } + else + { + m_pFirstFree = NULL; + } + + pCur = pCur->pNext; + } + + if ( nFromChunk == nBlocksPerChunk ) + { + m_Allocator.Free( m_Chunks[i] ); + m_nFree -= nBlocksPerChunk; + m_Chunks[i] = 0; + } + } + + for ( i = m_Chunks.Count() - 1; i >= 0 ; i-- ) + { + if ( !m_Chunks[i] ) + { + m_Chunks.FastRemove( i ); + } + } +} + +#endif // MEMPOOL_H diff --git a/public/tier1/memstack.h b/public/tier1/memstack.h new file mode 100644 index 0000000..3e27f1e --- /dev/null +++ b/public/tier1/memstack.h @@ -0,0 +1,345 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A fast stack memory allocator that uses virtual memory if available +// +//===========================================================================// + +#ifndef MEMSTACK_H +#define MEMSTACK_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier1/utlvector.h" + +//----------------------------------------------------------------------------- + +typedef unsigned MemoryStackMark_t; + +class CMemoryStack +{ +public: + CMemoryStack(); + ~CMemoryStack(); + + bool Init( unsigned maxSize = 0, unsigned commitSize = 0, unsigned initialCommit = 0, unsigned alignment = 16 ); +#ifdef _X360 + bool InitPhysical( uint size, uint nBaseAddrAlignment, uint alignment = 16, uint32 nAdditionalFlags = 0 ); +#endif + void Term(); + + int GetSize(); + int GetMaxSize(); + int GetUsed(); + + void *Alloc( unsigned bytes, bool bClear = false ) RESTRICT; + + MemoryStackMark_t GetCurrentAllocPoint(); + void FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit = true ); + void FreeAll( bool bDecommit = true ); + + void Access( void **ppRegion, unsigned *pBytes ); + + void PrintContents(); + + void *GetBase(); + const void *GetBase() const { return const_cast(this)->GetBase(); } + + bool CommitSize( int ); + +private: + bool CommitTo( byte * ) RESTRICT; + void RegisterAllocation(); + void RegisterDeallocation(); + + byte *m_pNextAlloc; + byte *m_pCommitLimit; + byte *m_pAllocLimit; + + byte *m_pBase; + bool m_bRegisteredAllocation; + + unsigned m_maxSize; + unsigned m_alignment; +#ifdef _WIN32 + unsigned m_commitSize; + unsigned m_minCommit; +#endif +#ifdef _X360 + bool m_bPhysical; +#endif +}; + +//------------------------------------- + +FORCEINLINE void *CMemoryStack::Alloc( unsigned bytes, bool bClear ) RESTRICT +{ + Assert( m_pBase ); + + int alignment = m_alignment; + if ( bytes ) + { + bytes = AlignValue( bytes, alignment ); + } + else + { + bytes = alignment; + } + + + void *pResult = m_pNextAlloc; + byte *pNextAlloc = m_pNextAlloc + bytes; + + if ( pNextAlloc > m_pCommitLimit ) + { + if ( !CommitTo( pNextAlloc ) ) + { + return NULL; + } + } + + if ( bClear ) + { + memset( pResult, 0, bytes ); + } + + m_pNextAlloc = pNextAlloc; + + return pResult; +} + +//------------------------------------- + +inline bool CMemoryStack::CommitSize( int nBytes ) +{ + if ( GetSize() != nBytes ) + { + return CommitTo( m_pBase + nBytes ); + } + return true; +} + +//------------------------------------- + +inline int CMemoryStack::GetMaxSize() +{ + return m_maxSize; +} + +//------------------------------------- + +inline int CMemoryStack::GetUsed() +{ + return ( m_pNextAlloc - m_pBase ); +} + +//------------------------------------- + +inline void *CMemoryStack::GetBase() +{ + return m_pBase; +} + +//------------------------------------- + +inline MemoryStackMark_t CMemoryStack::GetCurrentAllocPoint() +{ + return ( m_pNextAlloc - m_pBase ); +} + + +//----------------------------------------------------------------------------- +// The CUtlMemoryStack class: +// A fixed memory class +//----------------------------------------------------------------------------- +template< typename T, typename I, size_t MAX_SIZE, size_t COMMIT_SIZE = 0, size_t INITIAL_COMMIT = 0 > +class CUtlMemoryStack +{ +public: + // constructor, destructor + CUtlMemoryStack( int nGrowSize = 0, int nInitSize = 0 ) { m_MemoryStack.Init( MAX_SIZE * sizeof(T), COMMIT_SIZE * sizeof(T), INITIAL_COMMIT * sizeof(T), 4 ); COMPILE_TIME_ASSERT( sizeof(T) % 4 == 0 ); } + CUtlMemoryStack( T* pMemory, int numElements ) { Assert( 0 ); } + + // Can we use this index? + bool IsIdxValid( I i ) const { long x=i; return (x >= 0) && (x < m_nAllocated); } + + // Specify the invalid ('null') index that we'll only return on failure + static const I INVALID_INDEX = ( I )-1; // For use with COMPILE_TIME_ASSERT + static I InvalidIndex() { return INVALID_INDEX; } + + class Iterator_t + { + Iterator_t( I i ) : index( i ) {} + I index; + friend class CUtlMemoryStack; + public: + bool operator==( const Iterator_t it ) const { return index == it.index; } + bool operator!=( const Iterator_t it ) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t( m_nAllocated ? 0 : InvalidIndex() ); } + Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( it.index < m_nAllocated ? it.index + 1 : InvalidIndex() ); } + I GetIndex( const Iterator_t &it ) const { return it.index; } + bool IsIdxAfter( I i, const Iterator_t &it ) const { return i > it.index; } + bool IsValidIterator( const Iterator_t &it ) const { long x=it.index; return x >= 0 && x < m_nAllocated; } + Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); } + + // Gets the base address + T* Base() { return (T*)m_MemoryStack.GetBase(); } + const T* Base() const { return (const T*)m_MemoryStack.GetBase(); } + + // element access + T& operator[]( I i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& operator[]( I i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + T& Element( I i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& Element( I i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + + // Attaches the buffer to external memory.... + void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); } + + // Size + int NumAllocated() const { return m_nAllocated; } + int Count() const { return m_nAllocated; } + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow( int num = 1 ) { Assert( num > 0 ); m_nAllocated += num; m_MemoryStack.Alloc( num * sizeof(T) ); } + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ) { Assert( num <= MAX_SIZE ); if ( m_nAllocated < num ) Grow( num - m_nAllocated ); } + + // Memory deallocation + void Purge() { m_MemoryStack.FreeAll(); m_nAllocated = 0; } + + // is the memory externally allocated? + bool IsExternallyAllocated() const { return false; } + + // Set the size by which the memory grows + void SetGrowSize( int size ) {} + +private: + CMemoryStack m_MemoryStack; + int m_nAllocated; +}; + + +#ifdef _X360 +//----------------------------------------------------------------------------- +// A memory stack used for allocating physical memory on the 360 +// Usage pattern anticipates we usually never go over the initial allocation +// When we do so, we're ok with slightly slower allocation +//----------------------------------------------------------------------------- +class CPhysicalMemoryStack +{ +public: + CPhysicalMemoryStack(); + ~CPhysicalMemoryStack(); + + // The physical memory stack is allocated in chunks. We will initially + // allocate nInitChunkCount chunks, which will always be in memory. + // When FreeAll() is called, it will free down to the initial chunk count + // but not below it. + bool Init( size_t nChunkSizeInBytes, size_t nAlignment, int nInitialChunkCount, uint32 nAdditionalFlags ); + void Term(); + + size_t GetSize() const; + size_t GetPeakUsed() const; + size_t GetUsed() const; + size_t GetFramePeakUsed() const; + + MemoryStackMark_t GetCurrentAllocPoint() const; + void FreeToAllocPoint( MemoryStackMark_t mark, bool bUnused = true ); // bUnused is for interface compat with CMemoryStack + void *Alloc( size_t nSizeInBytes, bool bClear = false ) RESTRICT; + void FreeAll( bool bUnused = true ); // bUnused is for interface compat with CMemoryStack + + void PrintContents(); + +private: + void *AllocFromOverflow( size_t nSizeInBytes ); + + struct PhysicalChunk_t + { + uint8 *m_pBase; + uint8 *m_pNextAlloc; + uint8 *m_pAllocLimit; + }; + + PhysicalChunk_t m_InitialChunk; + CUtlVector< PhysicalChunk_t > m_ExtraChunks; + size_t m_nUsage; + size_t m_nFramePeakUsage; + size_t m_nPeakUsage; + size_t m_nAlignment; + size_t m_nChunkSizeInBytes; + int m_nFirstAvailableChunk; + int m_nAdditionalFlags; + PhysicalChunk_t *m_pLastAllocedChunk; +}; + +//------------------------------------- + +FORCEINLINE void *CPhysicalMemoryStack::Alloc( size_t nSizeInBytes, bool bClear ) RESTRICT +{ + if ( nSizeInBytes ) + { + nSizeInBytes = AlignValue( nSizeInBytes, m_nAlignment ); + } + else + { + nSizeInBytes = m_nAlignment; + } + + // Can't do an allocation bigger than the chunk size + Assert( nSizeInBytes <= m_nChunkSizeInBytes ); + + void *pResult = m_InitialChunk.m_pNextAlloc; + uint8 *pNextAlloc = m_InitialChunk.m_pNextAlloc + nSizeInBytes; + if ( pNextAlloc <= m_InitialChunk.m_pAllocLimit ) + { + m_InitialChunk.m_pNextAlloc = pNextAlloc; + m_pLastAllocedChunk = &m_InitialChunk; + } + else + { + pResult = AllocFromOverflow( nSizeInBytes ); + } + + m_nUsage += nSizeInBytes; + m_nFramePeakUsage = MAX( m_nUsage, m_nFramePeakUsage ); + m_nPeakUsage = MAX( m_nUsage, m_nPeakUsage ); + + if ( bClear ) + { + memset( pResult, 0, nSizeInBytes ); + } + + return pResult; +} + +//------------------------------------- + +inline size_t CPhysicalMemoryStack::GetPeakUsed() const +{ + return m_nPeakUsage; +} + +//------------------------------------- + +inline size_t CPhysicalMemoryStack::GetUsed() const +{ + return m_nUsage; +} + +inline size_t CPhysicalMemoryStack::GetFramePeakUsed() const +{ + return m_nFramePeakUsage; +} + +inline MemoryStackMark_t CPhysicalMemoryStack::GetCurrentAllocPoint() const +{ + Assert( m_pLastAllocedChunk ); + return ( m_pLastAllocedChunk->m_pNextAlloc - m_pLastAllocedChunk->m_pBase ); +} + +#endif // _X360 + +#endif // MEMSTACK_H diff --git a/public/tier1/netadr.h b/public/tier1/netadr.h new file mode 100644 index 0000000..174bc22 --- /dev/null +++ b/public/tier1/netadr.h @@ -0,0 +1,73 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// netadr.h +#ifndef NETADR_H +#define NETADR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#undef SetPort + +typedef enum +{ + NA_NULL = 0, + NA_LOOPBACK, + NA_BROADCAST, + NA_IP, +} netadrtype_t; + +typedef struct netadr_s +{ +public: + netadr_s() { SetIP( 0 ); SetPort( 0 ); SetType( NA_IP ); } + netadr_s( uint unIP, uint16 usPort ) { SetIP( unIP ); SetPort( usPort ); SetType( NA_IP ); } + netadr_s( const char *pch ) { SetFromString( pch ); } + void Clear(); // invalids Address + + void SetType( netadrtype_t type ); + void SetPort( unsigned short port ); + bool SetFromSockadr(const struct sockaddr *s); + void SetIP(uint8 b1, uint8 b2, uint8 b3, uint8 b4); + void SetIP(uint unIP); // Sets IP. unIP is in host order (little-endian) + void SetIPAndPort( uint unIP, unsigned short usPort ) { SetIP( unIP ); SetPort( usPort ); } + void SetFromString(const char *pch, bool bUseDNS = false ); // if bUseDNS is true then do a DNS lookup if needed + + bool CompareAdr (const netadr_s &a, bool onlyBase = false) const; + bool CompareClassBAdr (const netadr_s &a) const; + bool CompareClassCAdr (const netadr_s &a) const; + + netadrtype_t GetType() const; + unsigned short GetPort() const; + const char* ToString( bool onlyBase = false ) const; // returns xxx.xxx.xxx.xxx:ppppp + void ToSockadr(struct sockaddr *s) const; + unsigned int GetIP() const; + + bool IsLocalhost() const; // true, if this is the localhost IP + bool IsLoopback() const; // true if engine loopback buffers are used + bool IsReservedAdr() const; // true, if this is a private LAN IP + bool IsValid() const; // ip & port != 0 + bool IsBaseAdrValid() const; // ip != 0 + + void SetFromSocket( int hSocket ); + + // These function names are decorated because the Xbox360 defines macros for ntohl and htonl + unsigned long addr_ntohl() const; + unsigned long addr_htonl() const; + bool operator==(const netadr_s &netadr) const {return ( CompareAdr( netadr ) );} + bool operator<(const netadr_s &netadr) const; + +public: // members are public to avoid to much changes + + netadrtype_t type; + unsigned char ip[4]; + unsigned short port; +} netadr_t; + +#endif // NETADR_H diff --git a/public/tier1/processor_detect.h b/public/tier1/processor_detect.h new file mode 100644 index 0000000..7d12045 --- /dev/null +++ b/public/tier1/processor_detect.h @@ -0,0 +1,12 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: functions to expose CPU capabilities +// +// $NoKeywords: $ +//=============================================================================// + +bool CheckMMXTechnology(void); +bool CheckSSETechnology(void); +bool CheckSSE2Technology(void); +bool Check3DNowTechnology(void); + diff --git a/public/tier1/rangecheckedvar.h b/public/tier1/rangecheckedvar.h new file mode 100644 index 0000000..41c28de --- /dev/null +++ b/public/tier1/rangecheckedvar.h @@ -0,0 +1,113 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef RANGECHECKEDVAR_H +#define RANGECHECKEDVAR_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "mathlib/vector.h" +#include + + +// Use this to disable range checks within a scope. +class CDisableRangeChecks +{ +public: + CDisableRangeChecks(); + ~CDisableRangeChecks(); +}; + + +template< class T > +inline void RangeCheck( const T &value, int minValue, int maxValue ) +{ +#ifdef _DEBUG + extern bool g_bDoRangeChecks; + if ( ThreadInMainThread() && g_bDoRangeChecks ) + { + // Ignore the min/max stuff for now.. just make sure it's not a NAN. + Assert( IsFinite( value ) ); + } +#endif +} + +inline void RangeCheck( const Vector &value, int minValue, int maxValue ) +{ +#ifdef _DEBUG + RangeCheck( value.x, minValue, maxValue ); + RangeCheck( value.y, minValue, maxValue ); + RangeCheck( value.z, minValue, maxValue ); +#endif +} + + +template< class T, int minValue, int maxValue, int startValue > +class CRangeCheckedVar +{ +public: + + inline CRangeCheckedVar() + { + m_Val = startValue; + } + + inline CRangeCheckedVar( const T &value ) + { + *this = value; + } + + // Clamp the value to its limits. Interpolation code uses this after interpolating. + inline void Clamp() + { + if ( m_Val < minValue ) + m_Val = minValue; + else if ( m_Val > maxValue ) + m_Val = maxValue; + } + + inline operator const T&() const + { + return m_Val; + } + + inline CRangeCheckedVar& operator=( const T &value ) + { + RangeCheck( value, minValue, maxValue ); + m_Val = value; + return *this; + } + + inline CRangeCheckedVar& operator+=( const T &value ) + { + return (*this = m_Val + value); + } + + inline CRangeCheckedVar& operator-=( const T &value ) + { + return (*this = m_Val - value); + } + + inline CRangeCheckedVar& operator*=( const T &value ) + { + return (*this = m_Val * value); + } + + inline CRangeCheckedVar& operator/=( const T &value ) + { + return (*this = m_Val / value); + } + +private: + + T m_Val; +}; + +#endif // RANGECHECKEDVAR_H diff --git a/public/tier1/refcount.h b/public/tier1/refcount.h new file mode 100644 index 0000000..5ccb0e8 --- /dev/null +++ b/public/tier1/refcount.h @@ -0,0 +1,385 @@ +//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: Tools for correctly implementing & handling reference counted +// objects +// +//============================================================================= + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include "tier0/threadtools.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Implement a standard reference counted interface. Use of this +// is optional insofar as all the concrete tools only require +// at compile time that the function signatures match. +//----------------------------------------------------------------------------- + +class IRefCounted +{ +public: + virtual int AddRef() = 0; + virtual int Release() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Release a pointer and mark it NULL +//----------------------------------------------------------------------------- + +template +inline int SafeRelease( REFCOUNTED_ITEM_PTR &pRef ) +{ + // Use funny syntax so that this works on "auto pointers" + REFCOUNTED_ITEM_PTR *ppRef = &pRef; + if ( *ppRef ) + { + int result = (*ppRef)->Release(); + *ppRef = NULL; + return result; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Maintain a reference across a scope +//----------------------------------------------------------------------------- + +template +class CAutoRef +{ +public: + CAutoRef( T *pRef ) + : m_pRef( pRef ) + { + if ( m_pRef ) + m_pRef->AddRef(); + } + + ~CAutoRef() + { + if (m_pRef) + m_pRef->Release(); + } + +private: + T *m_pRef; +}; + +//----------------------------------------------------------------------------- +// Purpose: Do a an inline AddRef then return the pointer, useful when +// returning an object from a function +//----------------------------------------------------------------------------- + +#define RetAddRef( p ) ( (p)->AddRef(), (p) ) +#define InlineAddRef( p ) ( (p)->AddRef(), (p) ) + + +//----------------------------------------------------------------------------- +// Purpose: A class to both hold a pointer to an object and its reference. +// Base exists to support other cleanup models +//----------------------------------------------------------------------------- + +template +class CBaseAutoPtr +{ +public: + CBaseAutoPtr() : m_pObject(0) {} + CBaseAutoPtr(T *pFrom) : m_pObject(pFrom) {} + + operator const void *() const { return m_pObject; } + operator void *() { return m_pObject; } + + operator const T *() const { return m_pObject; } + operator const T *() { return m_pObject; } + operator T *() { return m_pObject; } + + int operator=( int i ) { AssertMsg( i == 0, "Only NULL allowed on integer assign" ); m_pObject = 0; return 0; } + T * operator=( T *p ) { m_pObject = p; return p; } + + bool operator !() const { return ( !m_pObject ); } + bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (m_pObject != NULL); } + bool operator==( const void *p ) const { return ( m_pObject == p ); } + bool operator!=( const void *p ) const { return ( m_pObject != p ); } + bool operator==( T *p ) const { return operator==( (void *)p ); } + bool operator!=( T *p ) const { return operator!=( (void *)p ); } + bool operator==( const CBaseAutoPtr &p ) const { return operator==( (const void *)p ); } + bool operator!=( const CBaseAutoPtr &p ) const { return operator!=( (const void *)p ); } + + T * operator->() { return m_pObject; } + T & operator *() { return *m_pObject; } + T ** operator &() { return &m_pObject; } + + const T * operator->() const { return m_pObject; } + const T & operator *() const { return *m_pObject; } + T * const * operator &() const { return &m_pObject; } + +protected: + CBaseAutoPtr( const CBaseAutoPtr &from ) : m_pObject( from.m_pObject ) {} + void operator=( const CBaseAutoPtr &from ) { m_pObject = from.m_pObject; } + + T *m_pObject; +}; + +//--------------------------------------------------------- + +template +class CRefPtr : public CBaseAutoPtr +{ + typedef CBaseAutoPtr BaseClass; +public: + CRefPtr() {} + CRefPtr( T *pInit ) : BaseClass( pInit ) {} + CRefPtr( const CRefPtr &from ) : BaseClass( from ) {} + ~CRefPtr() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->Release(); } + + void operator=( const CRefPtr &from ) { BaseClass::operator=( from ); } + + int operator=( int i ) { return BaseClass::operator=( i ); } + T *operator=( T *p ) { return BaseClass::operator=( p ); } + + operator bool() const { return !BaseClass::operator!(); } + operator bool() { return !BaseClass::operator!(); } + + void SafeRelease() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->Release(); BaseClass::m_pObject = 0; } + void AssignAddRef( T *pFrom ) { SafeRelease(); if (pFrom) pFrom->AddRef(); BaseClass::m_pObject = pFrom; } + void AddRefAssignTo( T *&pTo ) { ::SafeRelease( pTo ); if ( BaseClass::m_pObject ) BaseClass::m_pObject->AddRef(); pTo = BaseClass::m_pObject; } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Traits classes defining reference count threading model +//----------------------------------------------------------------------------- + +class CRefMT +{ +public: + static int Increment( int *p) { return ThreadInterlockedIncrement( (int32 *)p ); } + static int Decrement( int *p) { return ThreadInterlockedDecrement( (int32 *)p ); } +}; + +class CRefST +{ +public: + static int Increment( int *p) { return ++(*p); } + static int Decrement( int *p) { return --(*p); } +}; + +//----------------------------------------------------------------------------- +// Purpose: Actual reference counting implementation. Pulled out to reduce +// code bloat. +//----------------------------------------------------------------------------- + +template +class NO_VTABLE CRefCountServiceBase +{ +protected: + CRefCountServiceBase() + : m_iRefs( 1 ) + { + } + + virtual ~CRefCountServiceBase() + { + } + + virtual bool OnFinalRelease() + { + return true; + } + + int GetRefCount() const + { + return m_iRefs; + } + + int DoAddRef() + { + return CRefThreading::Increment( &m_iRefs ); + } + + int DoRelease() + { + int result = CRefThreading::Decrement( &m_iRefs ); + if ( result ) + return result; + if ( OnFinalRelease() && bSelfDelete ) + delete this; + return 0; + } + +private: + int m_iRefs; +}; + +class CRefCountServiceNull +{ +protected: + static int DoAddRef() { return 1; } + static int DoRelease() { return 1; } +}; + +template +class NO_VTABLE CRefCountServiceDestruct +{ +protected: + CRefCountServiceDestruct() + : m_iRefs( 1 ) + { + } + + virtual ~CRefCountServiceDestruct() + { + } + + int GetRefCount() const + { + return m_iRefs; + } + + int DoAddRef() + { + return CRefThreading::Increment( &m_iRefs ); + } + + int DoRelease() + { + int result = CRefThreading::Decrement( &m_iRefs ); + if ( result ) + return result; + this->~CRefCountServiceDestruct(); + return 0; + } + +private: + int m_iRefs; +}; + + +typedef CRefCountServiceBase CRefCountServiceST; +typedef CRefCountServiceBase CRefCountServiceNoDeleteST; + +typedef CRefCountServiceBase CRefCountServiceMT; +typedef CRefCountServiceBase CRefCountServiceNoDeleteMT; + +// Default to threadsafe +typedef CRefCountServiceNoDeleteMT CRefCountServiceNoDelete; +typedef CRefCountServiceMT CRefCountService; + +//----------------------------------------------------------------------------- +// Purpose: Base classes to implement reference counting +//----------------------------------------------------------------------------- + +template < class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted : public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted1 : public BASE1, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted1() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted2 : public BASE1, public BASE2, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted2() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted3 : public BASE1, public BASE2, public BASE3, + public REFCOUNT_SERVICE +{ + virtual ~CRefCounted3() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class BASE4, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted4 : public BASE1, public BASE2, public BASE3, public BASE4, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted4() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class BASE4, class BASE5, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted5 : public BASE1, public BASE2, public BASE3, public BASE4, public BASE5, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted5() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//----------------------------------------------------------------------------- +// Purpose: Class to throw around a reference counted item to debug +// referencing problems +//----------------------------------------------------------------------------- + +template +class CRefDebug : public BASE_REFCOUNTED +{ +public: +#ifdef _DEBUG + CRefDebug() + { + AssertMsg( this->GetRefCount() == 1, "Expected initial ref count of 1" ); + DevMsg( "%s:create 0x%x\n", ( pszName ) ? pszName : "", this ); + } + + virtual ~CRefDebug() + { + AssertDevMsg( this->GetRefCount() == FINAL_REFS, "Object still referenced on destroy?" ); + DevMsg( "%s:destroy 0x%x\n", ( pszName ) ? pszName : "", this ); + } + + int AddRef() + { + DevMsg( "%s:(0x%x)->AddRef() --> %d\n", ( pszName ) ? pszName : "", this, this->GetRefCount() + 1 ); + return BASE_REFCOUNTED::AddRef(); + } + + int Release() + { + DevMsg( "%s:(0x%x)->Release() --> %d\n", ( pszName ) ? pszName : "", this, this->GetRefCount() - 1 ); + Assert( this->GetRefCount() > 0 ); + return BASE_REFCOUNTED::Release(); + } +#endif +}; + +//----------------------------------------------------------------------------- + +#endif // REFCOUNT_H diff --git a/public/tier1/smartptr.h b/public/tier1/smartptr.h new file mode 100644 index 0000000..ec9f2e6 --- /dev/null +++ b/public/tier1/smartptr.h @@ -0,0 +1,279 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SMARTPTR_H +#define SMARTPTR_H +#ifdef _WIN32 +#pragma once +#endif + + +class CRefCountAccessor +{ +public: + template< class T > + static void AddRef( T *pObj ) + { + pObj->AddRef(); + } + + template< class T > + static void Release( T *pObj ) + { + pObj->Release(); + } +}; + +// This can be used if your objects use AddReference/ReleaseReference function names. +class CRefCountAccessorLongName +{ +public: + template< class T > + static void AddRef( T *pObj ) + { + pObj->AddReference(); + } + + template< class T > + static void Release( T *pObj ) + { + pObj->ReleaseReference(); + } +}; + + +// +// CPlainAutoPtr +// is a smart wrapper for a pointer on the stack that performs "delete" upon destruction. +// +// No reference counting is performed, copying is prohibited "s_p2.Attach( s_p1.Detach() )" should be used +// for readability and ease of maintenance. +// +// Auto pointer supports an "arrow" operator for invoking methods on the pointee and a "dereference" operator +// for getting a pointee reference. +// +// No automatic casting to bool/ptrtype is performed to avoid bugs and problems (read on "safe bool idiom" +// if casting to bool or pointer happens to be useful). +// +// Test for validity with "IsValid", get the pointer with "Get". +// +template < typename T > +class CPlainAutoPtr +{ +public: + explicit CPlainAutoPtr( T *p = NULL ) : m_p( p ) {} + ~CPlainAutoPtr( void ) { Delete(); } + +public: + void Delete( void ) { delete Detach(); } + +private: // Disallow copying, use Detach() instead to avoid ambiguity + CPlainAutoPtr( CPlainAutoPtr const &x ); + CPlainAutoPtr & operator = ( CPlainAutoPtr const &x ); + +public: + void Attach( T *p ) { m_p = p; } + T * Detach( void ) { T * p( m_p ); m_p = NULL; return p; } + +public: + bool IsValid( void ) const { return m_p != NULL; } + T * Get( void ) const { return m_p; } + T * operator -> ( void ) const { return Get(); } + T & operator * ( void ) const { return *Get(); } + +private: + T * m_p; +}; + +// +// CArrayAutoPtr +// is a smart wrapper for an array pointer on the stack that performs "delete []" upon destruction. +// +// No reference counting is performed, copying is prohibited "s_p2.Attach( s_p1.Detach() )" should be used +// for readability and ease of maintenance. +// +// Auto pointer supports an "indexing" operator for accessing array elements. +// +// No automatic casting to bool/ptrtype is performed to avoid bugs and problems (read on "safe bool idiom" +// if casting to bool or pointer happens to be useful). +// +// Test for validity with "IsValid", get the array pointer with "Get". +// +template < typename T > +class CArrayAutoPtr : public CPlainAutoPtr < T > // Warning: no polymorphic destructor (delete on base class will be a mistake) +{ +public: + explicit CArrayAutoPtr( T *p = NULL ) { Attach( p ); } + ~CArrayAutoPtr( void ) { Delete(); } + +public: + void Delete( void ) { delete [] this->Detach(); } + +public: + T & operator [] ( int k ) const { return this->Get()[ k ]; } +}; + + +// Smart pointers can be used to automatically free an object when nobody points +// at it anymore. Things contained in smart pointers must implement AddRef and Release +// functions. If those functions are private, then the class must make +// CRefCountAccessor a friend. +template +class CSmartPtr +{ +public: + CSmartPtr(); + CSmartPtr( T *pObj ); + CSmartPtr( const CSmartPtr &other ); + ~CSmartPtr(); + + T* operator=( T *pObj ); + void operator=( const CSmartPtr &other ); + const T* operator->() const; + T* operator->(); + bool operator!() const; + bool operator==( const T *pOther ) const; + bool IsValid() const; // Tells if the pointer is valid. + T* GetObject() const; // Get temporary object pointer, don't store it for later reuse! + void MarkDeleted(); + +private: + T *m_pObj; +}; + + +template< class T, class RefCountAccessor > +inline CSmartPtr::CSmartPtr() +{ + m_pObj = NULL; +} + +template< class T, class RefCountAccessor > +inline CSmartPtr::CSmartPtr( T *pObj ) +{ + m_pObj = NULL; + *this = pObj; +} + +template< class T, class RefCountAccessor > +inline CSmartPtr::CSmartPtr( const CSmartPtr &other ) +{ + m_pObj = NULL; + *this = other; +} + +template< class T, class RefCountAccessor > +inline CSmartPtr::~CSmartPtr() +{ + if ( m_pObj ) + { + RefCountAccessor::Release( m_pObj ); + } +} + +template< class T, class RefCountAccessor > +inline T* CSmartPtr::operator=( T *pObj ) +{ + if ( pObj == m_pObj ) + return pObj; + + if ( pObj ) + { + RefCountAccessor::AddRef( pObj ); + } + if ( m_pObj ) + { + RefCountAccessor::Release( m_pObj ); + } + m_pObj = pObj; + return pObj; +} + +template< class T, class RefCountAccessor > +inline void CSmartPtr::MarkDeleted() +{ + m_pObj = NULL; +} + +template< class T, class RefCountAccessor > +inline void CSmartPtr::operator=( const CSmartPtr &other ) +{ + *this = other.m_pObj; +} + +template< class T, class RefCountAccessor > +inline const T* CSmartPtr::operator->() const +{ + return m_pObj; +} + +template< class T, class RefCountAccessor > +inline T* CSmartPtr::operator->() +{ + return m_pObj; +} + +template< class T, class RefCountAccessor > +inline bool CSmartPtr::operator!() const +{ + return !m_pObj; +} + +template< class T, class RefCountAccessor > +inline bool CSmartPtr::operator==( const T *pOther ) const +{ + return m_pObj == pOther; +} + +template< class T, class RefCountAccessor > +inline bool CSmartPtr::IsValid() const +{ + return m_pObj != NULL; +} + +template< class T, class RefCountAccessor > +inline T* CSmartPtr::GetObject() const +{ + return m_pObj; +} + + +// +// CAutoPushPop +// allows you to set value of a variable upon construction and destruction. +// Constructors: +// CAutoPushPop x( myvar ) +// saves the value and restores upon destruction. +// CAutoPushPop x( myvar, newvalue ) +// saves the value, assigns new value upon construction, restores saved value upon destruction. +// CAutoPushPop x( myvar, newvalue, restorevalue ) +// assigns new value upon construction, assignes restorevalue upon destruction. +// +template < typename T > +class CAutoPushPop +{ +public: + explicit CAutoPushPop( T& var ) : m_rVar( var ), m_valPop( var ) {} + CAutoPushPop( T& var, T const &valPush ) : m_rVar( var ), m_valPop( var ) { m_rVar = valPush; } + CAutoPushPop( T& var, T const &valPush, T const &valPop ) : m_rVar( var ), m_valPop( var ) { m_rVar = valPush; } + + ~CAutoPushPop() { m_rVar = m_valPop; } + +private: // forbid copying + CAutoPushPop( CAutoPushPop const &x ); + CAutoPushPop & operator = ( CAutoPushPop const &x ); + +public: + T & Get() { return m_rVar; } + +private: + T &m_rVar; + T m_valPop; +}; + + +#endif // SMARTPTR_H diff --git a/public/tier1/stringpool.h b/public/tier1/stringpool.h new file mode 100644 index 0000000..adad8cf --- /dev/null +++ b/public/tier1/stringpool.h @@ -0,0 +1,102 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STRINGPOOL_H +#define STRINGPOOL_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "utlrbtree.h" +#include "utlvector.h" +#include "utlbuffer.h" + +//----------------------------------------------------------------------------- +// Purpose: Allocates memory for strings, checking for duplicates first, +// reusing exising strings if duplicate found. +//----------------------------------------------------------------------------- + +enum StringPoolCase_t +{ + StringPoolCaseInsensitive, + StringPoolCaseSensitive +}; + +class CStringPool +{ +public: + CStringPool( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive ); + ~CStringPool(); + + unsigned int Count() const; + + const char * Allocate( const char *pszValue ); + void FreeAll(); + + // searches for a string already in the pool + const char * Find( const char *pszValue ); + +protected: + typedef CUtlRBTree CStrSet; + + CStrSet m_Strings; +}; + +//----------------------------------------------------------------------------- +// Purpose: A reference counted string pool. +// +// Elements are stored more efficiently than in the conventional string pool, +// quicker to look up, and storage is tracked via reference counts. +// +// At some point this should replace CStringPool +//----------------------------------------------------------------------------- +class CCountedStringPool +{ +public: // HACK, hash_item_t structure should not be public. + + struct hash_item_t + { + char* pString; + unsigned short nNextElement; + unsigned char nReferenceCount; + unsigned char pad; + }; + + enum + { + INVALID_ELEMENT = 0, + MAX_REFERENCE = 0xFF, + HASH_TABLE_SIZE = 1024 + }; + + CUtlVector m_HashTable; // Points to each element + CUtlVector m_Elements; + unsigned short m_FreeListStart; + StringPoolCase_t m_caseSensitivity; + +public: + CCountedStringPool( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive ); + virtual ~CCountedStringPool(); + + void FreeAll(); + + char *FindString( const char* pIntrinsic ); + char *ReferenceString( const char* pIntrinsic ); + void DereferenceString( const char* pIntrinsic ); + + // These are only reliable if there are less than 64k strings in your string pool + unsigned short FindStringHandle( const char* pIntrinsic ); + unsigned short ReferenceStringHandle( const char* pIntrinsic ); + char *HandleToString( unsigned short handle ); + void SpewStrings(); + unsigned Hash( const char *pszKey ); + + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer );}; + +#endif // STRINGPOOL_H diff --git a/public/tier1/strtools.h b/public/tier1/strtools.h new file mode 100644 index 0000000..434cb5d --- /dev/null +++ b/public/tier1/strtools.h @@ -0,0 +1,565 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef TIER1_STRTOOLS_H +#define TIER1_STRTOOLS_H + +#include "tier0/basetypes.h" + +#ifdef _WIN32 +#pragma once +#elif POSIX +#include +#include +#include +#endif + +#include +#include + + +// 3d memcpy. Copy (up-to) 3 dimensional data with arbitrary source and destination +// strides. Optimizes to just a single memcpy when possible. For 2d data, set numslices to 1. +void CopyMemory3D( void *pDestAdr, void const *pSrcAdr, + int nNumCols, int nNumRows, int nNumSlices, // dimensions of copy + int nSrcBytesPerRow, int nSrcBytesPerSlice, // strides for source. + int nDestBytesPerRow, int nDestBytesPerSlice // strides for dest + ); + + + + +template< class T, class I > class CUtlMemory; +template< class T, class A > class CUtlVector; + + +//----------------------------------------------------------------------------- +// Portable versions of standard string functions +//----------------------------------------------------------------------------- +void _V_memset ( void *dest, int fill, int count ); +void _V_memcpy ( void *dest, const void *src, int count ); +void _V_memmove ( void *dest, const void *src, int count ); +int _V_memcmp ( const void *m1, const void *m2, int count ); +int _V_strlen ( const char *str ); +void _V_strcpy ( char *dest, const char *src ); +char* _V_strrchr ( const char *s, char c ); +int _V_strcmp ( const char *s1, const char *s2 ); +int _V_wcscmp ( const wchar_t *s1, const wchar_t *s2 ); +int _V_stricmp ( const char *s1, const char *s2 ); +char* _V_strstr ( const char *s1, const char *search ); +char* _V_strupr ( char *start ); +char* _V_strlower ( char *start ); +int _V_wcslen ( const wchar_t *pwch ); + +#ifdef POSIX +inline char *strupr( char *start ) +{ + char *str = start; + while( str && *str ) + { + *str = (char)toupper(*str); + str++; + } + return start; +} + +inline char *strlwr( char *start ) +{ + char *str = start; + while( str && *str ) + { + *str = (char)tolower(*str); + str++; + } + return start; +} + +#endif // POSIX + +// there are some users of these via tier1 templates in used in tier0. but tier0 can't depend on vstdlib which means in tier0 we always need the inlined ones +#if ( !defined( TIER0_DLL_EXPORT ) ) + +#define V_memset(dest, fill, count) _V_memset ((dest), (fill), (count)) +#define V_memcpy(dest, src, count) _V_memcpy ((dest), (src), (count)) +#define V_memmove(dest, src, count) _V_memmove ((dest), (src), (count)) +#define V_memcmp(m1, m2, count) _V_memcmp ((m1), (m2), (count)) +#define V_strlen(str) _V_strlen ((str)) +#define V_strcpy(dest, src) _V_strcpy ((dest), (src)) +#define V_strrchr(s, c) _V_strrchr ((s), (c)) +#define V_strcmp(s1, s2) _V_strcmp ((s1), (s2)) +#define V_wcscmp(s1, s2) _V_wcscmp ((s1), (s2)) +#define V_stricmp(s1, s2 ) _V_stricmp ((s1), (s2) ) +#define V_strstr(s1, search ) _V_strstr ((s1), (search) ) +#define V_strupr(start) _V_strupr ((start)) +#define V_strlower(start) _V_strlower ((start)) +#define V_wcslen(pwch) _V_wcslen ((pwch)) + +#else + +inline void V_memset (void *dest, int fill, int count) { memset( dest, fill, count ); } +inline void V_memcpy (void *dest, const void *src, int count) { memcpy( dest, src, count ); } +inline void V_memmove (void *dest, const void *src, int count) { memmove( dest, src, count ); } +inline int V_memcmp (const void *m1, const void *m2, int count){ return memcmp( m1, m2, count ); } +inline int V_strlen (const char *str) { return (int) strlen ( str ); } +inline void V_strcpy (char *dest, const char *src) { strcpy( dest, src ); } +inline int V_wcslen(const wchar_t *pwch) { return (int)wcslen(pwch); } +inline char* V_strrchr (const char *s, char c) { return (char*)strrchr( s, c ); } +inline int V_strcmp (const char *s1, const char *s2) { return strcmp( s1, s2 ); } +inline int V_wcscmp (const wchar_t *s1, const wchar_t *s2) { return wcscmp( s1, s2 ); } +inline int V_stricmp( const char *s1, const char *s2 ) { return stricmp( s1, s2 ); } +inline char* V_strstr( const char *s1, const char *search ) { return (char*)strstr( s1, search ); } +inline char* V_strupr (char *start) { return strupr( start ); } +inline char* V_strlower (char *start) { return strlwr( start ); } + +#endif + + +int V_strncmp (const char *s1, const char *s2, int count); +int V_strcasecmp (const char *s1, const char *s2); +int V_strncasecmp (const char *s1, const char *s2, int n); +int V_strnicmp (const char *s1, const char *s2, int n); +int V_atoi (const char *str); +int64 V_atoi64(const char *str); +uint64 V_atoui64(const char *str); +float V_atof (const char *str); +char* V_stristr( char* pStr, const char* pSearch ); +const char* V_stristr( const char* pStr, const char* pSearch ); +const char* V_strnistr( const char* pStr, const char* pSearch, int n ); +const char* V_strnchr( const char* pStr, char c, int n ); + +// returns string immediately following prefix, (ie str+strlen(prefix)) or NULL if prefix not found +const char *StringAfterPrefix ( const char *str, const char *prefix ); +const char *StringAfterPrefixCaseSensitive( const char *str, const char *prefix ); +inline bool StringHasPrefix ( const char *str, const char *prefix ) { return StringAfterPrefix ( str, prefix ) != NULL; } +inline bool StringHasPrefixCaseSensitive( const char *str, const char *prefix ) { return StringAfterPrefixCaseSensitive( str, prefix ) != NULL; } + + +// Normalizes a float string in place. +// (removes leading zeros, trailing zeros after the decimal point, and the decimal point itself where possible) +void V_normalizeFloatString( char* pFloat ); + +inline bool V_isspace(int c) +{ + // The standard white-space characters are the following: space, tab, carriage-return, newline, vertical tab, and form-feed. In the C locale, V_isspace() returns true only for the standard white-space characters. + //return c == ' ' || c == 9 /*horizontal tab*/ || c == '\r' || c == '\n' || c == 11 /*vertical tab*/ || c == '\f'; + // codes of whitespace symbols: 9 HT, 10 \n, 11 VT, 12 form feed, 13 \r, 32 space + + // easy to understand version, validated: + // return ((1 << (c-1)) & 0x80001F00) != 0 && ((c-1)&0xE0) == 0; + + // 5% faster on Core i7, 35% faster on Xbox360, no branches, validated: + #ifdef _X360 + return ((1 << (c-1)) & 0x80001F00 & ~(-int((c-1)&0xE0))) != 0; + #else + // this is 11% faster on Core i7 than the previous, VC2005 compiler generates a seemingly unbalanced search tree that's faster + switch(c) + { + case ' ': + case 9: + case '\r': + case '\n': + case 11: + case '\f': + return true; + default: + return false; + } + #endif +} + + +// These are versions of functions that guarantee NULL termination. +// +// maxLen is the maximum number of bytes in the destination string. +// pDest[maxLen-1] is always NULL terminated if pSrc's length is >= maxLen. +// +// This means the last parameter can usually be a sizeof() of a string. +void V_strncpy( char *pDest, const char *pSrc, int maxLen ); +int V_snprintf( char *pDest, int destLen, const char *pFormat, ... ) FMTFUNCTION( 3, 4 ); +void V_wcsncpy( wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ); +int V_snwprintf( wchar_t *pDest, int destLen, const wchar_t *pFormat, ... ); + +#define COPY_ALL_CHARACTERS -1 +char *V_strncat(char *, const char *, size_t destBufferSize, int max_chars_to_copy=COPY_ALL_CHARACTERS ); +char *V_strnlwr(char *, size_t); + + +// UNDONE: Find a non-compiler-specific way to do this +#ifdef _WIN32 +#ifndef _VA_LIST_DEFINED + +#ifdef _M_ALPHA + +struct va_list +{ + char *a0; /* pointer to first homed integer argument */ + int offset; /* byte offset of next parameter */ +}; + +#else // !_M_ALPHA + +typedef char * va_list; + +#endif // !_M_ALPHA + +#define _VA_LIST_DEFINED + +#endif // _VA_LIST_DEFINED + +#elif POSIX +#include +#endif + +#ifdef _WIN32 +#define CORRECT_PATH_SEPARATOR '\\' +#define INCORRECT_PATH_SEPARATOR '/' +#elif POSIX +#define CORRECT_PATH_SEPARATOR '/' +#define INCORRECT_PATH_SEPARATOR '\\' +#endif + +int V_vsnprintf( char *pDest, int maxLen, const char *pFormat, va_list params ); + +// Prints out a pretified memory counter string value ( e.g., 7,233.27 Mb, 1,298.003 Kb, 127 bytes ) +char *V_pretifymem( float value, int digitsafterdecimal = 2, bool usebinaryonek = false ); + +// Prints out a pretified integer with comma separators (eg, 7,233,270,000) +char *V_pretifynum( int64 value ); + +// conversion functions wchar_t <-> char, returning the number of characters converted +int V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes ); +int V_UnicodeToUTF8( const wchar_t *pUnicode, char *pUTF8, int cubDestSizeInBytes ); +int V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInBytes ); +int V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes ); + +// Functions for converting hexidecimal character strings back into binary data etc. +// +// e.g., +// int output; +// V_hextobinary( "ffffffff", 8, &output, sizeof( output ) ); +// would make output == 0xfffffff or -1 +// Similarly, +// char buffer[ 9 ]; +// V_binarytohex( &output, sizeof( output ), buffer, sizeof( buffer ) ); +// would put "ffffffff" into buffer (note null terminator!!!) +void V_hextobinary( char const *in, int numchars, byte *out, int maxoutputbytes ); +void V_binarytohex( const byte *in, int inputbytes, char *out, int outsize ); + +// Tools for working with filenames +// Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator) +void V_FileBase( const char *in, char *out,int maxlen ); +// Remove the final characters of ppath if it's '\' or '/'. +void V_StripTrailingSlash( char *ppath ); +// Remove any extension from in and return resulting string in out +void V_StripExtension( const char *in, char *out, int outLen ); +// Make path end with extension if it doesn't already have an extension +void V_DefaultExtension( char *path, const char *extension, int pathStringLength ); +// Strips any current extension from path and ensures that extension is the new extension. +// NOTE: extension string MUST include the . character +void V_SetExtension( char *path, const char *extension, int pathStringLength ); +// Removes any filename from path ( strips back to previous / or \ character ) +void V_StripFilename( char *path ); +// Remove the final directory from the path +bool V_StripLastDir( char *dirName, int maxlen ); +// Returns a pointer to the unqualified file name (no path) of a file name +const char * V_UnqualifiedFileName( const char * in ); +// Given a path and a filename, composes "path\filename", inserting the (OS correct) separator if necessary +void V_ComposeFileName( const char *path, const char *filename, char *dest, int destSize ); + +// Copy out the path except for the stuff after the final pathseparator +bool V_ExtractFilePath( const char *path, char *dest, int destSize ); +// Copy out the file extension into dest +void V_ExtractFileExtension( const char *path, char *dest, int destSize ); + +const char *V_GetFileExtension( const char * path ); + +// This removes "./" and "../" from the pathname. pFilename should be a full pathname. +// Returns false if it tries to ".." past the root directory in the drive (in which case +// it is an invalid path). +bool V_RemoveDotSlashes( char *pFilename, char separator = CORRECT_PATH_SEPARATOR ); + +// If pPath is a relative path, this function makes it into an absolute path +// using the current working directory as the base, or pStartingDir if it's non-NULL. +// Returns false if it runs out of room in the string, or if pPath tries to ".." past the root directory. +void V_MakeAbsolutePath( char *pOut, int outLen, const char *pPath, const char *pStartingDir = NULL ); + +// Creates a relative path given two full paths +// The first is the full path of the file to make a relative path for. +// The second is the full path of the directory to make the first file relative to +// Returns false if they can't be made relative (on separate drives, for example) +bool V_MakeRelativePath( const char *pFullPath, const char *pDirectory, char *pRelativePath, int nBufLen ); + +// Fixes up a file name, removing dot slashes, fixing slashes, converting to lowercase, etc. +void V_FixupPathName( char *pOut, size_t nOutLen, const char *pPath ); + +// Adds a path separator to the end of the string if there isn't one already. Returns false if it would run out of space. +void V_AppendSlash( char *pStr, int strSize ); + +// Returns true if the path is an absolute path. +bool V_IsAbsolutePath( const char *pPath ); + +// Scans pIn and replaces all occurences of pMatch with pReplaceWith. +// Writes the result to pOut. +// Returns true if it completed successfully. +// If it would overflow pOut, it fills as much as it can and returns false. +bool V_StrSubst( const char *pIn, const char *pMatch, const char *pReplaceWith, + char *pOut, int outLen, bool bCaseSensitive=false ); + + +// Split the specified string on the specified separator. +// Returns a list of strings separated by pSeparator. +// You are responsible for freeing the contents of outStrings (call outStrings.PurgeAndDeleteElements). +void V_SplitString( const char *pString, const char *pSeparator, CUtlVector > &outStrings ); + +// Just like V_SplitString, but it can use multiple possible separators. +void V_SplitString2( const char *pString, const char **pSeparators, int nSeparators, CUtlVector > &outStrings ); + +// Returns false if the buffer is not large enough to hold the working directory name. +bool V_GetCurrentDirectory( char *pOut, int maxLen ); + +// Set the working directory thus. +bool V_SetCurrentDirectory( const char *pDirName ); + + +// This function takes a slice out of pStr and stores it in pOut. +// It follows the Python slice convention: +// Negative numbers wrap around the string (-1 references the last character). +// Large numbers are clamped to the end of the string. +void V_StrSlice( const char *pStr, int firstChar, int lastCharNonInclusive, char *pOut, int outSize ); + +// Chop off the left nChars of a string. +void V_StrLeft( const char *pStr, int nChars, char *pOut, int outSize ); + +// Chop off the right nChars of a string. +void V_StrRight( const char *pStr, int nChars, char *pOut, int outSize ); + +// change "special" characters to have their c-style backslash sequence. like \n, \r, \t, ", etc. +// returns a pointer to a newly allocated string, which you must delete[] when finished with. +char *V_AddBackSlashesToSpecialChars( char const *pSrc ); + +// Force slashes of either type to be = separator character +void V_FixSlashes( char *pname, char separator = CORRECT_PATH_SEPARATOR ); + +// This function fixes cases of filenames like materials\\blah.vmt or somepath\otherpath\\ and removes the extra double slash. +void V_FixDoubleSlashes( char *pStr ); + +// Convert multibyte to wchar + back +// Specify -1 for nInSize for null-terminated string +void V_strtowcs( const char *pString, int nInSize, wchar_t *pWString, int nOutSize ); +void V_wcstostr( const wchar_t *pWString, int nInSize, char *pString, int nOutSize ); + +// buffer-safe strcat +inline void V_strcat( char *dest, const char *src, int cchDest ) +{ + V_strncat( dest, src, cchDest, COPY_ALL_CHARACTERS ); +} + +// Convert from a string to an array of integers. +void V_StringToIntArray( int *pVector, int count, const char *pString ); + +// Convert from a string to a 4 byte color value. +void V_StringToColor32( color32 *color, const char *pString ); + +// Convert \r\n (Windows linefeeds) to \n (Unix linefeeds). +void V_TranslateLineFeedsToUnix( char *pStr ); + +//----------------------------------------------------------------------------- +// generic unique name helper functions +//----------------------------------------------------------------------------- + +// returns -1 if no match, nDefault if pName==prefix, and N if pName==prefix+N +inline int V_IndexAfterPrefix( const char *pName, const char *prefix, int nDefault = 0 ) +{ + if ( !pName || !prefix ) + return -1; + + const char *pIndexStr = StringAfterPrefix( pName, prefix ); + if ( !pIndexStr ) + return -1; + + if ( !*pIndexStr ) + return nDefault; + + return atoi( pIndexStr ); +} + +// returns startindex if none found, 2 if "prefix" found, and n+1 if "prefixn" found +template < class NameArray > +int V_GenerateUniqueNameIndex( const char *prefix, const NameArray &nameArray, int startindex = 0 ) +{ + if ( !prefix ) + return 0; + + int freeindex = startindex; + + int nNames = nameArray.Count(); + for ( int i = 0; i < nNames; ++i ) + { + int index = V_IndexAfterPrefix( nameArray[ i ], prefix, 1 ); // returns -1 if no match, 0 for exact match, N for + if ( index >= freeindex ) + { + // TODO - check that there isn't more junk after the index in pElementName + freeindex = index + 1; + } + } + + return freeindex; +} + +template < class NameArray > +bool V_GenerateUniqueName( char *name, int memsize, const char *prefix, const NameArray &nameArray ) +{ + if ( name == NULL || memsize == 0 ) + return false; + + if ( prefix == NULL ) + { + name[ 0 ] = '\0'; + return false; + } + + int prefixLength = V_strlen( prefix ); + if ( prefixLength + 1 > memsize ) + { + name[ 0 ] = '\0'; + return false; + } + + int i = V_GenerateUniqueNameIndex( prefix, nameArray ); + if ( i <= 0 ) + { + V_strncpy( name, prefix, memsize ); + return true; + } + + int newlen = prefixLength + ( int )log10( ( float )i ) + 1; + if ( newlen + 1 > memsize ) + { + V_strncpy( name, prefix, memsize ); + return false; + } + + V_snprintf( name, memsize, "%s%d", prefix, i ); + return true; +} + + +extern bool V_StringToBin( const char*pString, void *pBin, uint nBinSize ); +extern bool V_BinToString( char*pString, void *pBin, uint nBinSize ); + +template +struct BinString_t +{ + BinString_t(){} + BinString_t( const char *pStr ) + { + V_strncpy( m_string, pStr, sizeof(m_string) ); + ToBin(); + } + BinString_t( const T & that ) + { + m_bin = that; + ToString(); + } + bool ToBin() + { + return V_StringToBin( m_string, &m_bin, sizeof( m_bin ) ); + } + void ToString() + { + V_BinToString( m_string, &m_bin, sizeof( m_bin ) ); + } + T m_bin; + char m_string[sizeof(T)*2+2]; // 0-terminated string representing the binary data in hex +}; + +template +inline BinString_t MakeBinString( const T& that ) +{ + return BinString_t( that ); +} + + + +// NOTE: This is for backward compatability! +// We need to DLL-export the Q methods in vstdlib but not link to them in other projects +#if !defined( VSTDLIB_BACKWARD_COMPAT ) + +#define Q_memset V_memset +#define Q_memcpy V_memcpy +#define Q_memmove V_memmove +#define Q_memcmp V_memcmp +#define Q_strlen V_strlen +#define Q_strcpy V_strcpy +#define Q_strrchr V_strrchr +#define Q_strcmp V_strcmp +#define Q_wcscmp V_wcscmp +#define Q_stricmp V_stricmp +#define Q_strstr V_strstr +#define Q_strupr V_strupr +#define Q_strlower V_strlower +#define Q_wcslen V_wcslen +#define Q_strncmp V_strncmp +#define Q_strcasecmp V_strcasecmp +#define Q_strncasecmp V_strncasecmp +#define Q_strnicmp V_strnicmp +#define Q_atoi V_atoi +#define Q_atoi64 V_atoi64 +#define Q_atoui64 V_atoui64 +#define Q_atof V_atof +#define Q_stristr V_stristr +#define Q_strnistr V_strnistr +#define Q_strnchr V_strnchr +#define Q_normalizeFloatString V_normalizeFloatString +#define Q_strncpy V_strncpy +#define Q_wcsncpy V_wcsncpy +#define Q_snprintf V_snprintf +#define Q_snwprintf V_snwprintf +#define Q_wcsncpy V_wcsncpy +#define Q_strncat V_strncat +#define Q_strnlwr V_strnlwr +#define Q_vsnprintf V_vsnprintf +#define Q_pretifymem V_pretifymem +#define Q_pretifynum V_pretifynum +#define Q_UTF8ToUnicode V_UTF8ToUnicode +#define Q_UnicodeToUTF8 V_UnicodeToUTF8 +#define Q_hextobinary V_hextobinary +#define Q_binarytohex V_binarytohex +#define Q_FileBase V_FileBase +#define Q_StripTrailingSlash V_StripTrailingSlash +#define Q_StripExtension V_StripExtension +#define Q_DefaultExtension V_DefaultExtension +#define Q_SetExtension V_SetExtension +#define Q_StripFilename V_StripFilename +#define Q_StripLastDir V_StripLastDir +#define Q_UnqualifiedFileName V_UnqualifiedFileName +#define Q_ComposeFileName V_ComposeFileName +#define Q_ExtractFilePath V_ExtractFilePath +#define Q_ExtractFileExtension V_ExtractFileExtension +#define Q_GetFileExtension V_GetFileExtension +#define Q_RemoveDotSlashes V_RemoveDotSlashes +#define Q_MakeAbsolutePath V_MakeAbsolutePath +#define Q_AppendSlash V_AppendSlash +#define Q_IsAbsolutePath V_IsAbsolutePath +#define Q_StrSubst V_StrSubst +#define Q_SplitString V_SplitString +#define Q_SplitString2 V_SplitString2 +#define Q_StrSlice V_StrSlice +#define Q_StrLeft V_StrLeft +#define Q_StrRight V_StrRight +#define Q_FixSlashes V_FixSlashes +#define Q_strtowcs V_strtowcs +#define Q_wcstostr V_wcstostr +#define Q_strcat V_strcat +#define Q_MakeRelativePath V_MakeRelativePath +#define Q_FixupPathName V_FixupPathName + +#endif // !defined( VSTDLIB_DLL_EXPORT ) + + +#endif // TIER1_STRTOOLS_H diff --git a/public/tier1/tier1.h b/public/tier1/tier1.h new file mode 100644 index 0000000..c4d89c6 --- /dev/null +++ b/public/tier1/tier1.h @@ -0,0 +1,85 @@ +//===== Copyright � 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef TIER1_H +#define TIER1_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "appframework/iappsystem.h" +#include "tier1/convar.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Call this to connect to/disconnect from all tier 1 libraries. +// It's up to the caller to check the globals it cares about to see if ones are missing +//----------------------------------------------------------------------------- +void ConnectTier1Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount ); +void DisconnectTier1Libraries(); + + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier2 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier1AppSystem : public CTier0AppSystem< IInterface > +{ + typedef CTier0AppSystem< IInterface > BaseClass; + +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + ConnectTier1Libraries( &factory, 1 ); + return true; + } + + virtual void Disconnect() + { + DisconnectTier1Libraries(); + BaseClass::Disconnect(); + } + + virtual InitReturnVal_t Init() + { + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + if ( g_pCVar ) + { + ConVar_Register( ConVarFlag ); + } + return INIT_OK; + } + + virtual void Shutdown() + { + if ( g_pCVar ) + { + ConVar_Unregister( ); + } + BaseClass::Shutdown( ); + } + + virtual AppSystemTier_t GetTier() + { + return APP_SYSTEM_TIER1; + } +}; + + +#endif // TIER1_H + diff --git a/public/tier1/timeutils.h b/public/tier1/timeutils.h new file mode 100644 index 0000000..85fb334 --- /dev/null +++ b/public/tier1/timeutils.h @@ -0,0 +1,170 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef TIMEUTILS_H +#define TIMEUTILS_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include "tier0/dbg.h" + +enum RoundStyle_t +{ + ROUND_NEAREST, + ROUND_DOWN, + ROUND_UP, +}; + +class DmeTime_t; + +class DmeFramerate_t +{ +public: + DmeFramerate_t( float fps ); + DmeFramerate_t( int fps = 0 ); + DmeFramerate_t( const DmeFramerate_t& src ) : m_num( src.m_num ), m_den( src.m_den ) {} + + void SetFramerate( float flFrameRate ); + void SetFramerate( int fps ); + // other (uncommon) options besides 30(29.97 - ntsc video) are 24 (23.976 - ntsc film) and 60 (59.94 - ntsc progressive) + void SetFramerateNTSC( int multiplier = 30 ); + + float GetFramesPerSecond() const; + DmeTime_t GetTimePerFrame() const; + + bool operator==( DmeFramerate_t f ) const { return m_num == f.m_num && m_den == f.m_den; } + bool operator!=( DmeFramerate_t f ) const { return m_num != f.m_num && m_den != f.m_den; } + bool operator< ( DmeFramerate_t f ) const { return m_num * ( int )f.m_den < f.m_num * ( int )m_den; } + bool operator> ( DmeFramerate_t f ) const { return m_num * ( int )f.m_den > f.m_num * ( int )m_den; } + bool operator<=( DmeFramerate_t f ) const { return m_num * ( int )f.m_den <= f.m_num * ( int )m_den; } + bool operator>=( DmeFramerate_t f ) const { return m_num * ( int )f.m_den >= f.m_num * ( int )m_den; } + + DmeFramerate_t operator*( int i ) const { return DmeFramerate_t( m_num * i, m_den ); } + DmeFramerate_t operator/( int i ) const { return DmeFramerate_t( m_num, m_den * i ); } + + DmeFramerate_t operator*=( int i ) { Assert( abs( m_num * i ) <= USHRT_MAX ); m_num *= ( unsigned short )i; return *this; } + DmeFramerate_t operator/=( int i ) { Assert( abs( m_den * i ) <= USHRT_MAX ); m_den *= ( unsigned short )i; return *this; } + +private: + DmeFramerate_t( int nNumerator, int nDenominator ); + + unsigned short m_num; + unsigned short m_den; + + friend class DmeTime_t; +}; + +#define DMETIME_ZERO DmeTime_t(0) +#define DMETIME_MINDELTA DmeTime_t::MinTimeDelta() +#define DMETIME_MINTIME DmeTime_t::MinTime() +#define DMETIME_MAXTIME DmeTime_t::MaxTime() +#define DMETIME_INVALID DmeTime_t::InvalidTime() + +class DmeTime_t +{ +public: + DmeTime_t() : m_tms( INT_MIN ) {} // invalid time + explicit DmeTime_t( int tms ) : m_tms( tms ) {} + explicit DmeTime_t( float sec ) : m_tms( RoundSecondsToTMS( sec ) ) {} + explicit DmeTime_t( double sec ) : m_tms( RoundSecondsToTMS( sec ) ) {} + DmeTime_t( int frame, DmeFramerate_t framerate ); + + + // time operators + + friend bool operator==( DmeTime_t a, DmeTime_t b ) { return a.m_tms == b.m_tms; } + friend bool operator!=( DmeTime_t a, DmeTime_t b ) { return a.m_tms != b.m_tms; } + friend bool operator< ( DmeTime_t a, DmeTime_t b ) { return a.m_tms < b.m_tms; } + friend bool operator> ( DmeTime_t a, DmeTime_t b ) { return a.m_tms > b.m_tms; } + friend bool operator<=( DmeTime_t a, DmeTime_t b ) { return a.m_tms <= b.m_tms; } + friend bool operator>=( DmeTime_t a, DmeTime_t b ) { return a.m_tms >= b.m_tms; } + + friend DmeTime_t operator%( DmeTime_t a, DmeTime_t b ) { return DmeTime_t( a.m_tms % b.m_tms ); } + friend DmeTime_t operator+( DmeTime_t a, DmeTime_t b ) { return DmeTime_t( a.m_tms + b.m_tms ); } + friend DmeTime_t operator-( DmeTime_t a, DmeTime_t b ) { return DmeTime_t( a.m_tms - b.m_tms ); } + + DmeTime_t operator-() const { return DmeTime_t( -m_tms ); } + + DmeTime_t operator+=( DmeTime_t t ) { m_tms += t.m_tms; return *this; } + DmeTime_t operator-=( DmeTime_t t ) { m_tms -= t.m_tms; return *this; } + + // float operators - comment these out to find potentially incorrect uses of DmeTime_t + + friend DmeTime_t operator*( DmeTime_t t, float f ) { t *= f; return t; } + friend DmeTime_t operator*( float f, DmeTime_t t ) { t *= f; return t; } + friend DmeTime_t operator/( DmeTime_t t, float f ) { t /= f; return t; } + friend float operator/( DmeTime_t n, DmeTime_t d ) { return float( n.m_tms / double( d.m_tms ) ); } + + DmeTime_t operator*=( float f ); + DmeTime_t operator/=( float f ); + + DmeTime_t operator++() { ++m_tms; return *this; } + DmeTime_t operator--() { --m_tms; return *this; } + DmeTime_t operator++( int ) { DmeTime_t t = *this; ++m_tms; return t; } // postfix version.. + DmeTime_t operator--( int ) { DmeTime_t t = *this; --m_tms; return t; } // postfix version.. + + // limits, special values and validity + + bool IsValid() const { return m_tms != INT_MIN; } + + static DmeTime_t InvalidTime() { return DmeTime_t( INT_MIN ); } + static DmeTime_t MinTime() { return DmeTime_t( INT_MIN + 1 ); } + static DmeTime_t MaxTime() { return DmeTime_t( INT_MAX ); } + static DmeTime_t MinTimeDelta() { return DmeTime_t( 1 ); } + + + // conversions between other time representations + + int GetTenthsOfMS() const { return m_tms; } + float GetSeconds() const { return m_tms * 0.0001f; } + void SetTenthsOfMS( int tms ) { m_tms = tms; } + void SetSeconds( float sec ) { m_tms = RoundSecondsToTMS( sec ); } + + + // helper methods + + void Clamp( DmeTime_t lo, DmeTime_t hi ); + bool IsInRange( DmeTime_t lo, DmeTime_t hi ) const; + + + // helper functions + + friend float GetFractionOfTimeBetween( DmeTime_t t, DmeTime_t start, DmeTime_t end, bool bClamp = false ); + friend float GetFractionOfTime( DmeTime_t t, DmeTime_t duration, bool bClamp = false ); + friend int FrameForTime( DmeTime_t t, DmeFramerate_t framerate ); + + + // standard arithmetic methods + + friend DmeTime_t abs( DmeTime_t t ) { return t.m_tms >= 0 ? t : -t; } + + + // framerate-dependent conversions to/from frames + + int CurrentFrame( DmeFramerate_t framerate, RoundStyle_t roundStyle = ROUND_DOWN ) const; + DmeTime_t TimeAtCurrentFrame( DmeFramerate_t framerate, RoundStyle_t roundStyle = ROUND_DOWN ) const; + DmeTime_t TimeAtNextFrame( DmeFramerate_t framerate ) const; + DmeTime_t TimeAtPrevFrame( DmeFramerate_t framerate ) const; + +private: + explicit DmeTime_t( int64 tms ) : m_tms( int( tms ) ) {} + + // conversion helper methods - implementation + static int RoundSecondsToTMS( float sec ); + static int RoundSecondsToTMS( double sec ); + + int m_tms; +}; + +class CUtlBuffer; +bool Serialize( CUtlBuffer &buf, const DmeTime_t &src ); +bool Unserialize( CUtlBuffer &buf, DmeTime_t &dest ); + + +#endif // TIMEUTILS_H diff --git a/public/tier1/tokenset.h b/public/tier1/tokenset.h new file mode 100644 index 0000000..b05e346 --- /dev/null +++ b/public/tier1/tokenset.h @@ -0,0 +1,122 @@ +//========= Copyright © 2008, Valve Corporation, All rights reserved. ============// + +#ifndef __TOKENSET_H +#define __TOKENSET_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// +// This class is a handy way to match a series of values to stings and vice +// versa. It should be initalized as static like an array, for example: +// +// const tokenset_t tokens[] = { +// { "token1", 1 }, +// { "token2", 2 }, +// { "token3", 3 }, +// { NULL, -1 } +// }; +// +// Then you can call the operators on it by using: +// +// int t = tokens->GetToken( s ); +// +// If s is "token1" it returns 1, etc. Invalid string returns the last NULL +// value. GetNameByToken() returns "__UNKNOWN__" when passed a mismatched +// token. GetNameByToken() returns szMismatchResult when passed a mismatched +// token and a string to return in case of mismatch. +// +//----------------------------------------------------------------------------- + +template +struct tokenset_t +{ + const char *name; + _T token; + + _T GetToken( const char *s ) const; + _T GetTokenI( const char *s ) const; + const char *GetNameByToken( _T token ) const; + const char *GetNameByToken( _T token, const char *szMismatchResult ) const; +}; + +template +inline _T tokenset_t< _T >::GetToken( const char *s ) const +{ + const tokenset_t< _T > *c; + + for ( c = this; c->name; ++c ) + { + if ( !s ) + { + continue; // Loop to the last NULL value + } + + if ( Q_strcmp( s, c->name ) == 0 ) + { + return c->token; + } + } + + return c->token; // c points to the last NULL value +} + +template +inline _T tokenset_t< _T >::GetTokenI( const char *s ) const +{ + const tokenset_t< _T > *c; + + for ( c = this; c->name; ++c ) + { + if ( !s ) + { + continue; // Loop to the last NULL value + } + + if ( Q_stricmp( s, c->name ) == 0 ) + { + return c->token; + } + } + + return c->token; // c points to the last NULL value +} + +template +inline const char *tokenset_t< _T >::GetNameByToken( _T token ) const +{ + static const char *unknown = "__UNKNOWN__"; + + const tokenset_t< _T > *c; + + for ( c = this; c->name; ++c ) + { + if ( c->token == token ) + { + return c->name; + } + } + + return unknown; +} + +template +inline const char *tokenset_t< _T >::GetNameByToken( _T token, char const *szMismatchResult ) const +{ + const tokenset_t< _T > *c; + + for ( c = this; c->name; ++c ) + { + if ( c->token == token ) + { + return c->name; + } + } + + return szMismatchResult; +} + +#endif //__TOKENSET_H + diff --git a/public/tier1/uniqueid.h b/public/tier1/uniqueid.h new file mode 100644 index 0000000..64aae94 --- /dev/null +++ b/public/tier1/uniqueid.h @@ -0,0 +1,56 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Utilities for globally unique IDs +//=============================================================================// + +#ifndef UNIQUEID_H +#define UNIQUEID_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct UniqueId_t; +class CUtlBuffer; + + +//----------------------------------------------------------------------------- +// Defines a globally unique ID +//----------------------------------------------------------------------------- +struct UniqueId_t +{ + unsigned char m_Value[16]; +}; + + +//----------------------------------------------------------------------------- +// Methods related to unique ids +//----------------------------------------------------------------------------- +void CreateUniqueId( UniqueId_t *pDest ); +void InvalidateUniqueId( UniqueId_t *pDest ); +bool IsUniqueIdValid( const UniqueId_t &id ); +bool IsUniqueIdEqual( const UniqueId_t &id1, const UniqueId_t &id2 ); +void UniqueIdToString( const UniqueId_t &id, char *pBuf, int nMaxLen ); +bool UniqueIdFromString( UniqueId_t *pDest, const char *pBuf, int nMaxLen = 0 ); +void CopyUniqueId( const UniqueId_t &src, UniqueId_t *pDest ); +bool Serialize( CUtlBuffer &buf, const UniqueId_t &src ); +bool Unserialize( CUtlBuffer &buf, UniqueId_t &dest ); + +inline bool operator ==( const UniqueId_t& lhs, const UniqueId_t& rhs ) +{ + return !Q_memcmp( (void *)&lhs.m_Value[ 0 ], (void *)&rhs.m_Value[ 0 ], sizeof( lhs.m_Value ) ); +} + + +#endif // UNIQUEID_H + diff --git a/public/tier1/utlbidirectionalset.h b/public/tier1/utlbidirectionalset.h new file mode 100644 index 0000000..25f0e33 --- /dev/null +++ b/public/tier1/utlbidirectionalset.h @@ -0,0 +1,394 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Bi-directional set. A Bucket knows about the elements that lie +// in it, and the elements know about the buckets they lie in. +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLBIDIRECTIONALSET_H +#define UTLBIDIRECTIONALSET_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "utllinkedlist.h" + +//----------------------------------------------------------------------------- +// Templatized helper class to deal with the kinds of things that spatial +// partition code always seems to have; buckets with lists of lots of elements +// and elements that live in lots of buckets. This makes it really quick to +// add and remove elements, and to iterate over all elements in a bucket. +// +// For this to work, you must initialize the set with two functions one that +// maps from bucket to the index of the first element in that bucket, and one +// that maps from element to the index of the first bucket that element lies in. +// The set will completely manage the index, it's just expected that those +// indices will be stored outside the set. +// +// S is the storage type of the index; it is the type that you may use to +// save indices into memory. I is the local iterator type, which you should +// use in any local scope (eg, inside a for() loop.) The reason for this is +// that you may wish to use unsigned shorts inside the structs you are +// saving with a CBidirectionalSet; but 16-bit arithmetic is catastrophically +// slow on a PowerPC -- during testing we saw CBidirectionalSet:: operations +// consume as much as 8% of the frame. +// +// For this reason, on the 360, the handles have been typedef'd to native +// register types (U32) which are accepted as parameters by the functions. +// The implicit assumption is that CBucketHandle and CElementHandle can +// be safely cast to ints! You can increase to U64 without performance +// penalty if necessary; the PowerPC is a 64-bit processor. +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I = S > +class CBidirectionalSet +{ +public: + // Install methods to get at the first bucket given a element + // and vice versa... + typedef S& (*FirstElementFunc_t)(CBucketHandle); + typedef S& (*FirstBucketFunc_t)(CElementHandle); + +#ifdef _X360 + typedef uint32 CBucketHandlePram; + typedef uint32 CElementHandlePram; +#else + typedef CBucketHandle CBucketHandlePram; + typedef CElementHandle CElementHandlePram; +#endif + + // Constructor + CBidirectionalSet(); + + // Call this before using the set + void Init( FirstElementFunc_t elemFunc, FirstBucketFunc_t bucketFunc ); + + // Add an element to a particular bucket + void AddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element ); + + // Prevalidate an add to a particular bucket + // NOTE: EXPENSIVE!!! + void ValidateAddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element ); + + // Test if an element is in a particular bucket. + // NOTE: EXPENSIVE!!! + bool IsElementInBucket( CBucketHandlePram bucket, CElementHandlePram element ); + + // Remove an element from a particular bucket + void RemoveElementFromBucket( CBucketHandlePram bucket, CElementHandlePram element ); + + // Remove an element from all buckets + void RemoveElement( CElementHandlePram element ); + void RemoveBucket( CBucketHandlePram element ); + + // Used to iterate elements in a bucket; I is the iterator + I FirstElement( CBucketHandlePram bucket ) const; + I NextElement( I idx ) const; + CElementHandle Element( I idx ) const; + + // Used to iterate buckets associated with an element; I is the iterator + I FirstBucket( CElementHandlePram bucket ) const; + I NextBucket( I idx ) const; + CBucketHandle Bucket( I idx ) const; + + static S InvalidIndex(); + + // Ensure capacity + void EnsureCapacity( int count ); + + // Deallocate.... + void Purge(); + + int NumAllocated( void ) const; + +private: + struct BucketListInfo_t + { + CElementHandle m_Element; + S m_BucketListIndex; // what's the m_BucketsUsedByElement index of the entry? + }; + + struct ElementListInfo_t + { + CBucketHandle m_Bucket; + S m_ElementListIndex; // what's the m_ElementsInBucket index of the entry? + }; + + // Maintains a list of all elements in a particular bucket + CUtlLinkedList< BucketListInfo_t, S, true, I > m_ElementsInBucket; + + // Maintains a list of all buckets a particular element lives in + CUtlLinkedList< ElementListInfo_t, S, true, I > m_BucketsUsedByElement; + + FirstBucketFunc_t m_FirstBucket; + FirstElementFunc_t m_FirstElement; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +CBidirectionalSet::CBidirectionalSet( ) +{ + m_FirstBucket = NULL; + m_FirstElement = NULL; +} + + +//----------------------------------------------------------------------------- +// Call this before using the set +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::Init( FirstElementFunc_t elemFunc, FirstBucketFunc_t bucketFunc ) +{ + m_FirstBucket = bucketFunc; + m_FirstElement = elemFunc; +} + + +//----------------------------------------------------------------------------- +// Adds an element to the bucket +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::ValidateAddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element ) +{ +#ifdef _DEBUG + // Make sure that this element doesn't already exist in the list of elements in the bucket + I elementInBucket = m_FirstElement( bucket ); + while( elementInBucket != m_ElementsInBucket.InvalidIndex() ) + { + // If you hit an Assert here, fix the calling code. It's too expensive to ensure + // that each item only shows up once here. Hopefully you can do something better + // outside of here. + Assert( m_ElementsInBucket[elementInBucket].m_Element != element ); + elementInBucket = m_ElementsInBucket.Next( elementInBucket ); + } + // Make sure that this bucket doesn't already exist in the element's list of buckets. + I bucketInElement = m_FirstBucket( element ); + while( bucketInElement != m_BucketsUsedByElement.InvalidIndex() ) + { + // If you hit an Assert here, fix the calling code. It's too expensive to ensure + // that each item only shows up once here. Hopefully you can do something better + // outside of here. + Assert( m_BucketsUsedByElement[bucketInElement].m_Bucket != bucket ); + bucketInElement = m_BucketsUsedByElement.Next( bucketInElement ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Adds an element to the bucket +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::AddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element ) +{ + Assert( m_FirstBucket && m_FirstElement ); + + // Allocate new element + bucket entries + I idx = m_ElementsInBucket.Alloc(true); + I list = m_BucketsUsedByElement.Alloc( true ); + + // Store off the element data + m_ElementsInBucket[idx].m_Element = element; + m_ElementsInBucket[idx].m_BucketListIndex = list; + + // Here's the bucket data + m_BucketsUsedByElement[list].m_Bucket = bucket; + m_BucketsUsedByElement[list].m_ElementListIndex = idx; + + // Insert the element into the list of elements in the bucket + S& firstElementInBucket = m_FirstElement( bucket ); + if ( firstElementInBucket != m_ElementsInBucket.InvalidIndex() ) + m_ElementsInBucket.LinkBefore( firstElementInBucket, idx ); + firstElementInBucket = idx; + + // Insert the bucket into the element's list of buckets + S& firstBucketInElement = m_FirstBucket( element ); + if ( firstBucketInElement != m_BucketsUsedByElement.InvalidIndex() ) + m_BucketsUsedByElement.LinkBefore( firstBucketInElement, list ); + firstBucketInElement = list; +} + +//----------------------------------------------------------------------------- +// Test if an element is in a particular bucket. +// NOTE: EXPENSIVE!!! +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +bool CBidirectionalSet::IsElementInBucket( CBucketHandlePram bucket, CElementHandlePram element ) +{ + // Search through all elements in this bucket to see if element is in there. + I elementInBucket = m_FirstElement( bucket ); + while( elementInBucket != m_ElementsInBucket.InvalidIndex() ) + { + if( m_ElementsInBucket[elementInBucket].m_Element == element ) + { + return true; + } + elementInBucket = m_ElementsInBucket.Next( elementInBucket ); + } + return false; +} + + +//----------------------------------------------------------------------------- +// Remove an element from a particular bucket +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::RemoveElementFromBucket( CBucketHandlePram bucket, CElementHandlePram element ) +{ + // FIXME: Implement me! + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Removes an element from all buckets +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::RemoveElement( CElementHandlePram element ) +{ + Assert( m_FirstBucket && m_FirstElement ); + + // Iterate over the list of all buckets the element is in + I i = m_FirstBucket( element ); + while (i != m_BucketsUsedByElement.InvalidIndex()) + { + CBucketHandlePram bucket = m_BucketsUsedByElement[i].m_Bucket; + I elementListIndex = m_BucketsUsedByElement[i].m_ElementListIndex; + + // Unhook the element from the bucket's list of elements + if (elementListIndex == m_FirstElement(bucket)) + m_FirstElement(bucket) = m_ElementsInBucket.Next(elementListIndex); + m_ElementsInBucket.Free(elementListIndex); + + I prevNode = i; + i = m_BucketsUsedByElement.Next(i); + m_BucketsUsedByElement.Free(prevNode); + } + + // Mark the list as empty + m_FirstBucket( element ) = m_BucketsUsedByElement.InvalidIndex(); +} + +//----------------------------------------------------------------------------- +// Removes a bucket from all elements +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::RemoveBucket( CBucketHandlePram bucket ) +{ + // Iterate over the list of all elements in the bucket + I i = m_FirstElement( bucket ); + while (i != m_ElementsInBucket.InvalidIndex()) + { + CElementHandlePram element = m_ElementsInBucket[i].m_Element; + I bucketListIndex = m_ElementsInBucket[i].m_BucketListIndex; + + // Unhook the bucket from the element's list of buckets + if (bucketListIndex == m_FirstBucket(element)) + m_FirstBucket(element) = m_BucketsUsedByElement.Next(bucketListIndex); + m_BucketsUsedByElement.Free(bucketListIndex); + + // Remove the list element + I prevNode = i; + i = m_ElementsInBucket.Next(i); + m_ElementsInBucket.Free(prevNode); + } + + // Mark the bucket list as empty + m_FirstElement( bucket ) = m_ElementsInBucket.InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Ensure capacity +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::EnsureCapacity( int count ) +{ + m_ElementsInBucket.EnsureCapacity( count ); + m_BucketsUsedByElement.EnsureCapacity( count ); +} + + +//----------------------------------------------------------------------------- +// Deallocate.... +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +void CBidirectionalSet::Purge() +{ + m_ElementsInBucket.Purge( ); + m_BucketsUsedByElement.Purge( ); +} + + +//----------------------------------------------------------------------------- +// Number of elements allocated in each linked list (should be the same) +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +int CBidirectionalSet::NumAllocated( void ) const +{ + Assert( m_ElementsInBucket.NumAllocated() == m_BucketsUsedByElement.NumAllocated() ); + return m_ElementsInBucket.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Invalid index for iteration.. +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +inline S CBidirectionalSet::InvalidIndex() +{ + return CUtlLinkedList< CElementHandle, I >::InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Used to iterate elements in a bucket; I is the iterator +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +inline I CBidirectionalSet::FirstElement( CBucketHandlePram bucket ) const +{ + Assert( m_FirstElement ); + return m_FirstElement(bucket); +} + +template< class CBucketHandle, class CElementHandle, class S, class I > +inline I CBidirectionalSet::NextElement( I idx ) const +{ + return m_ElementsInBucket.Next(idx); +} + +template< class CBucketHandle, class CElementHandle, class S, class I > +inline CElementHandle CBidirectionalSet::Element( I idx ) const +{ + return m_ElementsInBucket[idx].m_Element; +} + +//----------------------------------------------------------------------------- +// Used to iterate buckets an element lies in; I is the iterator +//----------------------------------------------------------------------------- +template< class CBucketHandle, class CElementHandle, class S, class I > +inline I CBidirectionalSet::FirstBucket( CElementHandlePram element ) const +{ + Assert( m_FirstBucket ); + return m_FirstBucket(element); +} + +template< class CBucketHandle, class CElementHandle, class S, class I > +inline I CBidirectionalSet::NextBucket( I idx ) const +{ + return m_BucketsUsedByElement.Next(idx); +} + +template< class CBucketHandle, class CElementHandle, class S, class I > +inline CBucketHandle CBidirectionalSet::Bucket( I idx ) const +{ + return m_BucketsUsedByElement[idx].m_Bucket; +} + +#endif // UTLBIDIRECTIONALSET_H diff --git a/public/tier1/utlblockmemory.h b/public/tier1/utlblockmemory.h new file mode 100644 index 0000000..ed6d19b --- /dev/null +++ b/public/tier1/utlblockmemory.h @@ -0,0 +1,349 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A growable memory class. +//===========================================================================// + +#ifndef UTLBLOCKMEMORY_H +#define UTLBLOCKMEMORY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier0/platform.h" +#include "mathlib/mathlib.h" + +#include "tier0/memalloc.h" +#include "tier0/memdbgon.h" + +#pragma warning (disable:4100) +#pragma warning (disable:4514) + +//----------------------------------------------------------------------------- + +#ifdef UTBLOCKLMEMORY_TRACK +#define UTLBLOCKMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlBlockMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#define UTLBLOCKMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlBlockMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#else +#define UTLBLOCKMEMORY_TRACK_ALLOC() ((void)0) +#define UTLBLOCKMEMORY_TRACK_FREE() ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// The CUtlBlockMemory class: +// A growable memory class that allocates non-sequential blocks, but is indexed sequentially +//----------------------------------------------------------------------------- +template< class T, class I > +class CUtlBlockMemory +{ +public: + // constructor, destructor + CUtlBlockMemory( int nGrowSize = 0, int nInitSize = 0 ); + ~CUtlBlockMemory(); + + // Set the size by which the memory grows - round up to the next power of 2 + void Init( int nGrowSize = 0, int nInitSize = 0 ); + + // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL + T* Base() { return NULL; } + const T* Base() const { return NULL; } + + class Iterator_t + { + public: + Iterator_t( I i ) : index( i ) {} + I index; + + bool operator==( const Iterator_t it ) const { return index == it.index; } + bool operator!=( const Iterator_t it ) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); } + Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); } + I GetIndex( const Iterator_t &it ) const { return it.index; } + bool IsIdxAfter( I i, const Iterator_t &it ) const { return i > it.index; } + bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); } + Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); } + + // element access + T& operator[]( I i ); + const T& operator[]( I i ) const; + T& Element( I i ); + const T& Element( I i ) const; + + // Can we use this index? + bool IsIdxValid( I i ) const; + static I InvalidIndex() { return ( I )-1; } + + void Swap( CUtlBlockMemory< T, I > &mem ); + + // Size + int NumAllocated() const; + int Count() const { return NumAllocated(); } + + // Grows memory by max(num,growsize) rounded up to the next power of 2, and returns the allocation index/ptr + void Grow( int num = 1 ); + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements + void Purge( int numElements ); + +protected: + int Index( int major, int minor ) const { return ( major << m_nIndexShift ) | minor; } + int MajorIndex( int i ) const { return i >> m_nIndexShift; } + int MinorIndex( int i ) const { return i & m_nIndexMask; } + void ChangeSize( int nBlocks ); + int NumElementsInBlock() const { return m_nIndexMask + 1; } + + T** m_pMemory; + int m_nBlocks; + int m_nIndexMask : 27; + int m_nIndexShift : 5; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class I > +CUtlBlockMemory::CUtlBlockMemory( int nGrowSize, int nInitAllocationCount ) +: m_pMemory( 0 ), m_nBlocks( 0 ), m_nIndexMask( 0 ), m_nIndexShift( 0 ) +{ + Init( nGrowSize, nInitAllocationCount ); +} + +template< class T, class I > +CUtlBlockMemory::~CUtlBlockMemory() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Swap( CUtlBlockMemory< T, I > &mem ) +{ + V_swap( m_pMemory, mem.m_pMemory ); + V_swap( m_nBlocks, mem.m_nBlocks ); + V_swap( m_nIndexMask, mem.m_nIndexMask ); + V_swap( m_nIndexShift, mem.m_nIndexShift ); +} + + +//----------------------------------------------------------------------------- +// Set the size by which the memory grows - round up to the next power of 2 +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Init( int nGrowSize /* = 0 */, int nInitSize /* = 0 */ ) +{ + Purge(); + + if ( nGrowSize == 0) + { + // default grow size is smallest size s.t. c++ allocation overhead is ~6% of block size + nGrowSize = ( 127 + sizeof( T ) ) / sizeof( T ); + } + nGrowSize = SmallestPowerOfTwoGreaterOrEqual( nGrowSize ); + m_nIndexMask = nGrowSize - 1; + + m_nIndexShift = 0; + while ( nGrowSize > 1 ) + { + nGrowSize >>= 1; + ++m_nIndexShift; + } + Assert( m_nIndexMask + 1 == ( 1 << m_nIndexShift ) ); + + Grow( nInitSize ); +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T, class I > +inline T& CUtlBlockMemory::operator[]( I i ) +{ + Assert( IsIdxValid(i) ); + T *pBlock = m_pMemory[ MajorIndex( i ) ]; + return pBlock[ MinorIndex( i ) ]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::operator[]( I i ) const +{ + Assert( IsIdxValid(i) ); + const T *pBlock = m_pMemory[ MajorIndex( i ) ]; + return pBlock[ MinorIndex( i ) ]; +} + +template< class T, class I > +inline T& CUtlBlockMemory::Element( I i ) +{ + Assert( IsIdxValid(i) ); + T *pBlock = m_pMemory[ MajorIndex( i ) ]; + return pBlock[ MinorIndex( i ) ]; +} + +template< class T, class I > +inline const T& CUtlBlockMemory::Element( I i ) const +{ + Assert( IsIdxValid(i) ); + const T *pBlock = m_pMemory[ MajorIndex( i ) ]; + return pBlock[ MinorIndex( i ) ]; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T, class I > +inline int CUtlBlockMemory::NumAllocated() const +{ + return m_nBlocks * NumElementsInBlock(); +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T, class I > +inline bool CUtlBlockMemory::IsIdxValid( I i ) const +{ + return ( i >= 0 ) && ( MajorIndex( i ) < m_nBlocks ); +} + +template< class T, class I > +void CUtlBlockMemory::Grow( int num ) +{ + if ( num <= 0 ) + return; + + int nBlockSize = NumElementsInBlock(); + int nBlocks = ( num + nBlockSize - 1 ) / nBlockSize; + + ChangeSize( m_nBlocks + nBlocks ); +} + +template< class T, class I > +void CUtlBlockMemory::ChangeSize( int nBlocks ) +{ + UTLBLOCKMEMORY_TRACK_FREE(); // this must stay before the recalculation of m_nBlocks, since it implicitly uses the old value + + int nBlocksOld = m_nBlocks; + m_nBlocks = nBlocks; + + UTLBLOCKMEMORY_TRACK_ALLOC(); // this must stay after the recalculation of m_nBlocks, since it implicitly uses the new value + + // free old blocks if shrinking + for ( int i = m_nBlocks; i < nBlocksOld; ++i ) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free( (void*)m_pMemory[ i ] ); + } + + if ( m_pMemory ) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)realloc( m_pMemory, m_nBlocks * sizeof(T*) ); + Assert( m_pMemory ); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T**)malloc( m_nBlocks * sizeof(T*) ); + Assert( m_pMemory ); + } + + if ( !m_pMemory ) + { + Error( "CUtlBlockMemory overflow!\n" ); + } + + // allocate new blocks if growing + int nBlockSize = NumElementsInBlock(); + for ( int i = nBlocksOld; i < m_nBlocks; ++i ) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory[ i ] = (T*)malloc( nBlockSize * sizeof( T ) ); + Assert( m_pMemory[ i ] ); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, class I > +inline void CUtlBlockMemory::EnsureCapacity( int num ) +{ + Grow( num - NumAllocated() ); +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlBlockMemory::Purge() +{ + if ( !m_pMemory ) + return; + + for ( int i = 0; i < m_nBlocks; ++i ) + { + UTLBLOCKMEMORY_TRACK_FREE(); + free( (void*)m_pMemory[ i ] ); + } + m_nBlocks = 0; + + UTLBLOCKMEMORY_TRACK_FREE(); + free( (void*)m_pMemory ); + m_pMemory = 0; +} + +template< class T, class I > +void CUtlBlockMemory::Purge( int numElements ) +{ + Assert( numElements >= 0 ); + + int nAllocated = NumAllocated(); + if ( numElements > nAllocated ) + { + // Ensure this isn't a grow request in disguise. + Assert( numElements <= nAllocated ); + return; + } + + if ( numElements <= 0 ) + { + Purge(); + return; + } + + int nBlockSize = NumElementsInBlock(); + int nBlocksOld = m_nBlocks; + int nBlocks = ( numElements + nBlockSize - 1 ) / nBlockSize; + + // If the number of blocks is the same as the allocated number of blocks, we are done. + if ( nBlocks == m_nBlocks ) + return; + + ChangeSize( nBlocks ); +} + +#include "tier0/memdbgoff.h" + +#endif // UTLBLOCKMEMORY_H diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h new file mode 100644 index 0000000..30c7a0f --- /dev/null +++ b/public/tier1/utlbuffer.h @@ -0,0 +1,1359 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLBUFFER_H +#define UTLBUFFER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "unitlib/unitlib.h" // just here for tests - remove before checking in!!! + +#include "tier1/utlmemory.h" +#include "tier1/byteswap.h" +#include + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct characterset_t; + + +//----------------------------------------------------------------------------- +// Description of character conversions for string output +// Here's an example of how to use the macros to define a character conversion +// BEGIN_CHAR_CONVERSION( CStringConversion, '\\' ) +// { '\n', "n" }, +// { '\t', "t" } +// END_CHAR_CONVERSION( CStringConversion, '\\' ) +//----------------------------------------------------------------------------- +class CUtlCharConversion +{ +public: + struct ConversionArray_t + { + char m_nActualChar; + char *m_pReplacementString; + }; + + CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); + char GetEscapeChar() const; + const char *GetDelimiter() const; + int GetDelimiterLength() const; + + const char *GetConversionString( char c ) const; + int GetConversionLength( char c ) const; + int MaxConversionLength() const; + + // Finds a conversion for the passed-in string, returns length + virtual char FindConversion( const char *pString, int *pLength ); + +protected: + struct ConversionInfo_t + { + int m_nLength; + char *m_pReplacementString; + }; + + char m_nEscapeChar; + const char *m_pDelimiter; + int m_nDelimiterLength; + int m_nCount; + int m_nMaxConversionLength; + char m_pList[255]; + ConversionInfo_t m_pReplacements[255]; +}; + +#define BEGIN_CHAR_CONVERSION( _name, _delimiter, _escapeChar ) \ + static CUtlCharConversion::ConversionArray_t s_pConversionArray ## _name[] = { + +#define END_CHAR_CONVERSION( _name, _delimiter, _escapeChar ) \ + }; \ + CUtlCharConversion _name( _escapeChar, _delimiter, sizeof( s_pConversionArray ## _name ) / sizeof( CUtlCharConversion::ConversionArray_t ), s_pConversionArray ## _name ); + +#define BEGIN_CUSTOM_CHAR_CONVERSION( _className, _name, _delimiter, _escapeChar ) \ + static CUtlCharConversion::ConversionArray_t s_pConversionArray ## _name[] = { + +#define END_CUSTOM_CHAR_CONVERSION( _className, _name, _delimiter, _escapeChar ) \ + }; \ + _className _name( _escapeChar, _delimiter, sizeof( s_pConversionArray ## _name ) / sizeof( CUtlCharConversion::ConversionArray_t ), s_pConversionArray ## _name ); + +//----------------------------------------------------------------------------- +// Character conversions for C strings +//----------------------------------------------------------------------------- +CUtlCharConversion *GetCStringCharConversion(); + +//----------------------------------------------------------------------------- +// Character conversions for quoted strings, with no escape sequences +//----------------------------------------------------------------------------- +CUtlCharConversion *GetNoEscCharConversion(); + + +//----------------------------------------------------------------------------- +// Macro to set overflow functions easily +//----------------------------------------------------------------------------- +#define SetUtlBufferOverflowFuncs( _get, _put ) \ + SetOverflowFuncs( static_cast ( _get ), static_cast ( _put ) ) + + + +typedef unsigned short ushort; + +template < class A > +static const char *GetFmtStr( int nRadix = 10, bool bPrint = true ) { Assert( 0 ); return ""; } + +template <> static const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } +template <> static const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> static const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } +template <> static const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } +template <> static const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%I64d"; } +template <> static const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } +template <> static const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 + + +//----------------------------------------------------------------------------- +// Command parsing.. +//----------------------------------------------------------------------------- +class CUtlBuffer +{ +// Brian has on his todo list to revisit this as there are issues in some cases with CUtlVector using operator = instead of copy construtor in InsertMultiple, etc. +// The unsafe case is something like this: +// CUtlVector< CUtlBuffer > vecFoo; +// +// CUtlBuffer buf; +// buf.Put( xxx ); +// vecFoo.Insert( buf ); +// +// This will cause memory corruption when vecFoo is cleared +// +//private: +// // Disallow copying +// CUtlBuffer( const CUtlBuffer & );// { Assert( 0 ); } +// CUtlBuffer &operator=( const CUtlBuffer & );// { Assert( 0 ); return *this; } + +public: + enum SeekType_t + { + SEEK_HEAD = 0, + SEEK_CURRENT, + SEEK_TAIL + }; + + // flags + enum BufferFlags_t + { + TEXT_BUFFER = 0x1, // Describes how get + put work (as strings, or binary) + EXTERNAL_GROWABLE = 0x2, // This is used w/ external buffers and causes the utlbuf to switch to reallocatable memory if an overflow happens when Putting. + CONTAINS_CRLF = 0x4, // For text buffers only, does this contain \n or \n\r? + READ_ONLY = 0x8, // For external buffers; prevents null termination from happening. + AUTO_TABS_DISABLED = 0x10, // Used to disable/enable push/pop tabs + }; + + // Overflow functions when a get or put overflows + typedef bool (CUtlBuffer::*UtlBufferOverflowFunc_t)( int nSize ); + + // Constructors for growable + external buffers for serialization/unserialization + CUtlBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 ); + CUtlBuffer( const void* pBuffer, int size, int nFlags = 0 ); + // This one isn't actually defined so that we catch contructors that are trying to pass a bool in as the third param. + CUtlBuffer( const void *pBuffer, int size, bool crap ); + + unsigned char GetFlags() const; + + // NOTE: This will assert if you attempt to recast it in a way that + // is not compatible. The only valid conversion is binary-> text w/CRLF + void SetBufferType( bool bIsText, bool bContainsCRLF ); + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ); + + // Access for direct read into buffer + void * AccessForDirectRead( int nBytes ); + + // Attaches the buffer to external memory.... + void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); + bool IsExternallyAllocated() const; + void AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nFlags = 0 ); + void *Detach(); + void* DetachMemory(); + + FORCEINLINE void ActivateByteSwappingIfBigEndian( void ) + { + if ( IsX360() ) + ActivateByteSwapping( true ); + } + + + // Controls endian-ness of binary utlbufs - default matches the current platform + void ActivateByteSwapping( bool bActivate ); + void SetBigEndian( bool bigEndian ); + bool IsBigEndian( void ); + + // Resets the buffer; but doesn't free memory + void Clear(); + + // Clears out the buffer; frees memory + void Purge(); + + // Dump the buffer to stdout + void Spew( ); + + // Read stuff out. + // Binary mode: it'll just read the bits directly in, and characters will be + // read for strings until a null character is reached. + // Text mode: it'll parse the file, turning text #s into real numbers. + // GetString will read a string until a space is reached + char GetChar( ); + unsigned char GetUnsignedChar( ); + short GetShort( ); + unsigned short GetUnsignedShort( ); + int GetInt( ); + int64 GetInt64( ); + unsigned int GetIntHex( ); + unsigned int GetUnsignedInt( ); + float GetFloat( ); + double GetDouble( ); + void * GetPtr(); + void GetString( char* pString, int nMaxChars = 0 ); + void Get( void* pMem, int size ); + void GetLine( char* pLine, int nMaxChars = 0 ); + + // Used for getting objects that have a byteswap datadesc defined + template void GetObjects( T *dest, int count = 1 ); + + // This will get at least 1 byte and up to nSize bytes. + // It will return the number of bytes actually read. + int GetUpTo( void *pMem, int nSize ); + + // This version of GetString converts \" to \\ and " to \, etc. + // It also reads a " at the beginning and end of the string + void GetDelimitedString( CUtlCharConversion *pConv, char *pString, int nMaxChars = 0 ); + char GetDelimitedChar( CUtlCharConversion *pConv ); + + // This will return the # of characters of the string about to be read out + // NOTE: The count will *include* the terminating 0!! + // In binary mode, it's the number of characters until the next 0 + // In text mode, it's the number of characters until the next space. + int PeekStringLength(); + + // This version of PeekStringLength converts \" to \\ and " to \, etc. + // It also reads a " at the beginning and end of the string + // NOTE: The count will *include* the terminating 0!! + // In binary mode, it's the number of characters until the next 0 + // In text mode, it's the number of characters between "s (checking for \") + // Specifying false for bActualSize will return the pre-translated number of characters + // including the delimiters and the escape characters. So, \n counts as 2 characters when bActualSize == false + // and only 1 character when bActualSize == true + int PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActualSize = true ); + + // Just like scanf, but doesn't work in binary mode + int Scanf( const char* pFmt, ... ); + int VaScanf( const char* pFmt, va_list list ); + + // Eats white space, advances Get index + void EatWhiteSpace(); + + // Eats C++ style comments + bool EatCPPComment(); + + // (For text buffers only) + // Parse a token from the buffer: + // Grab all text that lies between a starting delimiter + ending delimiter + // (skipping whitespace that leads + trails both delimiters). + // If successful, the get index is advanced and the function returns true, + // otherwise the index is not advanced and the function returns false. + bool ParseToken( const char *pStartingDelim, const char *pEndingDelim, char* pString, int nMaxLen ); + + // Advance the get index until after the particular string is found + // Do not eat whitespace before starting. Return false if it failed + // String test is case-insensitive. + bool GetToken( const char *pToken ); + + // Parses the next token, given a set of character breaks to stop at + // Returns the length of the token parsed in bytes (-1 if none parsed) + int ParseToken( characterset_t *pBreaks, char *pTokenBuf, int nMaxLen, bool bParseComments = true ); + + // Write stuff in + // Binary mode: it'll just write the bits directly in, and strings will be + // written with a null terminating character + // Text mode: it'll convert the numbers to text versions + // PutString will not write a terminating character + void PutChar( char c ); + void PutUnsignedChar( unsigned char uc ); + void PutShort( short s ); + void PutUnsignedShort( unsigned short us ); + void PutInt( int i ); + void PutInt64( int64 i ); + void PutUnsignedInt( unsigned int u ); + void PutFloat( float f ); + void PutDouble( double d ); + void PutPtr( void * ); // Writes the pointer, not the pointed to + void PutString( const char* pString ); + void Put( const void* pMem, int size ); + + // Used for putting objects that have a byteswap datadesc defined + template void PutObjects( T *src, int count = 1 ); + + // This version of PutString converts \ to \\ and " to \", etc. + // It also places " at the beginning and end of the string + void PutDelimitedString( CUtlCharConversion *pConv, const char *pString ); + void PutDelimitedChar( CUtlCharConversion *pConv, char c ); + + // Just like printf, writes a terminating zero in binary mode + void Printf( const char* pFmt, ... ) FMTFUNCTION( 2, 3 ); + void VaPrintf( const char* pFmt, va_list list ); + + // What am I writing (put)/reading (get)? + void* PeekPut( int offset = 0 ); + const void* PeekGet( int offset = 0 ) const; + const void* PeekGet( int nMaxSize, int nOffset ); + + // Where am I writing (put)/reading (get)? + int TellPut( ) const; + int TellGet( ) const; + + // What's the most I've ever written? + int TellMaxPut( ) const; + + // How many bytes remain to be read? + // NOTE: This is not accurate for streaming text files; it overshoots + int GetBytesRemaining() const; + + // Change where I'm writing (put)/reading (get) + void SeekPut( SeekType_t type, int offset ); + void SeekGet( SeekType_t type, int offset ); + + // Buffer base + const void* Base() const; + void* Base(); + + // memory allocation size, does *not* reflect size written or read, + // use TellPut or TellGet for that + int Size() const; + + // Am I a text buffer? + bool IsText() const; + + // Can I grow if I'm externally allocated? + bool IsGrowable() const; + + // Am I valid? (overflow or underflow error), Once invalid it stays invalid + bool IsValid() const; + + // Do I contain carriage return/linefeeds? + bool ContainsCRLF() const; + + // Am I read-only + bool IsReadOnly() const; + + // Converts a buffer from a CRLF buffer to a CR buffer (and back) + // Returns false if no conversion was necessary (and outBuf is left untouched) + // If the conversion occurs, outBuf will be cleared. + bool ConvertCRLF( CUtlBuffer &outBuf ); + + // Push/pop pretty-printing tabs + void PushTab(); + void PopTab(); + + // Temporarily disables pretty print + void EnableTabs( bool bEnable ); + +protected: + // error flags + enum + { + PUT_OVERFLOW = 0x1, + GET_OVERFLOW = 0x2, + MAX_ERROR_FLAG = GET_OVERFLOW, + }; + + void SetOverflowFuncs( UtlBufferOverflowFunc_t getFunc, UtlBufferOverflowFunc_t putFunc ); + + bool OnPutOverflow( int nSize ); + bool OnGetOverflow( int nSize ); + +protected: + // Checks if a get/put is ok + bool CheckPut( int size ); + bool CheckGet( int size ); + + void AddNullTermination( ); + + // Methods to help with pretty-printing + bool WasLastCharacterCR(); + void PutTabs(); + + // Help with delimited stuff + char GetDelimitedCharInternal( CUtlCharConversion *pConv ); + void PutDelimitedCharInternal( CUtlCharConversion *pConv, char c ); + + // Default overflow funcs + bool PutOverflow( int nSize ); + bool GetOverflow( int nSize ); + + // Does the next bytes of the buffer match a pattern? + bool PeekStringMatch( int nOffset, const char *pString, int nLen ); + + // Peek size of line to come, check memory bound + int PeekLineLength(); + + // How much whitespace should I skip? + int PeekWhiteSpace( int nOffset ); + + // Checks if a peek get is ok + bool CheckPeekGet( int nOffset, int nSize ); + + // Call this to peek arbitrarily long into memory. It doesn't fail unless + // it can't read *anything* new + bool CheckArbitraryPeekGet( int nOffset, int &nIncrement ); + + template void GetType( T& dest ); + template void GetTypeBin( T& dest ); + template bool GetTypeText( T &value, int nRadix = 10 ); + template void GetObject( T *src ); + + template void PutType( T src ); + template void PutTypeBin( T src ); + template void PutObject( T *src ); + + CUtlMemory m_Memory; + int m_Get; + int m_Put; + + unsigned char m_Error; + unsigned char m_Flags; + unsigned char m_Reserved; +#if defined( _X360 ) + unsigned char pad; +#endif + + int m_nTab; + int m_nMaxPut; + int m_nOffset; + + UtlBufferOverflowFunc_t m_GetOverflowFunc; + UtlBufferOverflowFunc_t m_PutOverflowFunc; + + CByteswap m_Byteswap; +}; + + +// Stream style output operators for CUtlBuffer +inline CUtlBuffer &operator<<( CUtlBuffer &b, char v ) +{ + b.PutChar( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned char v ) +{ + b.PutUnsignedChar( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, short v ) +{ + b.PutShort( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned short v ) +{ + b.PutUnsignedShort( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, int v ) +{ + b.PutInt( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, unsigned int v ) +{ + b.PutUnsignedInt( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, float v ) +{ + b.PutFloat( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, double v ) +{ + b.PutDouble( v ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, const char *pv ) +{ + b.PutString( pv ); + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, const Vector &v ) +{ + b << v.x << " " << v.y << " " << v.z; + return b; +} + +inline CUtlBuffer &operator<<( CUtlBuffer &b, const Vector2D &v ) +{ + b << v.x << " " << v.y; + return b; +} + + +class CUtlInplaceBuffer : public CUtlBuffer +{ +public: + CUtlInplaceBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 ); + + // + // Routines returning buffer-inplace-pointers + // +public: + // + // Upon success, determines the line length, fills out the pointer to the + // beginning of the line and the line length, advances the "get" pointer + // offset by the line length and returns "true". + // + // If end of file is reached or upon error returns "false". + // + // Note: the returned length of the line is at least one character because the + // trailing newline characters are also included as part of the line. + // + // Note: the pointer returned points into the local memory of this buffer, in + // case the buffer gets relocated or destroyed the pointer becomes invalid. + // + // e.g.: ------------- + // + // char *pszLine; + // int nLineLen; + // while ( pUtlInplaceBuffer->InplaceGetLinePtr( &pszLine, &nLineLen ) ) + // { + // ... + // } + // + // ------------- + // + // @param ppszInBufferPtr on return points into this buffer at start of line + // @param pnLineLength on return holds num bytes accessible via (*ppszInBufferPtr) + // + // @returns true if line was successfully read + // false when EOF is reached or error occurs + // + bool InplaceGetLinePtr( /* out */ char **ppszInBufferPtr, /* out */ int *pnLineLength ); + + // + // Determines the line length, advances the "get" pointer offset by the line length, + // replaces the newline character with null-terminator and returns the initial pointer + // to now null-terminated line. + // + // If end of file is reached or upon error returns NULL. + // + // Note: the pointer returned points into the local memory of this buffer, in + // case the buffer gets relocated or destroyed the pointer becomes invalid. + // + // e.g.: ------------- + // + // while ( char *pszLine = pUtlInplaceBuffer->InplaceGetLinePtr() ) + // { + // ... + // } + // + // ------------- + // + // @returns ptr-to-zero-terminated-line if line was successfully read and buffer modified + // NULL when EOF is reached or error occurs + // + char * InplaceGetLinePtr( void ); +}; + + +//----------------------------------------------------------------------------- +// Where am I reading? +//----------------------------------------------------------------------------- +inline int CUtlBuffer::TellGet( ) const +{ + return m_Get; +} + + +//----------------------------------------------------------------------------- +// How many bytes remain to be read? +//----------------------------------------------------------------------------- +inline int CUtlBuffer::GetBytesRemaining() const +{ + return m_nMaxPut - TellGet(); +} + + +//----------------------------------------------------------------------------- +// What am I reading? +//----------------------------------------------------------------------------- +inline const void* CUtlBuffer::PeekGet( int offset ) const +{ + return &m_Memory[ m_Get + offset - m_nOffset ]; +} + + +//----------------------------------------------------------------------------- +// Unserialization +//----------------------------------------------------------------------------- + +template +inline void CUtlBuffer::GetObject( T *dest ) +{ + if ( CheckGet( sizeof(T) ) ) + { + if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) + { + *dest = *(T *)PeekGet(); + } + else + { + m_Byteswap.SwapFieldsToTargetEndian( dest, (T*)PeekGet() ); + } + m_Get += sizeof(T); + } + else + { + Q_memset( &dest, 0, sizeof(T) ); + } +} + + +template +inline void CUtlBuffer::GetObjects( T *dest, int count ) +{ + for ( int i = 0; i < count; ++i, ++dest ) + { + GetObject( dest ); + } +} + + +template +inline void CUtlBuffer::GetTypeBin( T &dest ) +{ + if ( CheckGet( sizeof(T) ) ) + { + if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) + { + dest = *(T *)PeekGet(); + } + else + { + m_Byteswap.SwapBufferToTargetEndian( &dest, (T*)PeekGet() ); + } + m_Get += sizeof(T); + } + else + { + dest = 0; + } +} + +template <> +inline void CUtlBuffer::GetTypeBin< float >( float &dest ) +{ + if ( CheckGet( sizeof( float ) ) ) + { + uintp pData = (uintp)PeekGet(); + if ( IsX360() && ( pData & 0x03 ) ) + { + // handle unaligned read + ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; + ((unsigned char*)&dest)[1] = ((unsigned char*)pData)[1]; + ((unsigned char*)&dest)[2] = ((unsigned char*)pData)[2]; + ((unsigned char*)&dest)[3] = ((unsigned char*)pData)[3]; + } + else + { + // aligned read + dest = *(float *)pData; + } + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian< float >( &dest, &dest ); + } + m_Get += sizeof( float ); + } + else + { + dest = 0; + } +} + +template <> +inline void CUtlBuffer::GetTypeBin< double >( double &dest ) +{ + if ( CheckGet( sizeof( double ) ) ) + { + uintp pData = (uintp)PeekGet(); + if ( IsX360() && ( pData & 0x07 ) ) + { + // handle unaligned read + ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; + ((unsigned char*)&dest)[1] = ((unsigned char*)pData)[1]; + ((unsigned char*)&dest)[2] = ((unsigned char*)pData)[2]; + ((unsigned char*)&dest)[3] = ((unsigned char*)pData)[3]; + ((unsigned char*)&dest)[4] = ((unsigned char*)pData)[4]; + ((unsigned char*)&dest)[5] = ((unsigned char*)pData)[5]; + ((unsigned char*)&dest)[6] = ((unsigned char*)pData)[6]; + ((unsigned char*)&dest)[7] = ((unsigned char*)pData)[7]; + } + else + { + // aligned read + dest = *(double *)pData; + } + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian< double >( &dest, &dest ); + } + m_Get += sizeof( double ); + } + else + { + dest = 0; + } +} + +template < class T > +inline T StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + Assert( 0 ); + *ppEnd = pString; + return 0; +} + +template <> +inline int8 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int8 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint8 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint8 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int16 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int16 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint16 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint16 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int32 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int32 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint32 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint32 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int64 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int64 )_strtoi64( pString, ppEnd, nRadix ); +} + +template <> +inline float StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + /*UNUSED*/( nRadix ); + return ( float )strtod( pString, ppEnd ); +} + +template <> +inline double StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + /*UNUSED*/( nRadix ); + return ( double )strtod( pString, ppEnd ); +} + +template +inline bool CUtlBuffer::GetTypeText( T &value, int nRadix /*= 10*/ ) +{ + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + int nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + value = 0; + return false; + } + + char *pStart = (char*)PeekGet(); + char* pEnd = pStart; + value = StringToNumber< T >( pStart, &pEnd, nRadix ); + + int nBytesRead = (int)( pEnd - pStart ); + if ( nBytesRead == 0 ) + return false; + + m_Get += nBytesRead; + return true; +} + +template +inline void CUtlBuffer::GetType( T &dest ) +{ + if (!IsText()) + { + GetTypeBin( dest ); + } + else + { + GetTypeText( dest ); + } +} + +inline char CUtlBuffer::GetChar( ) +{ + // LEGACY WARNING: this behaves differently than GetUnsignedChar() + char c; + GetTypeBin( c ); // always reads as binary + return c; +} + +inline unsigned char CUtlBuffer::GetUnsignedChar( ) +{ + // LEGACY WARNING: this behaves differently than GetChar() + unsigned char c; + if (!IsText()) + { + GetTypeBin( c ); + } + else + { + c = ( unsigned char )GetUnsignedShort(); + } + return c; +} + +inline short CUtlBuffer::GetShort( ) +{ + short s; + GetType( s ); + return s; +} + +inline unsigned short CUtlBuffer::GetUnsignedShort( ) +{ + unsigned short s; + GetType( s ); + return s; +} + +inline int CUtlBuffer::GetInt( ) +{ + int i; + GetType( i ); + return i; +} + +inline int64 CUtlBuffer::GetInt64( ) +{ + int64 i; + GetType( i ); + return i; +} + +inline unsigned int CUtlBuffer::GetIntHex( ) +{ + uint i; + if (!IsText()) + { + GetTypeBin( i ); + } + else + { + GetTypeText( i, 16 ); + } + return i; +} + +inline unsigned int CUtlBuffer::GetUnsignedInt( ) +{ + unsigned int i; + GetType( i ); + return i; +} + +inline float CUtlBuffer::GetFloat( ) +{ + float f; + GetType( f ); + return f; +} + +inline double CUtlBuffer::GetDouble( ) +{ + double d; + GetType( d ); + return d; +} + +inline void *CUtlBuffer::GetPtr( ) +{ + void *p; + // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal +#ifndef X64BITS + p = ( void* )GetUnsignedInt(); +#else + p = ( void* )GetInt64(); +#endif + return p; +} + +//----------------------------------------------------------------------------- +// Where am I writing? +//----------------------------------------------------------------------------- +inline unsigned char CUtlBuffer::GetFlags() const +{ + return m_Flags; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::IsExternallyAllocated() const +{ + return m_Memory.IsExternallyAllocated(); +} + + +//----------------------------------------------------------------------------- +// Where am I writing? +//----------------------------------------------------------------------------- +inline int CUtlBuffer::TellPut( ) const +{ + return m_Put; +} + + +//----------------------------------------------------------------------------- +// What's the most I've ever written? +//----------------------------------------------------------------------------- +inline int CUtlBuffer::TellMaxPut( ) const +{ + return m_nMaxPut; +} + + +//----------------------------------------------------------------------------- +// What am I reading? +//----------------------------------------------------------------------------- +inline void* CUtlBuffer::PeekPut( int offset ) +{ + return &m_Memory[m_Put + offset - m_nOffset]; +} + + +//----------------------------------------------------------------------------- +// Various put methods +//----------------------------------------------------------------------------- + +template +inline void CUtlBuffer::PutObject( T *src ) +{ + if ( CheckPut( sizeof(T) ) ) + { + if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) + { + *(T *)PeekPut() = *src; + } + else + { + m_Byteswap.SwapFieldsToTargetEndian( (T*)PeekPut(), src ); + } + m_Put += sizeof(T); + AddNullTermination(); + } +} + + +template +inline void CUtlBuffer::PutObjects( T *src, int count ) +{ + for ( int i = 0; i < count; ++i, ++src ) + { + PutObject( src ); + } +} + + +template +inline void CUtlBuffer::PutTypeBin( T src ) +{ + if ( CheckPut( sizeof(T) ) ) + { + if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) + { + *(T *)PeekPut() = src; + } + else + { + m_Byteswap.SwapBufferToTargetEndian( (T*)PeekPut(), &src ); + } + m_Put += sizeof(T); + AddNullTermination(); + } +} + +#if defined( _X360 ) +template <> +inline void CUtlBuffer::PutTypeBin< float >( float src ) +{ + if ( CheckPut( sizeof( src ) ) ) + { + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian( &src, &src ); + } + + // + // Write the data + // + unsigned pData = (unsigned)PeekPut(); + if ( pData & 0x03 ) + { + // handle unaligned write + byte* dst = (byte*)pData; + byte* srcPtr = (byte*)&src; + dst[0] = srcPtr[0]; + dst[1] = srcPtr[1]; + dst[2] = srcPtr[2]; + dst[3] = srcPtr[3]; + } + else + { + *(float *)pData = src; + } + + m_Put += sizeof(float); + AddNullTermination(); + } +} + +template <> +inline void CUtlBuffer::PutTypeBin< double >( double src ) +{ + if ( CheckPut( sizeof( src ) ) ) + { + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian( &src, &src ); + } + + // + // Write the data + // + unsigned pData = (unsigned)PeekPut(); + if ( pData & 0x07 ) + { + // handle unaligned write + byte* dst = (byte*)pData; + byte* srcPtr = (byte*)&src; + dst[0] = srcPtr[0]; + dst[1] = srcPtr[1]; + dst[2] = srcPtr[2]; + dst[3] = srcPtr[3]; + dst[4] = srcPtr[4]; + dst[5] = srcPtr[5]; + dst[6] = srcPtr[6]; + dst[7] = srcPtr[7]; + } + else + { + *(double *)pData = src; + } + + m_Put += sizeof(double); + AddNullTermination(); + } +} +#endif + +template +inline void CUtlBuffer::PutType( T src ) +{ + if (!IsText()) + { + PutTypeBin( src ); + } + else + { + Printf( GetFmtStr< T >(), src ); + } +} + +//----------------------------------------------------------------------------- +// Methods to help with pretty-printing +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::WasLastCharacterCR() +{ + if ( !IsText() || (TellPut() == 0) ) + return false; + return ( *( const char * )PeekPut( -1 ) == '\n' ); +} + +inline void CUtlBuffer::PutTabs() +{ + int nTabCount = ( m_Flags & AUTO_TABS_DISABLED ) ? 0 : m_nTab; + for (int i = nTabCount; --i >= 0; ) + { + PutTypeBin( '\t' ); + } +} + + +//----------------------------------------------------------------------------- +// Push/pop pretty-printing tabs +//----------------------------------------------------------------------------- +inline void CUtlBuffer::PushTab( ) +{ + ++m_nTab; +} + +inline void CUtlBuffer::PopTab() +{ + if ( --m_nTab < 0 ) + { + m_nTab = 0; + } +} + + +//----------------------------------------------------------------------------- +// Temporarily disables pretty print +//----------------------------------------------------------------------------- +inline void CUtlBuffer::EnableTabs( bool bEnable ) +{ + if ( bEnable ) + { + m_Flags &= ~AUTO_TABS_DISABLED; + } + else + { + m_Flags |= AUTO_TABS_DISABLED; + } +} + +inline void CUtlBuffer::PutChar( char c ) +{ + if ( WasLastCharacterCR() ) + { + PutTabs(); + } + + PutTypeBin( c ); +} + +inline void CUtlBuffer::PutUnsignedChar( unsigned char c ) +{ + if (!IsText()) + { + PutTypeBin( c ); + } + else + { + PutUnsignedShort( c ); + } +} + +inline void CUtlBuffer::PutShort( short s ) +{ + PutType( s ); +} + +inline void CUtlBuffer::PutUnsignedShort( unsigned short s ) +{ + PutType( s ); +} + +inline void CUtlBuffer::PutInt( int i ) +{ + PutType( i ); +} + +inline void CUtlBuffer::PutInt64( int64 i ) +{ + PutType( i ); +} + +inline void CUtlBuffer::PutUnsignedInt( unsigned int u ) +{ + PutType( u ); +} + +inline void CUtlBuffer::PutFloat( float f ) +{ + PutType( f ); +} + +inline void CUtlBuffer::PutDouble( double d ) +{ + PutType( d ); +} + +inline void CUtlBuffer::PutPtr( void *p ) +{ + // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal + if (!IsText()) + { + PutTypeBin( p ); + } + else + { + Printf( "0x%p", p ); + } +} + +//----------------------------------------------------------------------------- +// Am I a text buffer? +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::IsText() const +{ + return (m_Flags & TEXT_BUFFER) != 0; +} + + +//----------------------------------------------------------------------------- +// Can I grow if I'm externally allocated? +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::IsGrowable() const +{ + return (m_Flags & EXTERNAL_GROWABLE) != 0; +} + + +//----------------------------------------------------------------------------- +// Am I valid? (overflow or underflow error), Once invalid it stays invalid +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::IsValid() const +{ + return m_Error == 0; +} + + +//----------------------------------------------------------------------------- +// Do I contain carriage return/linefeeds? +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::ContainsCRLF() const +{ + return IsText() && ((m_Flags & CONTAINS_CRLF) != 0); +} + + +//----------------------------------------------------------------------------- +// Am I read-only +//----------------------------------------------------------------------------- +inline bool CUtlBuffer::IsReadOnly() const +{ + return (m_Flags & READ_ONLY) != 0; +} + + +//----------------------------------------------------------------------------- +// Buffer base and size +//----------------------------------------------------------------------------- +inline const void* CUtlBuffer::Base() const +{ + return m_Memory.Base(); +} + +inline void* CUtlBuffer::Base() +{ + return m_Memory.Base(); +} + +inline int CUtlBuffer::Size() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Clears out the buffer; frees memory +//----------------------------------------------------------------------------- +inline void CUtlBuffer::Clear() +{ + m_Get = 0; + m_Put = 0; + m_Error = 0; + m_nOffset = 0; + m_nMaxPut = -1; + AddNullTermination(); +} + +inline void CUtlBuffer::Purge() +{ + m_Get = 0; + m_Put = 0; + m_nOffset = 0; + m_nMaxPut = 0; + m_Error = 0; + m_Memory.Purge(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) +{ + Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); + EnsureCapacity( nBytes ); + m_nMaxPut = nBytes; + return Base(); +} + +inline void *CUtlBuffer::Detach() +{ + void *p = m_Memory.Detach(); + Clear(); + return p; +} + +//----------------------------------------------------------------------------- + +inline void CUtlBuffer::Spew( ) +{ + SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + char pTmpLine[1024]; + while( IsValid() && GetBytesRemaining() ) + { + V_memset( pTmpLine, 0, sizeof(pTmpLine) ); + Get( pTmpLine, MIN( GetBytesRemaining(), sizeof(pTmpLine-1) ) ); + Msg( _T( "%s" ), pTmpLine ); + } +} + + +#endif // UTLBUFFER_H + diff --git a/public/tier1/utlbufferutil.h b/public/tier1/utlbufferutil.h new file mode 100644 index 0000000..c6d1860 --- /dev/null +++ b/public/tier1/utlbufferutil.h @@ -0,0 +1,197 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Utilities for serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLBUFFERUTIL_H +#define UTLBUFFERUTIL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class Vector2D; +class Vector; +class Vector4D; +class QAngle; +class Quaternion; +class VMatrix; +class Color; +class CUtlBinaryBlock; +class CUtlString; +class CUtlCharConversion; +class CUtlSymbolLarge; + + +//----------------------------------------------------------------------------- +// For string serialization, set the delimiter rules +//----------------------------------------------------------------------------- +void SetSerializationDelimiter( CUtlCharConversion *pConv ); +void SetSerializationArrayDelimiter( const char *pDelimiter ); + + +//----------------------------------------------------------------------------- +// Standard serialization methods for basic types +//----------------------------------------------------------------------------- +bool Serialize( CUtlBuffer &buf, const bool &src ); +bool Unserialize( CUtlBuffer &buf, bool &dest ); + +bool Serialize( CUtlBuffer &buf, const int &src ); +bool Unserialize( CUtlBuffer &buf, int &dest ); + +bool Serialize( CUtlBuffer &buf, const float &src ); +bool Unserialize( CUtlBuffer &buf, float &dest ); + +bool Serialize( CUtlBuffer &buf, const Vector2D &src ); +bool Unserialize( CUtlBuffer &buf, Vector2D &dest ); + +bool Serialize( CUtlBuffer &buf, const Vector &src ); +bool Unserialize( CUtlBuffer &buf, Vector &dest ); + +bool Serialize( CUtlBuffer &buf, const Vector4D &src ); +bool Unserialize( CUtlBuffer &buf, Vector4D &dest ); + +bool Serialize( CUtlBuffer &buf, const QAngle &src ); +bool Unserialize( CUtlBuffer &buf, QAngle &dest ); + +bool Serialize( CUtlBuffer &buf, const Quaternion &src ); +bool Unserialize( CUtlBuffer &buf, Quaternion &dest ); + +bool Serialize( CUtlBuffer &buf, const VMatrix &src ); +bool Unserialize( CUtlBuffer &buf, VMatrix &dest ); + +bool Serialize( CUtlBuffer &buf, const Color &src ); +bool Unserialize( CUtlBuffer &buf, Color &dest ); + +bool Serialize( CUtlBuffer &buf, const CUtlBinaryBlock &src ); +bool Unserialize( CUtlBuffer &buf, CUtlBinaryBlock &dest ); + +bool Serialize( CUtlBuffer &buf, const CUtlString &src ); +bool Unserialize( CUtlBuffer &buf, CUtlString &dest ); + +bool Serialize( CUtlBuffer &buf, const CUtlSymbolLarge &src ); +// There is explicitly no unserialize of CUtlSymbolLarge, +// it requires adding the a string to a specific symbol table. + + +//----------------------------------------------------------------------------- +// You can use this to check if a type serializes on multiple lines +//----------------------------------------------------------------------------- +template< class T > +inline bool SerializesOnMultipleLines() +{ + return false; +} + +template< > +inline bool SerializesOnMultipleLines() +{ + return true; +} + +template< > +inline bool SerializesOnMultipleLines() +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Vector serialization +//----------------------------------------------------------------------------- +template< class T > +bool Serialize( CUtlBuffer &buf, const CUtlVector &src ) +{ + extern const char *s_pUtlBufferUtilArrayDelim; + + int nCount = src.Count(); + + if ( !buf.IsText() ) + { + buf.PutInt( nCount ); + for ( int i = 0; i < nCount; ++i ) + { + ::Serialize( buf, src[i] ); + } + return buf.IsValid(); + } + + if ( !SerializesOnMultipleLines() ) + { + buf.PutChar('\n'); + for ( int i = 0; i < nCount; ++i ) + { + ::Serialize( buf, src[i] ); + if ( s_pUtlBufferUtilArrayDelim && (i != nCount-1) ) + { + buf.PutString( s_pUtlBufferUtilArrayDelim ); + } + buf.PutChar('\n'); + } + } + else + { + for ( int i = 0; i < nCount; ++i ) + { + ::Serialize( buf, src[i] ); + if ( s_pUtlBufferUtilArrayDelim && (i != nCount-1) ) + { + buf.PutString( s_pUtlBufferUtilArrayDelim ); + } + buf.PutChar(' '); + } + } + + return buf.IsValid(); +} + +template< class T > +bool Unserialize( CUtlBuffer &buf, CUtlVector &dest ) +{ + dest.RemoveAll(); + + MEM_ALLOC_CREDIT_FUNCTION(); + + if ( !buf.IsText() ) + { + int nCount = buf.GetInt(); + if ( nCount ) + { + dest.EnsureCapacity( nCount ); + for ( int i = 0; i < nCount; ++i ) + { + VerifyEquals( dest.AddToTail(), i ); + if ( !::Unserialize( buf, dest[i] ) ) + return false; + } + } + return buf.IsValid(); + } + + while ( true ) + { + buf.EatWhiteSpace(); + if ( !buf.IsValid() ) + break; + + int i = dest.AddToTail( ); + if ( ! ::Unserialize( buf, dest[i] ) ) + return false; + } + return true; +} + + +#endif // UTLBUFFERUTIL_H + diff --git a/public/tier1/utldict.h b/public/tier1/utldict.h new file mode 100644 index 0000000..12714a4 --- /dev/null +++ b/public/tier1/utldict.h @@ -0,0 +1,324 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: A dictionary mapping from symbol to structure +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLDICT_H +#define UTLDICT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier1/utlmap.h" + +// Include this because tons of code was implicitly getting utlsymbol or utlvector via utldict.h +#include "tier1/utlsymbol.h" + +#include "tier0/memdbgon.h" + +enum EDictCompareType +{ + k_eDictCompareTypeCaseSensitive=0, + k_eDictCompareTypeCaseInsensitive=1, + k_eDictCompareTypeFilenames // Slashes and backslashes count as the same character.. +}; + +//----------------------------------------------------------------------------- +// A dictionary mapping from symbol to structure +//----------------------------------------------------------------------------- +template +class CUtlDict +{ +public: + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + CUtlDict( int compareType = k_eDictCompareTypeCaseInsensitive, int growSize = 0, int initSize = 0 ); + ~CUtlDict( ); + + void EnsureCapacity( int ); + + // gets particular elements + T& Element( I i ); + const T& Element( I i ) const; + T& operator[]( I i ); + const T& operator[]( I i ) const; + + // gets element names + char *GetElementName( I i ); + char const *GetElementName( I i ) const; + + void SetElementName( I i, char const *pName ); + + // Number of elements + unsigned int Count() const; + + // Checks if a node is valid and in the tree + bool IsValidIndex( I i ) const; + + // Invalid index + static I InvalidIndex(); + + // Insert method (inserts in order) + I Insert( const char *pName, const T &element ); + I Insert( const char *pName ); + + // Find method + I Find( const char *pName ) const; + + // Remove methods + void RemoveAt( I i ); + void Remove( const char *pName ); + void RemoveAll( ); + + // Purge memory + void Purge(); + void PurgeAndDeleteElements(); // Call delete on each element. + + // Iteration methods + I First() const; + I Next( I i ) const; + + // Nested typedefs, for code that might need + // to fish out the index type from a given dict + typedef I IndexType_t; + +protected: + typedef CUtlMap DictElementMap_t; + DictElementMap_t m_Elements; +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template +CUtlDict::CUtlDict( int compareType, int growSize, int initSize ) : m_Elements( growSize, initSize ) +{ + if ( compareType == k_eDictCompareTypeFilenames ) + { + m_Elements.SetLessFunc( CaselessStringLessThanIgnoreSlashes ); + } + else if ( compareType == k_eDictCompareTypeCaseInsensitive ) + { + m_Elements.SetLessFunc( CaselessStringLessThan ); + } + else + { + m_Elements.SetLessFunc( StringLessThan ); + } +} + +template +CUtlDict::~CUtlDict() +{ + Purge(); +} + +template +inline void CUtlDict::EnsureCapacity( int num ) +{ + return m_Elements.EnsureCapacity( num ); +} + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlDict::Element( I i ) +{ + return m_Elements[i]; +} + +template +inline const T& CUtlDict::Element( I i ) const +{ + return m_Elements[i]; +} + +//----------------------------------------------------------------------------- +// gets element names +//----------------------------------------------------------------------------- +template +inline char *CUtlDict::GetElementName( I i ) +{ + return (char *)m_Elements.Key( i ); +} + +template +inline char const *CUtlDict::GetElementName( I i ) const +{ + return m_Elements.Key( i ); +} + +template +inline T& CUtlDict::operator[]( I i ) +{ + return Element(i); +} + +template +inline const T & CUtlDict::operator[]( I i ) const +{ + return Element(i); +} + +template +inline void CUtlDict::SetElementName( I i, char const *pName ) +{ + MEM_ALLOC_CREDIT_CLASS(); + // TODO: This makes a copy of the old element + // TODO: This relies on the rb tree putting the most recently + // removed element at the head of the insert list + free( (void *)m_Elements.Key( i ) ); + m_Elements.Reinsert( strdup( pName ), i ); +} + +//----------------------------------------------------------------------------- +// Num elements +//----------------------------------------------------------------------------- +template +inline unsigned int CUtlDict::Count() const +{ + return m_Elements.Count(); +} + + +//----------------------------------------------------------------------------- +// Checks if a node is valid and in the tree +//----------------------------------------------------------------------------- +template +inline bool CUtlDict::IsValidIndex( I i ) const +{ + return m_Elements.IsValidIndex(i); +} + + +//----------------------------------------------------------------------------- +// Invalid index +//----------------------------------------------------------------------------- +template +inline I CUtlDict::InvalidIndex() +{ + return DictElementMap_t::InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Delete a node from the tree +//----------------------------------------------------------------------------- +template +void CUtlDict::RemoveAt(I elem) +{ + free( (void *)m_Elements.Key( elem ) ); + m_Elements.RemoveAt(elem); +} + + +//----------------------------------------------------------------------------- +// remove a node in the tree +//----------------------------------------------------------------------------- +template void CUtlDict::Remove( const char *search ) +{ + I node = Find( search ); + if (node != InvalidIndex()) + { + RemoveAt(node); + } +} + + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree +//----------------------------------------------------------------------------- +template +void CUtlDict::RemoveAll() +{ + typename DictElementMap_t::IndexType_t index = m_Elements.FirstInorder(); + while ( index != m_Elements.InvalidIndex() ) + { + free( (void *)m_Elements.Key( index ) ); + index = m_Elements.NextInorder( index ); + } + + m_Elements.RemoveAll(); +} + +template +void CUtlDict::Purge() +{ + RemoveAll(); +} + + +template +void CUtlDict::PurgeAndDeleteElements() +{ + // Delete all the elements. + I index = m_Elements.FirstInorder(); + while ( index != m_Elements.InvalidIndex() ) + { + free( (void *)m_Elements.Key( index ) ); + delete m_Elements[index]; + index = m_Elements.NextInorder( index ); + } + + m_Elements.RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// inserts a node into the tree +//----------------------------------------------------------------------------- +template +I CUtlDict::Insert( const char *pName, const T &element ) +{ + MEM_ALLOC_CREDIT_CLASS(); + return m_Elements.Insert( strdup( pName ), element ); +} + +template +I CUtlDict::Insert( const char *pName ) +{ + MEM_ALLOC_CREDIT_CLASS(); + return m_Elements.Insert( strdup( pName ) ); +} + + +//----------------------------------------------------------------------------- +// finds a node in the tree +//----------------------------------------------------------------------------- +template +I CUtlDict::Find( const char *pName ) const +{ + MEM_ALLOC_CREDIT_CLASS(); + if ( pName ) + return m_Elements.Find( pName ); + else + return InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Iteration methods +//----------------------------------------------------------------------------- +template +I CUtlDict::First() const +{ + return m_Elements.FirstInorder(); +} + +template +I CUtlDict::Next( I i ) const +{ + return m_Elements.NextInorder(i); +} + +#include "tier0/memdbgoff.h" + +#endif // UTLDICT_H diff --git a/public/tier1/utlenvelope.h b/public/tier1/utlenvelope.h new file mode 100644 index 0000000..28e434b --- /dev/null +++ b/public/tier1/utlenvelope.h @@ -0,0 +1,241 @@ +//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: A class to wrap data for transport over a boundary like a thread +// or window. +// +//============================================================================= + +#include "tier1/utlstring.h" +#include "tier0/basetypes.h" + +#ifndef UTLENVELOPE_H +#define UTLENVELOPE_H + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- + +class CUtlDataEnvelope +{ +public: + CUtlDataEnvelope( const void *pData, int nBytes ); + CUtlDataEnvelope( const CUtlDataEnvelope &from ); + ~CUtlDataEnvelope(); + + CUtlDataEnvelope &operator=( const CUtlDataEnvelope &from ); + + operator void *(); + operator void *() const; + +private: + void Assign( const void *pData, int nBytes ); + void Assign( const CUtlDataEnvelope &from ); + void Purge(); + + // TODO: switch to a reference counted array? + union + { + byte *m_pData; + byte m_data[4]; + }; + int m_nBytes; +}; + + +//----------------------------------------------------------------------------- + +template +class CUtlEnvelope : protected CUtlDataEnvelope +{ +public: + CUtlEnvelope( const T *pData, int nElems = 1 ); + CUtlEnvelope( const CUtlEnvelope &from ); + + CUtlEnvelope &operator=( const CUtlEnvelope &from ); + + operator T *(); + operator T *() const; + + operator void *(); + operator void *() const; +}; + +//----------------------------------------------------------------------------- + +template <> +class CUtlEnvelope +{ +public: + CUtlEnvelope( const char *pData ) + { + m_string = pData; + } + + CUtlEnvelope( const CUtlEnvelope &from ) + { + m_string = from.m_string; + } + + CUtlEnvelope &operator=( const CUtlEnvelope &from ) + { + m_string = from.m_string; + return *this; + } + + operator char *() + { + return (char *) m_string.Get(); + } + + operator char *() const + { + return (char *) m_string.Get(); + } + + operator void *() + { + return (void *) m_string.Get(); + } + + operator void *() const + { + return (void *) m_string.Get(); + } + +private: + CUtlString m_string; +}; + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgon.h" + +inline void CUtlDataEnvelope::Assign( const void *pData, int nBytes ) +{ + if ( pData ) + { + m_nBytes = nBytes; + if ( m_nBytes > 4 ) + { + m_pData = new byte[nBytes]; + memcpy( m_pData, pData, nBytes ); + } + else + { + memcpy( m_data, pData, nBytes ); + } + } + else + { + m_pData = NULL; + m_nBytes = 0; + } +} + +inline void CUtlDataEnvelope::Assign( const CUtlDataEnvelope &from ) +{ + Assign( from.operator void *(), from.m_nBytes ); +} + +inline void CUtlDataEnvelope::Purge() +{ + if (m_nBytes > 4) + delete [] m_pData; + m_nBytes = 0; +} + +inline CUtlDataEnvelope::CUtlDataEnvelope( const void *pData, int nBytes ) +{ + Assign( pData, nBytes ); +} + +inline CUtlDataEnvelope::CUtlDataEnvelope( const CUtlDataEnvelope &from ) +{ + Assign( from ); +} + +inline CUtlDataEnvelope::~CUtlDataEnvelope() +{ + Purge(); +} + +inline CUtlDataEnvelope &CUtlDataEnvelope::operator=( const CUtlDataEnvelope &from ) +{ + Purge(); + Assign( from ); + return *this; +} + +inline CUtlDataEnvelope::operator void *() +{ + if ( !m_nBytes ) + { + return NULL; + } + + return ( m_nBytes > 4) ? m_pData : m_data; +} + +inline CUtlDataEnvelope::operator void *() const +{ + if ( !m_nBytes ) + { + return NULL; + } + + return ( m_nBytes > 4) ? (void *)m_pData : (void *)m_data; +} + +//----------------------------------------------------------------------------- + +template +inline CUtlEnvelope::CUtlEnvelope( const T *pData, int nElems ) + : CUtlDataEnvelope( pData, sizeof(T) * nElems ) +{ +} + +template +inline CUtlEnvelope::CUtlEnvelope( const CUtlEnvelope &from ) + : CUtlDataEnvelope( from ) +{ + +} + +template +inline CUtlEnvelope &CUtlEnvelope::operator=( const CUtlEnvelope &from ) +{ + CUtlDataEnvelope::operator=( from ); + return *this; +} + +template +inline CUtlEnvelope::operator T *() +{ + return (T *)CUtlDataEnvelope::operator void *(); +} + +template +inline CUtlEnvelope::operator T *() const +{ + return (T *)( (const_cast *>(this))->operator T *() ); +} + +template +inline CUtlEnvelope::operator void *() +{ + return CUtlDataEnvelope::operator void *(); +} + +template +inline CUtlEnvelope::operator void *() const +{ + return ( (const_cast *>(this))->operator void *() ); +} + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // UTLENVELOPE_H diff --git a/public/tier1/utlfixedmemory.h b/public/tier1/utlfixedmemory.h new file mode 100644 index 0000000..5c3c63f --- /dev/null +++ b/public/tier1/utlfixedmemory.h @@ -0,0 +1,354 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A growable memory class. +//===========================================================================// + +#ifndef UTLFIXEDMEMORY_H +#define UTLFIXEDMEMORY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "tier0/platform.h" + +#include "tier0/memalloc.h" +#include "tier0/memdbgon.h" + +#pragma warning (disable:4100) +#pragma warning (disable:4514) + +//----------------------------------------------------------------------------- + +#ifdef UTLFIXEDMEMORY_TRACK +#define UTLFIXEDMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlFixedMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#define UTLFIXEDMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlFixedMemory", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) +#else +#define UTLFIXEDMEMORY_TRACK_ALLOC() ((void)0) +#define UTLFIXEDMEMORY_TRACK_FREE() ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// The CUtlFixedMemory class: +// A growable memory class that allocates non-sequential blocks, but is indexed sequentially +//----------------------------------------------------------------------------- +template< class T > +class CUtlFixedMemory +{ +public: + // constructor, destructor + CUtlFixedMemory( int nGrowSize = 0, int nInitSize = 0 ); + ~CUtlFixedMemory(); + + // Set the size by which the memory grows + void Init( int nGrowSize = 0, int nInitSize = 0 ); + + // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL + T* Base() { return NULL; } + const T* Base() const { return NULL; } + +protected: + struct BlockHeader_t; + +public: + class Iterator_t + { + public: + Iterator_t( BlockHeader_t *p, int i ) : m_pBlockHeader( p ), m_nIndex( i ) {} + BlockHeader_t *m_pBlockHeader; + int m_nIndex; + + bool operator==( const Iterator_t it ) const { return m_pBlockHeader == it.m_pBlockHeader && m_nIndex == it.m_nIndex; } + bool operator!=( const Iterator_t it ) const { return m_pBlockHeader != it.m_pBlockHeader || m_nIndex != it.m_nIndex; } + }; + Iterator_t First() const { return m_pBlocks ? Iterator_t( m_pBlocks, 0 ) : InvalidIterator(); } + Iterator_t Next( const Iterator_t &it ) const + { + Assert( IsValidIterator( it ) ); + if ( !IsValidIterator( it ) ) + return InvalidIterator(); + + BlockHeader_t * RESTRICT pHeader = it.m_pBlockHeader; + if ( it.m_nIndex + 1 < pHeader->m_nBlockSize ) + return Iterator_t( pHeader, it.m_nIndex + 1 ); + + return pHeader->m_pNext ? Iterator_t( pHeader->m_pNext, 0 ) : InvalidIterator(); + } + int GetIndex( const Iterator_t &it ) const + { + Assert( IsValidIterator( it ) ); + if ( !IsValidIterator( it ) ) + return InvalidIndex(); + + return ( int )( HeaderToBlock( it.m_pBlockHeader ) + it.m_nIndex ); + } + bool IsIdxAfter( int i, const Iterator_t &it ) const + { + Assert( IsValidIterator( it ) ); + if ( !IsValidIterator( it ) ) + return false; + + if ( IsInBlock( i, it.m_pBlockHeader ) ) + return i > GetIndex( it ); + + for ( BlockHeader_t * RESTRICT pbh = it.m_pBlockHeader->m_pNext; pbh; pbh = pbh->m_pNext ) + { + if ( IsInBlock( i, pbh ) ) + return true; + } + return false; + } + bool IsValidIterator( const Iterator_t &it ) const { return it.m_pBlockHeader && it.m_nIndex >= 0 && it.m_nIndex < it.m_pBlockHeader->m_nBlockSize; } + Iterator_t InvalidIterator() const { return Iterator_t( NULL, -1 ); } + + // element access + T& operator[]( int i ); + const T& operator[]( int i ) const; + T& Element( int i ); + const T& Element( int i ) const; + + // Can we use this index? + bool IsIdxValid( int i ) const; + + // Specify the invalid ('null') index that we'll only return on failure + static const int INVALID_INDEX = 0; // For use with COMPILE_TIME_ASSERT + static int InvalidIndex() { return INVALID_INDEX; } + + // Size + int NumAllocated() const; + int Count() const { return NumAllocated(); } + + // Grows memory by max(num,growsize), and returns the allocation index/ptr + void Grow( int num = 1 ); + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + +protected: + // Fast swap - WARNING: Swap invalidates all ptr-based indices!!! + void Swap( CUtlFixedMemory< T > &mem ); + + bool IsInBlock( int i, BlockHeader_t *pBlockHeader ) const + { + T *p = ( T* )i; + const T *p0 = HeaderToBlock( pBlockHeader ); + return p >= p0 && p < p0 + pBlockHeader->m_nBlockSize; + } + + struct BlockHeader_t + { + BlockHeader_t *m_pNext; + int m_nBlockSize; + }; + + const T *HeaderToBlock( const BlockHeader_t *pHeader ) const { return ( T* )( pHeader + 1 ); } + const BlockHeader_t *BlockToHeader( const T *pBlock ) const { return ( BlockHeader_t* )( pBlock ) - 1; } + + BlockHeader_t* m_pBlocks; + int m_nAllocationCount; + int m_nGrowSize; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T > +CUtlFixedMemory::CUtlFixedMemory( int nGrowSize, int nInitAllocationCount ) +: m_pBlocks( 0 ), m_nAllocationCount( 0 ), m_nGrowSize( 0 ) +{ + Init( nGrowSize, nInitAllocationCount ); +} + +template< class T > +CUtlFixedMemory::~CUtlFixedMemory() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Fast swap - WARNING: Swap invalidates all ptr-based indices!!! +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Swap( CUtlFixedMemory< T > &mem ) +{ + V_swap( m_pBlocks, mem.m_pBlocks ); + V_swap( m_nAllocationCount, mem.m_nAllocationCount ); + V_swap( m_nGrowSize, mem.m_nGrowSize ); +} + + +//----------------------------------------------------------------------------- +// Set the size by which the memory grows - round up to the next power of 2 +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Init( int nGrowSize /* = 0 */, int nInitSize /* = 0 */ ) +{ + Purge(); + + m_nGrowSize = nGrowSize; + + Grow( nInitSize ); +} + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T > +inline T& CUtlFixedMemory::operator[]( int i ) +{ + Assert( IsIdxValid(i) ); + return *( T* )i; +} + +template< class T > +inline const T& CUtlFixedMemory::operator[]( int i ) const +{ + Assert( IsIdxValid(i) ); + return *( T* )i; +} + +template< class T > +inline T& CUtlFixedMemory::Element( int i ) +{ + Assert( IsIdxValid(i) ); + return *( T* )i; +} + +template< class T > +inline const T& CUtlFixedMemory::Element( int i ) const +{ + Assert( IsIdxValid(i) ); + return *( T* )i; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T > +inline int CUtlFixedMemory::NumAllocated() const +{ + return m_nAllocationCount; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T > +inline bool CUtlFixedMemory::IsIdxValid( int i ) const +{ +#ifdef _DEBUG + for ( BlockHeader_t *pbh = m_pBlocks; pbh; pbh = pbh->m_pNext ) + { + if ( IsInBlock( i, pbh ) ) + return true; + } + return false; +#else + return i != InvalidIndex(); +#endif +} + +template< class T > +void CUtlFixedMemory::Grow( int num ) +{ + if ( num <= 0 ) + return; + + int nBlockSize = m_nGrowSize; + if ( nBlockSize == 0 ) + { + if ( m_nAllocationCount ) + { + nBlockSize = m_nAllocationCount; + } + else + { + // Compute an allocation which is at least as big as a cache line... + nBlockSize = ( 31 + sizeof( T ) ) / sizeof( T ); + Assert( nBlockSize ); + } + } + if ( nBlockSize < num ) + { + int n = ( num + nBlockSize -1 ) / nBlockSize; + Assert( n * nBlockSize >= num ); + Assert( ( n - 1 ) * nBlockSize < num ); + nBlockSize *= n; + } + m_nAllocationCount += nBlockSize; + + MEM_ALLOC_CREDIT_CLASS(); + BlockHeader_t * RESTRICT pBlockHeader = ( BlockHeader_t* )malloc( sizeof( BlockHeader_t ) + nBlockSize * sizeof( T ) ); + if ( !pBlockHeader ) + { + Error( "CUtlFixedMemory overflow!\n" ); + } + pBlockHeader->m_pNext = NULL; + pBlockHeader->m_nBlockSize = nBlockSize; + + if ( !m_pBlocks ) + { + m_pBlocks = pBlockHeader; + } + else + { +#if 1 // IsIdxAfter assumes that newly allocated blocks are at the end + BlockHeader_t * RESTRICT pbh = m_pBlocks; + while ( pbh->m_pNext ) + { + pbh = pbh->m_pNext; + } + pbh->m_pNext = pBlockHeader; +#else + pBlockHeader = m_pBlocks; + pBlockHeader->m_pNext = m_pBlocks; +#endif + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T > +inline void CUtlFixedMemory::EnsureCapacity( int num ) +{ + Grow( num - NumAllocated() ); +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T > +void CUtlFixedMemory::Purge() +{ + if ( !m_pBlocks ) + return; + + for ( BlockHeader_t *pbh = m_pBlocks; pbh; ) + { + BlockHeader_t *pFree = pbh; + pbh = pbh->m_pNext; + free( pFree ); + } + m_pBlocks = NULL; + m_nAllocationCount = 0; +} + +#include "tier0/memdbgoff.h" + +#endif // UTLFIXEDMEMORY_H diff --git a/public/tier1/utlflags.h b/public/tier1/utlflags.h new file mode 100644 index 0000000..893a5c7 --- /dev/null +++ b/public/tier1/utlflags.h @@ -0,0 +1,124 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: Simple class to make it easier to deal with flags +// +//============================================================================= + +#ifndef UTLFLAGS_H +#define UTLFLAGS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" + + +//----------------------------------------------------------------------------- +// Simple class to make it easier to deal with flags +//----------------------------------------------------------------------------- +template< class T > +class CUtlFlags +{ +public: + CUtlFlags( int nInitialFlags = 0 ); + + // Flag setting + void SetFlag( int nFlagMask ); + void SetFlag( int nFlagMask, bool bEnable ); + + // Flag clearing + void ClearFlag( int nFlagMask ); + void ClearAllFlags(); + bool IsFlagSet( int nFlagMask ) const; + + // Is any flag set? + bool IsAnyFlagSet() const; + +private: + T m_nFlags; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +template< class T > +CUtlFlags::CUtlFlags( int nInitialFlags ) +{ + // Makes sure we didn't truncate + Assert( nInitialFlags == (T)nInitialFlags ); + + m_nFlags = (T)nInitialFlags; +} + + +//----------------------------------------------------------------------------- +// Set flags +//----------------------------------------------------------------------------- +template< class T > +void CUtlFlags::SetFlag( int nFlagMask ) +{ + // Makes sure we didn't truncate + Assert( nFlagMask == (T)nFlagMask ); + + m_nFlags |= (T)nFlagMask; +} + +template< class T > +void CUtlFlags::SetFlag( int nFlagMask, bool bEnable ) +{ + // Makes sure we didn't truncate + Assert( nFlagMask == (T)nFlagMask ); + + if ( bEnable ) + { + m_nFlags |= (T)nFlagMask; + } + else + { + m_nFlags &= ~((T)nFlagMask); + } +} + + +//----------------------------------------------------------------------------- +// Clear flags +//----------------------------------------------------------------------------- +template< class T > +void CUtlFlags::ClearFlag( int nFlagMask ) +{ + // Makes sure we didn't truncate + Assert( nFlagMask == (T)nFlagMask ); + m_nFlags &= ~((T)nFlagMask); +} + +template< class T > +void CUtlFlags::ClearAllFlags() +{ + m_nFlags = 0; +} + + +//----------------------------------------------------------------------------- +// Is a flag set? +//----------------------------------------------------------------------------- +template< class T > +bool CUtlFlags::IsFlagSet( int nFlagMask ) const +{ + // Makes sure we didn't truncate + Assert( nFlagMask == (T)nFlagMask ); + return ( m_nFlags & nFlagMask ) != 0; +} + + +//----------------------------------------------------------------------------- +// Is any flag set? +//----------------------------------------------------------------------------- +template< class T > +bool CUtlFlags::IsAnyFlagSet() const +{ + return m_nFlags != 0; +} + + +#endif // UTLFLAGS_H diff --git a/public/tier1/utlhandletable.h b/public/tier1/utlhandletable.h new file mode 100644 index 0000000..27764dd --- /dev/null +++ b/public/tier1/utlhandletable.h @@ -0,0 +1,586 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef UTLHANDLETABLE_H +#define UTLHANDLETABLE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utlvector.h" +#include "tier1/utlqueue.h" + + +//----------------------------------------------------------------------------- +// Handles are 32 bits. Invalid handles are all 1s +//----------------------------------------------------------------------------- +typedef unsigned int UtlHandle_t; +#define UTLHANDLE_INVALID ((UtlHandle_t)~0) + + +//----------------------------------------------------------------------------- +// Purpose: This is a table used to allocate handles +// HandleBits specifies the max # of simultaneously allocated handles. +// An extra bit is used for the validity state +// The rest of the 32 bits are used for a serial number +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +class CUtlHandleTable +{ +public: + CUtlHandleTable(); + + // Allocate, deallocate handles + UtlHandle_t AddHandle(); + void RemoveHandle( UtlHandle_t h ); + + // Set/get handle values + void SetHandle( UtlHandle_t h, T *pData ); + T *GetHandle( UtlHandle_t h ) const; + T *GetHandle( UtlHandle_t h, bool checkValidity ) const; + + // Is a handle valid? + bool IsHandleValid( UtlHandle_t h ) const; + + // Iterate over handles; they may not be valid + unsigned int GetValidHandleCount() const; + unsigned int GetHandleCount() const; + UtlHandle_t GetHandleFromIndex( int i ) const; + int GetIndexFromHandle( UtlHandle_t h ) const; + + void MarkHandleInvalid( UtlHandle_t h ); + void MarkHandleValid( UtlHandle_t h ); + +private: + struct HandleType_t + { + HandleType_t( unsigned int i, unsigned int s ) : nIndex( i ), nSerial( s ) + { + Assert( i < ( 1 << HandleBits ) ); + Assert( s < ( 1 << ( 31 - HandleBits ) ) ); + } + unsigned int nIndex : HandleBits; + unsigned int nSerial : 31 - HandleBits; + }; + + struct EntryType_t + { + EntryType_t() : m_nSerial( 0 ), nInvalid( 0 ), m_pData( 0 ) {} + unsigned int m_nSerial : 31; + unsigned int nInvalid : 1; + T *m_pData; + }; + + static unsigned int GetSerialNumber( UtlHandle_t handle ); + static unsigned int GetListIndex( UtlHandle_t handle ); + static UtlHandle_t CreateHandle( unsigned int nSerial, unsigned int nIndex ); + const EntryType_t *GetEntry( UtlHandle_t handle, bool checkValidity ) const; + + unsigned int m_nValidHandles; + CUtlVector< EntryType_t > m_list; + CUtlQueue< int > m_unused; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +CUtlHandleTable::CUtlHandleTable() : m_nValidHandles( 0 ) +{ +} + + +//----------------------------------------------------------------------------- +// Allocate, deallocate handles +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +UtlHandle_t CUtlHandleTable::AddHandle() +{ + unsigned int nIndex = ( m_unused.Count() > 0 ) ? m_unused.RemoveAtHead() : m_list.AddToTail(); + + EntryType_t &entry = m_list[ nIndex ]; + entry.nInvalid = 0; + entry.m_pData = NULL; + + ++m_nValidHandles; + + return CreateHandle( entry.m_nSerial, nIndex ); +} + +template< class T, int HandleBits > +void CUtlHandleTable::RemoveHandle( UtlHandle_t handle ) +{ + unsigned int nIndex = GetListIndex( handle ); + Assert( nIndex < ( unsigned int )m_list.Count() ); + if ( nIndex >= ( unsigned int )m_list.Count() ) + return; + + EntryType_t &entry = m_list[ nIndex ]; + ++entry.m_nSerial; // mark old serial# invalid + if ( !entry.nInvalid ) + { + entry.nInvalid = 1; + --m_nValidHandles; + } + entry.m_pData = NULL; + + + // If a handle has been used this many times, then we need to take it out of service, otherwise if the + // serial # wraps around we'll possibly revalidate old handles and they'll start to point at the wrong objects. Unlikely, but possible. + bool bStopUsing = ( entry.m_nSerial >= ( (1 << ( 31 - HandleBits ) ) - 1 ) ); + if ( !bStopUsing ) + { + m_unused.Insert( nIndex ); + } +} + + +//----------------------------------------------------------------------------- +// Set/get handle values +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +void CUtlHandleTable::SetHandle( UtlHandle_t handle, T *pData ) +{ + EntryType_t *entry = const_cast< EntryType_t* >( GetEntry( handle, false ) ); + Assert( entry ); + if ( entry == NULL ) + return; + + // Validate the handle + if ( entry->nInvalid ) + { + ++m_nValidHandles; + entry->nInvalid = 0; + } + entry->m_pData = pData; +} + +template< class T, int HandleBits > +T *CUtlHandleTable::GetHandle( UtlHandle_t handle ) const +{ + const EntryType_t *entry = GetEntry( handle, true ); + return entry ? entry->m_pData : NULL; +} + +template< class T, int HandleBits > +T *CUtlHandleTable::GetHandle( UtlHandle_t handle, bool checkValidity ) const +{ + const EntryType_t *entry = GetEntry( handle, checkValidity ); + return entry ? entry->m_pData : NULL; +} + + +//----------------------------------------------------------------------------- +// Is a handle valid? +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +bool CUtlHandleTable::IsHandleValid( UtlHandle_t handle ) const +{ + if ( handle == UTLHANDLE_INVALID ) + return false; + + unsigned int nIndex = GetListIndex( handle ); + AssertOnce( nIndex < ( unsigned int )m_list.Count() ); + if ( nIndex >= ( unsigned int )m_list.Count() ) + return false; + + const EntryType_t &entry = m_list[ nIndex ]; + if ( entry.m_nSerial != GetSerialNumber( handle ) ) + return false; + + if ( 1 == entry.nInvalid ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Current max handle +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +unsigned int CUtlHandleTable::GetValidHandleCount() const +{ + return m_nValidHandles; +} + +template< class T, int HandleBits > +unsigned int CUtlHandleTable::GetHandleCount() const +{ + return m_list.Count(); +} + +template< class T, int HandleBits > +UtlHandle_t CUtlHandleTable::GetHandleFromIndex( int i ) const +{ + if ( m_list[i].m_pData ) + return CreateHandle( m_list[i].m_nSerial, i ); + return UTLHANDLE_INVALID; +} + +template< class T, int HandleBits > +int CUtlHandleTable::GetIndexFromHandle( UtlHandle_t h ) const +{ + if ( h == UTLHANDLE_INVALID ) + return -1; + + return GetListIndex( h ); +} + + + +//----------------------------------------------------------------------------- +// Cracking handles into indices + serial numbers +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +unsigned int CUtlHandleTable::GetSerialNumber( UtlHandle_t handle ) +{ + return ( ( HandleType_t* )&handle )->nSerial; +} + +template< class T, int HandleBits > +unsigned int CUtlHandleTable::GetListIndex( UtlHandle_t handle ) +{ + return ( ( HandleType_t* )&handle )->nIndex; +} + +template< class T, int HandleBits > +UtlHandle_t CUtlHandleTable::CreateHandle( unsigned int nSerial, unsigned int nIndex ) +{ + HandleType_t h( nIndex, nSerial ); + return *( UtlHandle_t* )&h; +} + + +//----------------------------------------------------------------------------- +// Looks up a entry by handle +//----------------------------------------------------------------------------- +template< class T, int HandleBits > +const typename CUtlHandleTable::EntryType_t *CUtlHandleTable::GetEntry( UtlHandle_t handle, bool checkValidity ) const +{ + if ( handle == UTLHANDLE_INVALID ) + return NULL; + + unsigned int nIndex = GetListIndex( handle ); + Assert( nIndex < ( unsigned int )m_list.Count() ); + if ( nIndex >= ( unsigned int )m_list.Count() ) + return NULL; + + const EntryType_t &entry = m_list[ nIndex ]; + if ( entry.m_nSerial != GetSerialNumber( handle ) ) + return NULL; + + if ( checkValidity && + ( 1 == entry.nInvalid ) ) + return NULL; + + return &entry; +} + +template< class T, int HandleBits > +void CUtlHandleTable::MarkHandleInvalid( UtlHandle_t handle ) +{ + if ( handle == UTLHANDLE_INVALID ) + return; + + unsigned int nIndex = GetListIndex( handle ); + Assert( nIndex < ( unsigned int )m_list.Count() ); + if ( nIndex >= ( unsigned int )m_list.Count() ) + return; + + EntryType_t &entry = m_list[ nIndex ]; + if ( entry.m_nSerial != GetSerialNumber( handle ) ) + return; + + if ( !entry.nInvalid ) + { + --m_nValidHandles; + entry.nInvalid = 1; + } +} + +template< class T, int HandleBits > +void CUtlHandleTable::MarkHandleValid( UtlHandle_t handle ) +{ + if ( handle == UTLHANDLE_INVALID ) + return; + + unsigned int nIndex = GetListIndex( handle ); + Assert( nIndex < ( unsigned int )m_list.Count() ); + if ( nIndex >= ( unsigned int )m_list.Count() ) + return; + + EntryType_t &entry = m_list[ nIndex ]; + if ( entry.m_nSerial != GetSerialNumber( handle ) ) + return; + + if ( entry.nInvalid ) + { + ++m_nValidHandles; + entry.nInvalid = 0; + } +} + + +//----------------------------------------------------------------------------- +// Handle wrapper. Assumes 2 things +// 1) That class T has a non-static method called GetHandle which returns a UtlHandle_t +// 2) That class T has a static method called GetPtrFromHandle which returns a T* given a UtlHandle_t +// 3) That class T has a static method called IsHandleValid which accepts a UtlHandle_t +//----------------------------------------------------------------------------- +template< class T > +class CUtlHandle +{ +public: + // Constructors + CUtlHandle(); + explicit CUtlHandle( T *pObject ); + CUtlHandle( UtlHandle_t h ); + CUtlHandle( const CUtlHandle &h ); + + // Assignment + void Set( T *pObject ); + void Set( UtlHandle_t h ); + const CUtlHandle &operator=( UtlHandle_t h ); + const CUtlHandle &operator=( T *pObject ); + + // Retrieval + T *Get(); + const T* Get() const; + + // Is the handle valid? + bool IsValid() const; + + // Casting + operator T*(); + operator UtlHandle_t(); + operator bool(); + T* operator->(); + const T* operator->() const; + + // Equality + bool operator==( CUtlHandle h ) const; + bool operator==( T *pObject ) const; + bool operator==( UtlHandle_t h ) const; + bool operator!=( CUtlHandle h ) const; + bool operator!=( T *pObject ) const; + bool operator!=( UtlHandle_t h ) const; + +private: + UtlHandle_t m_handle; +}; + + +//----------------------------------------------------------------------------- +// Constructors +//----------------------------------------------------------------------------- +template< class T > +CUtlHandle::CUtlHandle() : m_handle( UTLHANDLE_INVALID ) +{ +} + +template< class T > +CUtlHandle::CUtlHandle( T *pObject ) +{ + Set( pObject ); +} + +template< class T > +CUtlHandle::CUtlHandle( UtlHandle_t h ) +{ + m_handle = h; +} + +template< class T > +CUtlHandle::CUtlHandle( const CUtlHandle &h ) +{ + m_handle = h.m_handle; +} + + +//----------------------------------------------------------------------------- +// Assignment +//----------------------------------------------------------------------------- +template< class T > +void CUtlHandle::Set( T *pObject ) +{ + // Assumes T has a member function GetHandle + m_handle = pObject ? pObject->GetHandle() : UTLHANDLE_INVALID; +} + +template< class T > +void CUtlHandle::Set( UtlHandle_t h ) +{ + m_handle = h; +} + +template< class T > +const CUtlHandle &CUtlHandle::operator=( UtlHandle_t h ) +{ + Set( h ); + return *this; +} + +template< class T > +const CUtlHandle &CUtlHandle::operator=( T *pObject ) +{ + Set( pObject ); + return *this; +} + + +//----------------------------------------------------------------------------- +// Is the handle valid? +//----------------------------------------------------------------------------- +template< class T > +bool CUtlHandle::IsValid() const +{ + // Assumes T has a static member function IsHandleValid + return T::IsHandleValid( m_handle ); +} + + +//----------------------------------------------------------------------------- +// Retrieval +//----------------------------------------------------------------------------- +template< class T > +T *CUtlHandle::Get() +{ + // Assumes T has a static member function GetPtrFromHandle + return T::GetPtrFromHandle( m_handle ); +} + +template< class T > +const T* CUtlHandle::Get() const +{ + // Assumes T has a static member function GetPtrFromHandle + return T::GetPtrFromHandle( m_handle ); +} + + +//----------------------------------------------------------------------------- +// Casting +//----------------------------------------------------------------------------- +template< class T > +CUtlHandle::operator T*() +{ + return Get(); +} + +template< class T > +CUtlHandle::operator UtlHandle_t() +{ + return m_handle; +} + +template< class T > +T* CUtlHandle::operator->() +{ + return Get(); +} + +template< class T > +const T* CUtlHandle::operator->() const +{ + return Get(); +} + +template< class T > +CUtlHandle::operator bool() +{ + return m_handle != UTLHANDLE_INVALID; +} + + +//----------------------------------------------------------------------------- +// Equality +//----------------------------------------------------------------------------- +template< class T > +bool CUtlHandle::operator==( CUtlHandle h ) const +{ + return m_handle == h.m_handle; +} + +template< class T > +bool CUtlHandle::operator==( T *pObject ) const +{ + UtlHandle_t h = pObject ? pObject->GetHandle() : UTLHANDLE_INVALID; + return m_handle == h; +} + +template< class T > +bool CUtlHandle::operator==( UtlHandle_t h ) const +{ + return m_handle == h; +} + +template< class T > +bool CUtlHandle::operator!=( CUtlHandle h ) const +{ + return m_handle != h.m_handle; +} + +template< class T > +bool CUtlHandle::operator!=( T *pObject ) const +{ + UtlHandle_t h = pObject ? pObject->GetHandle() : UTLHANDLE_INVALID; + return m_handle != h; +} + +template< class T > +bool CUtlHandle::operator!=( UtlHandle_t h ) const +{ + return m_handle != h; +} + + +//----------------------------------------------------------------------------- +// Add this macro to a class definition to hook in handles for it! +//----------------------------------------------------------------------------- +#define DECLARE_HANDLES( _className, _handleBitCount ) \ + public: \ + UtlHandle_t GetHandle() \ + { \ + return m_Handle; \ + } \ + static _className* GetPtrFromHandle( UtlHandle_t h ) \ + { \ + return m_HandleTable.GetHandle( h ); \ + } \ + static bool IsHandleValid( UtlHandle_t h ) \ + { \ + return m_HandleTable.IsHandleValid( h ); \ + } \ + private: \ + UtlHandle_t m_Handle; \ + static CUtlHandleTable< _className, _handleBitCount > m_HandleTable + + +//----------------------------------------------------------------------------- +// Add this macro to a .cpp file to hook in handles for it! +//----------------------------------------------------------------------------- +#define IMPLEMENT_HANDLES( _className, _handleBitCount ) \ + CUtlHandleTable< _className, _handleBitCount > _className::m_HandleTable; + + +//----------------------------------------------------------------------------- +// Add these macro to the class constructor + destructor +//----------------------------------------------------------------------------- +#define CONSTRUCT_HANDLE( ) \ + m_Handle = m_HandleTable.AddHandle(); \ + m_HandleTable.SetHandle( m_Handle, this ) + +#define DESTRUCT_HANDLE() \ + m_HandleTable.RemoveHandle( m_Handle ); \ + m_Handle = UTLHANDLE_INVALID + + + +#endif // UTLHANDLETABLE_H + diff --git a/public/tier1/utlhash.h b/public/tier1/utlhash.h new file mode 100644 index 0000000..80ccc38 --- /dev/null +++ b/public/tier1/utlhash.h @@ -0,0 +1,1299 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLHASH_H +#define UTLHASH_H +#pragma once + +#include +#include "utlmemory.h" +#include "utlvector.h" +#include "utllinkedlist.h" +#include "utllinkedlist.h" +#include "commonmacros.h" +#include "generichash.h" + +typedef unsigned int UtlHashHandle_t; + +template +class CUtlHash +{ +public: + // compare and key functions - implemented by the + typedef C CompareFunc_t; + typedef K KeyFunc_t; + + // constructor/deconstructor + CUtlHash( int bucketCount = 0, int growCount = 0, int initCount = 0, + CompareFunc_t compareFunc = 0, KeyFunc_t keyFunc = 0 ); + ~CUtlHash(); + + // invalid handle + static UtlHashHandle_t InvalidHandle( void ) { return ( UtlHashHandle_t )~0; } + bool IsValidHandle( UtlHashHandle_t handle ) const; + + // size + int Count( void ) const; + + // memory + void Purge( void ); + + // insertion methods + UtlHashHandle_t Insert( Data const &src ); + UtlHashHandle_t Insert( Data const &src, bool *pDidInsert ); + UtlHashHandle_t AllocEntryFromKey( Data const &src ); + + // removal methods + void Remove( UtlHashHandle_t handle ); + void RemoveAll(); + + // retrieval methods + UtlHashHandle_t Find( Data const &src ) const; + + Data &Element( UtlHashHandle_t handle ); + Data const &Element( UtlHashHandle_t handle ) const; + Data &operator[]( UtlHashHandle_t handle ); + Data const &operator[]( UtlHashHandle_t handle ) const; + + UtlHashHandle_t GetFirstHandle() const; + UtlHashHandle_t GetNextHandle( UtlHashHandle_t h ) const; + + // debugging!! + void Log( const char *filename ); + void Dump(); + +protected: + + int GetBucketIndex( UtlHashHandle_t handle ) const; + int GetKeyDataIndex( UtlHashHandle_t handle ) const; + UtlHashHandle_t BuildHandle( int ndxBucket, int ndxKeyData ) const; + + bool DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const; + +protected: + + // handle upper 16 bits = bucket index (bucket heads) + // handle lower 16 bits = key index (bucket list) + typedef CUtlVector HashBucketList_t; + CUtlVector m_Buckets; + + CompareFunc_t m_CompareFunc; // function used to handle unique compares on data + KeyFunc_t m_KeyFunc; // function used to generate the key value + + bool m_bPowerOfTwo; // if the bucket value is a power of two, + unsigned int m_ModMask; // use the mod mask to "mod" +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::CUtlHash( int bucketCount, int growCount, int initCount, + CompareFunc_t compareFunc, KeyFunc_t keyFunc ) : + m_CompareFunc( compareFunc ), + m_KeyFunc( keyFunc ) +{ + bucketCount = MIN(bucketCount, 65536); + m_Buckets.SetSize( bucketCount ); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].SetSize( initCount ); + m_Buckets[ndxBucket].SetGrowSize( growCount ); + } + + // check to see if the bucket count is a power of 2 and set up + // optimizations appropriately + m_bPowerOfTwo = IsPowerOfTwo( bucketCount ); + m_ModMask = m_bPowerOfTwo ? (bucketCount-1) : 0; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::~CUtlHash() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::IsValidHandle( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + // ndxBucket and ndxKeyData can't possibly be less than zero -- take a + // look at the definition of the Get..Index functions for why. However, + // if you override those functions, you will need to override this one + // as well. + if( /*( ndxBucket >= 0 ) && */ ( ndxBucket < m_Buckets.Count() ) ) + { + if( /*( ndxKeyData >= 0 ) && */ ( ndxKeyData < m_Buckets[ndxBucket].Count() ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::Count( void ) const +{ + int count = 0; + + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + count += m_Buckets[ndxBucket].Count(); + } + + return count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetBucketIndex( UtlHashHandle_t handle ) const +{ + return ( ( ( handle >> 16 ) & 0x0000ffff ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetKeyDataIndex( UtlHashHandle_t handle ) const +{ + return ( handle & 0x0000ffff ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::BuildHandle( int ndxBucket, int ndxKeyData ) const +{ + Assert( ( ndxBucket >= 0 ) && ( ndxBucket < 65536 ) ); + Assert( ( ndxKeyData >= 0 ) && ( ndxKeyData < 65536 ) ); + + UtlHashHandle_t handle = ndxKeyData; + handle |= ( ndxBucket << 16 ); + + return handle; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Purge( void ) +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].Purge(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const +{ + // generate the data "key" + unsigned int key = m_KeyFunc( src ); + + // hash the "key" - get the correct hash table "bucket" + unsigned int ndxBucket; + if( m_bPowerOfTwo ) + { + *pBucket = ndxBucket = ( key & m_ModMask ); + } + else + { + int bucketCount = m_Buckets.Count(); + *pBucket = ndxBucket = key % bucketCount; + } + + int ndxKeyData; + const CUtlVector &bucket = m_Buckets[ndxBucket]; + int keyDataCount = bucket.Count(); + for( ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++ ) + { + if( m_CompareFunc( bucket.Element( ndxKeyData ), src ) ) + break; + } + + if( ndxKeyData == keyDataCount ) + return false; + + *pIndex = ndxKeyData; + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Find( Data const &src ) const +{ + unsigned int ndxBucket; + int ndxKeyData; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + return ( InvalidHandle() ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src, bool *pDidInsert ) +{ + unsigned int ndxBucket; + int ndxKeyData; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + *pDidInsert = false; + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + *pDidInsert = true; + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::AllocEntryFromKey( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail(); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Remove( UtlHashHandle_t handle ) +{ + Assert( IsValidHandle( handle ) ); + + // check to see if the bucket exists + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + if( m_Buckets[ndxBucket].IsValidIndex( ndxKeyData ) ) + { + m_Buckets[ndxBucket].FastRemove( ndxKeyData ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::RemoveAll() +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].RemoveAll(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::Element( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::Element( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::operator[]( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::operator[]( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::GetFirstHandle() const +{ + return GetNextHandle( ( UtlHashHandle_t )-1 ); +} + +template +inline UtlHashHandle_t CUtlHash::GetNextHandle( UtlHashHandle_t handle ) const +{ + ++handle; // start at the first possible handle after the one given + + int bi = GetBucketIndex( handle ); + int ki = GetKeyDataIndex( handle ); + + int nBuckets = m_Buckets.Count(); + for ( ; bi < nBuckets; ++bi ) + { + if ( ki < m_Buckets[ bi ].Count() ) + return BuildHandle( bi, ki ); + + ki = 0; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Log( const char *filename ) +{ + FILE *pDebugFp; + pDebugFp = fopen( filename, "w" ); + if( !pDebugFp ) + return; + + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + fprintf( pDebugFp, "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + fprintf( pDebugFp, "Bucket %d: %d\n", ndxBucket, count ); + } + + fprintf( pDebugFp, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + fprintf( pDebugFp, "Max Bucket Size: %d\n", maxBucketSize ); + + fclose( pDebugFp ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Dump( ) +{ + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + Msg( "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + Msg( "Bucket %d: %d\n", ndxBucket, count ); + } + + Msg( "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + Msg( "Max Bucket Size: %d\n", maxBucketSize ); +} + +//============================================================================= +// +// Fast Hash +// +// Number of buckets must be a power of 2. +// Key must be 32-bits (unsigned int). +// +typedef int UtlHashFastHandle_t; + +#define UTLHASH_POOL_SCALAR 2 + +class CUtlHashFastNoHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( key & bucketMask ); + } +}; + +class CUtlHashFastGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( HashIntConventional( key ) & bucketMask ); + } +}; + +template +class CUtlHashFast +{ +public: + + // Constructor/Deconstructor. + CUtlHashFast(); + ~CUtlHashFast(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFastHandle_t InvalidHandle( void ) { return ( UtlHashFastHandle_t )~0; } + inline bool IsValidHandle( UtlHashFastHandle_t hHash ) const; + + // Initialize. + bool Init( int nBucketCount ); + + // Size not available; count is meaningless for multilists. + // int Count( void ) const; + + // Insertion. + UtlHashFastHandle_t Insert( unsigned int uiKey, const Data &data ); + UtlHashFastHandle_t FastInsert( unsigned int uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFastHandle_t Find( unsigned int uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ); + Data const &Element( UtlHashFastHandle_t hHash ) const; + Data &operator[]( UtlHashFastHandle_t hHash ); + Data const &operator[]( UtlHashFastHandle_t hHash ) const; + + // Iteration + struct UtlHashFastIterator_t + { + int bucket; + UtlHashFastHandle_t handle; + + UtlHashFastIterator_t(int _bucket, const UtlHashFastHandle_t &_handle) + : bucket(_bucket), handle(_handle) {}; + // inline operator UtlHashFastHandle_t() const { return handle; }; + }; + inline UtlHashFastIterator_t First() const; + inline UtlHashFastIterator_t Next( const UtlHashFastIterator_t &hHash ) const; + inline bool IsValidIterator( const UtlHashFastIterator_t &iter ) const; + inline Data &operator[]( const UtlHashFastIterator_t &iter ) { return (*this)[iter.handle]; } + inline Data const &operator[]( const UtlHashFastIterator_t &iter ) const { return (*this)[iter.handle]; } + +//protected: + + // Templatized for memory tracking purposes + template + struct HashFastData_t_ + { + unsigned int m_uiKey; + HashData m_Data; + }; + + typedef HashFastData_t_ HashFastData_t; + + unsigned int m_uiBucketMask; + CUtlVector m_aBuckets; + CUtlFixedLinkedList m_aDataPool; + +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFast::CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFast::~CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Purge( void ) +{ + m_aBuckets.Purge(); + m_aDataPool.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize the hash - set bucket count and hash grow amount. +//----------------------------------------------------------------------------- +template bool CUtlHashFast::Init( int nBucketCount ) +{ + // Verify the bucket count is power of 2. + if ( !IsPowerOfTwo( nBucketCount ) ) + return false; + + // Set the bucket size. + m_aBuckets.SetSize( nBucketCount ); + for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket ) + { + m_aBuckets[iBucket] = m_aDataPool.InvalidIndex(); + } + + // Set the mod mask. + m_uiBucketMask = nBucketCount - 1; + + // Calculate the grow size. + int nGrowSize = UTLHASH_POOL_SCALAR * nBucketCount; + m_aDataPool.SetGrowSize( nGrowSize ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +// Not available because count isn't accurately maintained for multilists. +//----------------------------------------------------------------------------- +/* +template inline int CUtlHashFast::Count( void ) const +{ + return m_aDataPool.Count(); +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Insert( unsigned int uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFastHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::FastInsert( unsigned int uiKey, const Data &data ) +{ + // Get a new element from the pool. + int iHashData = m_aDataPool.Alloc( true ); + HashFastData_t *pHashData = &m_aDataPool[iHashData]; + if ( !pHashData ) + return InvalidHandle(); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + // Link element. + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + m_aDataPool.LinkBefore( m_aBuckets[iBucket], iHashData ); + m_aBuckets[iBucket] = iHashData; + + return iHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Remove( UtlHashFastHandle_t hHash ) +{ + int iBucket = HashFuncs::Hash( m_aDataPool[hHash].m_uiKey, m_uiBucketMask ); + if ( m_aBuckets[iBucket] == hHash ) + { + // It is a bucket head. + m_aBuckets[iBucket] = m_aDataPool.Next( hHash ); + } + else + { + // Not a bucket head. + m_aDataPool.Unlink( hHash ); + } + + // Remove the element. + m_aDataPool.Remove( hHash ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::RemoveAll( void ) +{ + m_aBuckets.RemoveAll(); + m_aDataPool.RemoveAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Find( unsigned int uiKey ) const +{ + // hash the "key" - get the correct hash table "bucket" + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + + for ( int iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) ) + { + if ( m_aDataPool[iElement].m_uiKey == uiKey ) + return iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the index of the first element +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::First() const +{ + // walk through the buckets to find the first one that has some data + int bucketCount = m_aBuckets.Count(); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + for ( int bucket = 0 ; bucket < bucketCount ; ++bucket ) + { + UtlHashFastHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket + if (iElement != invalidIndex) + return UtlHashFastIterator_t(bucket,iElement); + } + + // if we are down here, the list is empty + return UtlHashFastIterator_t(-1, invalidIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the next element after +// the param one. Or an invalid iterator. +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::Next( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + // look for the next entry in the current bucket + UtlHashFastHandle_t next = m_aDataPool.Next(iter.handle); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + if (next != invalidIndex) + { + // this bucket still has more elements in it + return UtlHashFastIterator_t(iter.bucket, next); + } + + // otherwise look for the next bucket with data + int bucketCount = m_aBuckets.Count(); + for ( int bucket = iter.bucket+1 ; bucket < bucketCount ; ++bucket ) + { + UtlHashFastHandle_t next = m_aBuckets[bucket]; // get the head of the bucket + if (next != invalidIndex) + return UtlHashFastIterator_t( bucket, next ); + } + + // if we're here, there's no more data to be had + return UtlHashFastIterator_t(-1, invalidIndex); +} + +template + bool CUtlHashFast::IsValidIterator( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) ); +} + + +template inline bool CUtlHashFast::IsValidHandle( UtlHashFastHandle_t hHash ) const +{ + return m_aDataPool.IsValidIndex(hHash); +} + +//============================================================================= +// +// Fixed Hash +// +// Number of buckets must be a power of 2. +// Key must be 32-bits (unsigned int). +// +typedef int UtlHashFixedHandle_t; + +template +class CUtlHashFixedGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + int hash = HashIntConventional( key ); + if ( NUM_BUCKETS <= USHRT_MAX ) + { + hash ^= ( hash >> 16 ); + } + if ( NUM_BUCKETS <= UCHAR_MAX ) + { + hash ^= ( hash >> 8 ); + } + return ( hash & bucketMask ); + } +}; + +template +class CUtlHashFixed +{ +public: + + // Constructor/Deconstructor. + CUtlHashFixed(); + ~CUtlHashFixed(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFixedHandle_t InvalidHandle( void ) { return ( UtlHashFixedHandle_t )~0; } + + // Size. + int Count( void ); + + // Insertion. + UtlHashFixedHandle_t Insert( unsigned int uiKey, const Data &data ); + UtlHashFixedHandle_t FastInsert( unsigned int uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFixedHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFixedHandle_t Find( unsigned int uiKey ); + + Data &Element( UtlHashFixedHandle_t hHash ); + Data const &Element( UtlHashFixedHandle_t hHash ) const; + Data &operator[]( UtlHashFixedHandle_t hHash ); + Data const &operator[]( UtlHashFixedHandle_t hHash ) const; + + //protected: + + // Templatized for memory tracking purposes + template + struct HashFixedData_t_ + { + unsigned int m_uiKey; + Data_t m_Data; + }; + + typedef HashFixedData_t_ HashFixedData_t; + + enum + { + BUCKET_MASK = NUM_BUCKETS - 1 + }; + CUtlPtrLinkedList m_aBuckets[NUM_BUCKETS]; + int m_nElements; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::~CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Purge( void ) +{ + RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +//----------------------------------------------------------------------------- +template inline int CUtlHashFixed::Count( void ) +{ + return m_nElements; +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Insert( unsigned int uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFixedHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::FastInsert( unsigned int uiKey, const Data &data ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + UtlPtrLinkedListIndex_t iElem = m_aBuckets[iBucket].AddToHead(); + + HashFixedData_t *pHashData = &m_aBuckets[iBucket][iElem]; + + Assert( (UtlPtrLinkedListIndex_t)pHashData == iElem ); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + m_nElements++; + return (UtlHashFixedHandle_t)pHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Remove( UtlHashFixedHandle_t hHash ) +{ + HashFixedData_t *pHashData = (HashFixedData_t *)hHash; + Assert( Find(pHashData->m_uiKey) != InvalidHandle() ); + int iBucket = HashFuncs::Hash( pHashData->m_uiKey, NUM_BUCKETS - 1 ); + m_aBuckets[iBucket].Remove( (UtlPtrLinkedListIndex_t)pHashData ); + m_nElements--; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::RemoveAll( void ) +{ + for ( int i = 0; i < NUM_BUCKETS; i++ ) + { + m_aBuckets[i].RemoveAll(); + } + m_nElements = 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Find( unsigned int uiKey ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + CUtlPtrLinkedList &bucket = m_aBuckets[iBucket]; + + for ( UtlPtrLinkedListIndex_t iElement = bucket.Head(); iElement != bucket.InvalidIndex(); iElement = bucket.Next( iElement ) ) + { + if ( bucket[iElement].m_uiKey == uiKey ) + return (UtlHashFixedHandle_t)iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +class CDefaultHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return HashIntConventional(nKey); } +}; + +class CPassthroughHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return nKey; } +}; + + +// This is a simpler hash for scalar types that stores the entire hash + buckets in a single linear array +// This is much more cache friendly for small (e.g. 32-bit) types stored in the hash +template +class CUtlScalarHash +{ +public: + + // Constructor/Destructor. + CUtlScalarHash(); + ~CUtlScalarHash(); + + // Memory. + // void Purge( void ); + + // Invalid handle. + static const UtlHashFastHandle_t InvalidHandle( void ) { return (unsigned int)~0; } + + // Initialize. + bool Init( int nBucketCount ); + + // Size. + int Count( void ) const { return m_dataCount; } + + // Insertion. + UtlHashFastHandle_t Insert( unsigned int uiKey, const Data &data ); + + // Removal. + void FindAndRemove( unsigned int uiKey, const Data &dataRecord ); + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + void Grow(); + + // Retrieval. Finds by uiKey and then by comparing dataRecord + UtlHashFastHandle_t Find( unsigned int uiKey, const Data &dataRecord ) const; + UtlHashFastHandle_t FindByUniqueKey( unsigned int uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &Element( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data &operator[]( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &operator[]( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + + unsigned int Key( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_uiKey; } + + UtlHashFastHandle_t FirstInorder() const + { + return NextInorder(-1); + } + UtlHashFastHandle_t NextInorder( UtlHashFastHandle_t nStart ) const + { + int nElementCount = m_maxData * 2; + unsigned int nUnusedListElement = (unsigned int)InvalidHandle(); + for ( int i = nStart+1; i < nElementCount; i++ ) + { + if ( m_pData[i].m_uiKey != nUnusedListElement ) + return i; + } + return nUnusedListElement; + } + + //protected: + + struct HashScalarData_t + { + unsigned int m_uiKey; + Data m_Data; + }; + + unsigned int m_uiBucketMask; + HashScalarData_t *m_pData; + int m_maxData; + int m_dataCount; +}; + +template CUtlScalarHash::CUtlScalarHash() +{ + m_pData = NULL; + m_uiBucketMask = 0; + m_maxData = 0; + m_dataCount = 0; +} + +template CUtlScalarHash::~CUtlScalarHash() +{ + delete[] m_pData; +} + +template bool CUtlScalarHash::Init( int nBucketCount ) +{ + Assert(m_dataCount==0); + m_maxData = SmallestPowerOfTwoGreaterOrEqual(nBucketCount); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount - 1; + RemoveAll(); + return true; +} + +template void CUtlScalarHash::Grow() +{ + ASSERT_NO_REENTRY(); + int oldElementCount = m_maxData * 2; + HashScalarData_t *pOldData = m_pData; + + // Grow to a minimum size of 16 + m_maxData = MAX( oldElementCount, 16 ); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount-1; + m_dataCount = 0; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = InvalidHandle(); + } + for ( int i = 0; i < oldElementCount; i++ ) + { + if ( pOldData[i].m_uiKey != (unsigned)InvalidHandle() ) + { + Insert( pOldData[i].m_uiKey, pOldData[i].m_Data ); + } + } + delete[] pOldData; +} + +template UtlHashFastHandle_t CUtlScalarHash::Insert( unsigned int uiKey, const Data &data ) +{ + if ( m_dataCount >= m_maxData ) + { + Grow(); + } + m_dataCount++; + Assert(uiKey != (uint)InvalidHandle()); // This hash stores less data by assuming uiKey != ~0 + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + index = (index+1) & m_uiBucketMask; + } + m_pData[index].m_uiKey = uiKey; + m_pData[index].m_Data = data; + + return index; +} + +// Removal. +template void CUtlScalarHash::Remove( UtlHashFastHandle_t hHash ) +{ + int mid = (m_uiBucketMask+1) / 2; + int lastRemoveIndex = hHash; + // remove the item + m_pData[lastRemoveIndex].m_uiKey = InvalidHandle(); + m_dataCount--; + + // now search for any items needing to be swapped down + unsigned int endOfList = (unsigned int)InvalidHandle(); + for ( int index = (hHash+1) & m_uiBucketMask; m_pData[index].m_uiKey != endOfList; index = (index+1) & m_uiBucketMask ) + { + int ideal = CHashFunction::HashKey32(m_pData[index].m_uiKey) & m_uiBucketMask; + + // is the ideal index for this element <= (in a wrapped buffer sense) the ideal index of the removed element? + // if so, swap + int diff = ideal - lastRemoveIndex; + if ( diff > mid ) + { + diff -= (m_uiBucketMask+1); + } + if ( diff < -mid ) + { + diff += (m_uiBucketMask+1); + } + + // should I swap this? + if ( diff <= 0 ) + { + m_pData[lastRemoveIndex] = m_pData[index]; + lastRemoveIndex = index; + m_pData[index].m_uiKey = InvalidHandle(); + } + } +} + +template void CUtlScalarHash::FindAndRemove( unsigned int uiKey, const Data &dataRecord ) +{ + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + { + Remove(index); + return; + } + index = (index+1) & m_uiBucketMask; + } +} + +template void CUtlScalarHash::RemoveAll( void ) +{ + int elementCount = m_maxData * 2; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = (unsigned)InvalidHandle(); + } + m_dataCount = 0; +} + +// Retrieval. +template UtlHashFastHandle_t CUtlScalarHash::Find( unsigned int uiKey, const Data &dataRecord ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + +template UtlHashFastHandle_t CUtlScalarHash::FindByUniqueKey( unsigned int uiKey ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + + +#endif // UTLHASH_H diff --git a/public/tier1/utlhashdict.h b/public/tier1/utlhashdict.h new file mode 100644 index 0000000..c7e6bf1 --- /dev/null +++ b/public/tier1/utlhashdict.h @@ -0,0 +1,342 @@ +//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef UTLHASHDICT_H +#define UTLHASHDICT_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier1/utlhash.h" +#include "tier1/generichash.h" +#include "mathlib/mathlib.h" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template +class CUtlHashDict +{ +public: + // constructor, destructor + CUtlHashDict( int bucketCount = 16, int growCount = 0, int initCount = 0 ); + ~CUtlHashDict( ); + + // gets particular elements + T& Element( unsigned i ); + const T& Element( unsigned i ) const; + T& operator[]( unsigned i ); + const T& operator[]( unsigned i ) const; + + // gets element names + char const *GetElementName( unsigned i ) const; + + // Number of elements + int Count() const; + + // Checks if a node is valid and in the tree + bool IsValidIndex( unsigned i ) const; + + // Invalid index + static unsigned InvalidHandle(); + + // Insert method (inserts in order) + unsigned Insert( const char *pName, const T &element ); + unsigned Insert( const char *pName ); + + // Find method + unsigned Find( const char *pName ) const; + + // Remove methods + void RemoveAt( unsigned i ); + void Remove( const char *pName ); + void RemoveAll( ); + + // Purge memory + void Purge(); + void PurgeAndDeleteElements(); // Call delete on each element. + + // Iteration methods + unsigned First() const; + unsigned Next( unsigned i ) const; + +protected: + struct Entry_t + { + const char *pszSymbol; + T value; + }; + + template + class CCompare + { + public: + CCompare( int ignored ) {} + + bool operator()( const Entry_t &entry1, const Entry_t &entry2 ) const + { + return !( ( bCaseIgnore ) ? stricmp( entry1.pszSymbol, entry2.pszSymbol ) : strcmp( entry1.pszSymbol, entry2.pszSymbol ) ); + } + }; + + template + class CHash + { + public: + CHash( int ignored ) {} + + unsigned operator()( const Entry_t &entry ) const + { + return !( ( bCaseIgnore ) ? HashStringCaseless( entry.pszSymbol ) : HashString( entry.pszSymbol ) ); + } + }; + + typedef CUtlHash, CHash > CHashTable; + CHashTable m_Elements; + int m_nCount; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template +CUtlHashDict::CUtlHashDict( int bucketCount = 16, int growCount = 0, int initCount = 0 ) : + m_Elements( SmallestPowerOfTwoGreaterOrEqual(bucketCount), growCount, initCount ) +{ + Assert( SmallestPowerOfTwoGreaterOrEqual(bucketCount) <= 0xffff ); +} + +template +CUtlHashDict::~CUtlHashDict() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlHashDict::Element( unsigned i ) +{ + return m_Elements[i].value; +} + +template +inline const T& CUtlHashDict::Element( unsigned i ) const +{ + return m_Elements[i].value; +} + +//----------------------------------------------------------------------------- +// gets element names +//----------------------------------------------------------------------------- +template +inline char const *CUtlHashDict::GetElementName( unsigned i ) const +{ + return m_Elements[i].pszSymbol; +} + +template +inline T& CUtlHashDict::operator[]( unsigned i ) +{ + return m_Elements[i].value; +} + +template +inline const T & CUtlHashDict::operator[]( unsigned i ) const +{ + return m_Elements[i].value; +} + +//----------------------------------------------------------------------------- +// Num elements +//----------------------------------------------------------------------------- +template +inline int CUtlHashDict::Count() const +{ + Assert( m_nCount == m_Elements.Count() ); + return m_nCount; +} + + +//----------------------------------------------------------------------------- +// Checks if a node is valid and in the tree +//----------------------------------------------------------------------------- +template +inline bool CUtlHashDict::IsValidIndex( unsigned i ) const +{ + return m_Elements.IsValidHandle(i); +} + + +//----------------------------------------------------------------------------- +// Invalid index +//----------------------------------------------------------------------------- +template +inline unsigned CUtlHashDict::InvalidHandle() +{ + return CHashTable::InvalidHandle(); +} + + +//----------------------------------------------------------------------------- +// Delete a node from the tree +//----------------------------------------------------------------------------- +template +void CUtlHashDict::RemoveAt(unsigned elem) +{ + if ( bDupeStrings ) + { + free( (void *)m_Elements[elem].pszSymbol ); + } + m_Elements.Remove(elem); + m_nCount--; +} + + +//----------------------------------------------------------------------------- +// remove a node in the tree +//----------------------------------------------------------------------------- +template void CUtlHashDict::Remove( const char *search ) +{ + unsigned node = Find( search ); + if (node != InvalidHandle()) + { + RemoveAt(node); + } +} + + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree +//----------------------------------------------------------------------------- +template +void CUtlHashDict::RemoveAll() +{ + if ( bDupeStrings ) + { + UtlHashHandle_t index = m_Elements.GetFirstHandle(); + while ( index != m_Elements.InvalidHandle() ) + { + free( (void *)m_Elements[index].pszSymbol ); + index = m_Elements.GetNextHandle( index ); + } + } + + m_Elements.RemoveAll(); + m_nCount = 0; +} + +template +void CUtlHashDict::Purge() +{ + if ( bDupeStrings ) + { + UtlHashHandle_t index = m_Elements.GetFirstHandle(); + while ( index != m_Elements.InvalidHandle() ) + { + free( (void *)m_Elements[index].pszSymbol ); + index = m_Elements.GetNextHandle( index ); + } + } + + m_Elements.Purge(); + m_nCount = 0; +} + + +template +void CUtlHashDict::PurgeAndDeleteElements() +{ + // Delete all the elements. + unsigned index = m_Elements.GetFirstHandle(); + while ( index != m_Elements.InvalidHandle() ) + { + if ( bDupeStrings ) + { + free( (void *)m_Elements[index].pszSymbol ); + } + delete m_Elements[index].value; + index = m_Elements.GetNextHandle( index ); + } + + m_Elements.RemoveAll(); + m_nCount = 0; +} + + +//----------------------------------------------------------------------------- +// inserts a node into the tree +//----------------------------------------------------------------------------- +template +unsigned CUtlHashDict::Insert( const char *pName, const T &element ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_nCount++; + Entry_t entry = + { + (bDupeStrings) ? strdup( pName ) : pName, + element + }; + bool bInserted; + unsigned result = m_Elements.Insert( entry, &bInserted ); + if ( bDupeStrings && !bInserted ) + { + free( (void *)entry.pszSymbol ); + } + return result; +} + +template +unsigned CUtlHashDict::Insert( const char *pName ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_nCount++; + Entry_t entry = + { + (bDupeStrings) ? strdup( pName ) : pName + }; + bool bInserted; + unsigned result = m_Elements.Insert( entry, &bInserted ); + if ( bDupeStrings && !bInserted ) + { + free( (void *)entry.pszSymbol ); + } + return result; +} + + +//----------------------------------------------------------------------------- +// finds a node in the tree +//----------------------------------------------------------------------------- +template +unsigned CUtlHashDict::Find( const char *pName ) const +{ + MEM_ALLOC_CREDIT_CLASS(); + if ( pName ) + return m_Elements.Find( *((Entry_t *)&pName) ); + else + return InvalidHandle(); +} + + +//----------------------------------------------------------------------------- +// Iteration methods +//----------------------------------------------------------------------------- +template +unsigned CUtlHashDict::First() const +{ + return m_Elements.GetFirstHandle(); +} + +template +unsigned CUtlHashDict::Next( unsigned i ) const +{ + return m_Elements.GetNextHandle(i); +} + +#endif // UTLHASHDICT_H diff --git a/public/tier1/utlintrusivelist.h b/public/tier1/utlintrusivelist.h new file mode 100644 index 0000000..048d6b9 --- /dev/null +++ b/public/tier1/utlintrusivelist.h @@ -0,0 +1,1064 @@ +//===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: Intrusive linked list templates, both for singly and doubly linked lists +// +// $Revision: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTILINTRUSIVELIST_H +#define UTILINTRUSIVELIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/basetypes.h" +#include "utlmemory.h" +#include "tier0/dbg.h" +#include "tier1/generichash.h" +#include "tier0/threadtools.h" + + +// +// These templates are used for intrusive linked list classes. Intrusive linked list templates +// force the structs and classes contained within them to have their own m_pNext, (optionally), +// m_pPrev, and other fields contained within. All memory management is up to the caller and their +// classes. No data will ever be copied. Nodes can only exist on one list at a time, because of +// only having on m_Next field, and manipulating the list while walking it requires that care on +// the part of the caller. All accessing and searching functions work by passing and returning +// pointers. +// +// +// +// naming and field conventions: +// functions referring to a DList are for doubly linked lists. nodes must have m_pHead and +// m_pPrev pointer fields. +// Functions using Priority require an m_Priority field, which must be comparable. +// +// Some functions are mean for use with lists which maintain both a head and tail pointer +// in order to support fast adding to the end. + + +/// validates that the doubly linked list has the proper structure, pointer-wise +//#define SUPERSLOW_DEBUG_VERSION + +namespace IntrusiveList +{ +#ifdef SUPERSLOW_DEBUG_VERSION + template inline void ValidateDList(T *head) + { + if (head) + { + Assert(head->m_pPrev==0); + } + while(head) + { + if (head->m_pNext) + { + Assert(head->m_pNext->m_pPrev==head); + } + if (head->m_pPrev) + { + Assert(head->m_pPrev->m_pNext==head); + } + head=head->m_pNext; + } + } +#else + template inline void ValidateDList(T * /*head*/) + { + } +#endif + + + +// move a node in a doubly linked list backwards one step. + template inline void MoveDNodeBackwards( T *which, T * &head) + { + if (which->m_pPrev) + { + T *p=which->m_pPrev; + T *pp=p->m_pPrev; + T *n=which->m_pNext; + Assert(p->m_pNext == which); + if (n) + { + Assert(n->m_pPrev==which); + n->m_pPrev=p; + } + if (pp) + { + Assert(pp->m_pNext==p); + pp->m_pNext=which; + } + else + { + head=which; // this node is the new root! + } + which->m_pNext=p; + which->m_pPrev=pp; + p->m_pNext=n; + p->m_pPrev=which; + } + ValidateDList(head); + } + + + + // removes node 'which' from doubly linked list with 'head' + template inline void RemoveFromDList(T * &head, T *which) + { + if (which->m_pPrev) + { + Assert(which->m_pPrev->m_pNext==which); + which->m_pPrev->m_pNext=which->m_pNext; + if (which->m_pNext) + { + Assert(which->m_pNext->m_pPrev==which); + which->m_pNext->m_pPrev=which->m_pPrev; + } + } + else + { + if (head==which) + { + head=which->m_pNext; + if (head) + { + Assert(head->m_pPrev==which); + head->m_pPrev=0; + } + } + } + which->m_pNext=which->m_pPrev=0; + ValidateDList(head); + + } + + //checks to see if node is in doubly linked list + template bool OnDList(T const *head, T const *which) + { + return (head==which) || (which->m_pNext !=0) || (which->m_pPrev !=0); + } + + // add a node to the end of a singly linked list + template void AddToDTail(T * & head, T * node) + { + node->m_pNext=0; + if (! head) + { + head=node; + node->m_pPrev = NULL; + node->m_pNext = NULL; + } + else + { + T *ptr = head; + while(ptr->m_pNext) + { + ptr=ptr->m_pNext; + } + ptr->m_pNext = node; + node->m_pPrev = ptr; // + node->m_pNext = NULL; + } + } + + // add a node to end of doubly linked list. + template inline void AddToDHead(T * &head, T *which) + { + which->m_pNext=head; + if (head) + { + head->m_pPrev=which; + } + which->m_pPrev=0; + head=which; + ValidateDList(head); + } + + // add a node to front of doubly linked list which maintains a tail ptr + template inline void AddToDHeadWithTailPtr(T * &head, T *which, T * &tailptr) + { + which->m_pNext=head; + if (head) + { + head->m_pPrev=which; + } + else + { + tailptr=which; + } + which->m_pPrev=0; + head=which; + ValidateDList(head); + } + + // add a node to end of doubly linked list which maintains a tail ptr + template inline void AddToDTailWithTailPtr(T * &head, T *which, T * & tailptr) + { + if (! tailptr) + { + Assert(! head); + which->m_pPrev=which->m_pNext=0; + tailptr=head=which; + } + else + { + which->m_pNext=0; + which->m_pPrev=tailptr; + tailptr->m_pNext=which; + tailptr=which; + } + ValidateDList( head ); + } + + // Remove a node from a dlist , maintaining the tail ptr. node is not 'delete' d + template inline void RemoveFromDListWithTailPtr(T * &head, T *which, T * & tailptr) + { + if (which==tailptr) + { + tailptr=which->m_pPrev; + } + if (which->m_pPrev) + { + Assert(which->m_pPrev->m_pNext==which); + which->m_pPrev->m_pNext=which->m_pNext; + if (which->m_pNext) + { + Assert(which->m_pNext->m_pPrev==which); + which->m_pNext->m_pPrev=which->m_pPrev; + } + } + else + { + if (head==which) + { + head=which->m_pNext; + if (head) + { + Assert(head->m_pPrev==which); + head->m_pPrev=0; + } + } + } + which->m_pNext=which->m_pPrev=0; + ValidateDList(head); + + } + + // this function removes a node, and delete's the node + template inline void DeleteFromDListWithTailPtr(T * &head, T *which, T * & tailptr) + { + T *tmp=which; + if (which==tailptr) + { + tailptr=which->m_pPrev; + } + if (which->m_pPrev) + { + Assert(which->m_pPrev->m_pNext==which); + which->m_pPrev->m_pNext=which->m_pNext; + if (which->m_pNext) + { + Assert(which->m_pNext->m_pPrev==which); + which->m_pNext->m_pPrev=which->m_pPrev; + } + } + else + { + if (head==which) + { + head=which->m_pNext; + if (head) + { + Assert(head->m_pPrev==which); + head->m_pPrev=0; + } + } + } + which->m_pNext=which->m_pPrev=0; + delete tmp; + ValidateDList(head); + } + + // Add a node to a d-list, keeping the highest priority nodes first. This is a simple + // linear search to insert, NOT a O(logn) heap. + template inline void AddToDPriority(T * &head, T *which) + { + T* prevnode=0; + for(T *curnode=head;curnode;curnode=curnode->m_pNext) + { + if (which->m_Priority>=curnode->m_Priority) + break; + prevnode=curnode; + } + // now, we have either run out of list, or we have found an + // element to add this one before + if (! prevnode) + { + AddToDHead(head,which); + } + else + { + which->m_pNext=prevnode->m_pNext; + prevnode->m_pNext=which; + which->m_pPrev=prevnode; + if (which->m_pNext) + which->m_pNext->m_pPrev=which; + } + } + + // same as AddToDPriority, except with reverse order + template inline void AddToDPriorityLowestFirst(T * &head, T *which) + { + T* prevnode=0; + for(T *curnode=head;curnode;curnode=curnode->m_pNext) + { + if (which->m_Priority<=curnode->m_Priority) + break; + prevnode=curnode; + } + // now, we have either run out of list, or we have found an + // element to add this one before + if (! prevnode) + { + AddToDHead(head,which); + } + else + { + which->m_pNext=prevnode->m_pNext; + prevnode->m_pNext=which; + which->m_pPrev=prevnode; + if (which->m_pNext) + which->m_pNext->m_pPrev=which; + } + } + + + // return a pointer to the last node in a singly-linked (or doubly) list + template T * LastNode(T * head) + { + if (head) + { + while(head->m_pNext) + { + head=head->m_pNext; + } + } + return head; + } + + + // Remove from a singly linked list. no delete called. + template void RemoveFromList(T * & head, V *which) + { + if (head==which) + { + head=which->m_pNext; + } + else + { + for(T * i=head; i; i=i->m_pNext) + { + if (i->m_pNext==which) + { + i->m_pNext=which->m_pNext; + return; + } + } + } + } + + // same as RemoveFromList, but 'delete' is called. + template void DeleteFromList(T * & head, V *which) + { + T *tmp; + if (head==which) + { + tmp=which->m_pNext; + delete(head); + head=tmp; + } + else + { + for(T * i=head; i; i=i->m_pNext) + { + if (i->m_pNext==which) + { + tmp=which->m_pNext; + delete(which); + i->m_pNext=tmp; + return; + } + } + } + } + + // find the position in a list of a node. -1 if not found. Linear search. + // nodes must have comparison functions + template int PositionInList(T *head, V *node) + { + int pos=0; + while(head) + { + if (head==node) return pos; + head=head->m_pNext; + pos++; + } + return -1; + } + + // find the Nth node in a list. null if index too high. + template T *NthNode(T * head, int idx) + { + while(idx && head) + { + idx--; + head=head->m_pNext; + } + return head; + } + + //Add a node to the head of a singly-linked + // Note that the head var passed to this will be modified. + template FORCEINLINE void AddToHead(T * & head, V * node) + { + node->m_pNext=head; + head=node; + } + + //Add a node to the head of a singly-linked list in a thread safe fashion. NOTE RESTRICTIONS + // EXTREMELY CAREFULLY: the ONLY thing that is thread safe about this is multiple threads + // adding to a list at the same time. ANY other simultaneous operations (walking the list, + // modifying it in any way, ANYTHING else) is NOT thread safe. The only way to use this + // function in code and have that code WORK is to follow the restriction that the only + // simultaneous operation is an AddHead. CTSList is a better choice for ALMOST ANYTHING that + // wants this functionality. The m_pNext pointer in the node needs to be properly aligned for + // interlocked compare exchange. Do NOT use this function unless you understand every + // implication of the above paragraph and want something lighter than ctslist. + template FORCEINLINE void AddToHeadTS( T * &pHead, V * pNode) + { + for(;; ) + { + T *pCurHead = ReadVolatileMemory( &pHead ); + pNode->m_pNext = pCurHead; + ThreadMemoryBarrier(); + if ( ThreadInterlockedAssignPointerIf( ( void * volatile * ) ( &pHead ) , pNode, pCurHead ) ) + { + break; + } + } + } + + // a version of AddToHeadTS which uses an alternate field to m_pnext for the linkage. Same + // restrictions as above. + template FORCEINLINE void AddToHeadByFieldTS( T * &pHead, V * pNode, T * V::*field ) + { + for(;; ) + { + T *pCurHead = ReadVolatileMemory( &pHead ); + ( *pNode ).*field = pCurHead; + ThreadMemoryBarrier(); + if ( ThreadInterlockedAssignPointerIf( ( void * volatile * ) ( &pHead ) , pNode, pCurHead ) ) + { + break; + } + } + } + + + // remove the head node of a list in a thread safe fashion. It is NOT SAFE to do ANYTHING else + // with the list during this operation. The only thread safe usage pattern for this is multiple + // threads grabbing the head node at the same time. + template FORCEINLINE T *RemoveHeadTS( T * &pHead ) + { + for(;; ) + { + T *pCurHead = ReadVolatileMemory( &pHead ); + if ( ! pCurHead ) + { + return NULL; + } + ThreadMemoryBarrier(); + if ( ThreadInterlockedAssignPointerIf( ( void * volatile * ) ( &pHead ) , pCurHead->m_pNext, pCurHead ) ) + { + return pCurHead; + } + } + } + + + + //Add a node to the tail of a singly-linked. Not fast + // Note that the head var passed to this will be modified. + template FORCEINLINE void AddToTail(T * & head, V * node) + { + node->m_pNext = NULL; + if ( ! head ) + head = node; + else + { + T *pLastNode = head; + while( pLastNode->m_pNext ) + pLastNode = pLastNode->m_pNext; + pLastNode->m_pNext = node; + } + } + + //Add a node to the head of a singly-linked list, maintaining a tail pointer + template FORCEINLINE void AddToHead(T * & head, T * &tail,V * node) + { + if (! head) + { + tail=node; + } + node->m_pNext=head; + head=node; + } + + + + // return the node in head before in a singly linked list. returns null if head is empty, n is + // null, or if n is the first node. not fast. + template FORCEINLINE T * PrevNode(T *head, T *node) + { + T *i; + for( i = head; i ; i = i->m_pNext) + { + if ( i->m_pNext == node ) + break; + } + return i; + } + + + // add a node to the end of a singly linked list. Not fast. + template void AddToEnd(T * & head, V * node) + { + node->m_pNext=0; + if (! head) + { + head=node; + } + else + { + T *ptr=head; + while(ptr->m_pNext) + { + ptr=ptr->m_pNext; + } + ptr->m_pNext=node; + } + } + + // add a node to the end of a singly linked list, maintaining a tail pointer. + // the head and tail pointer can be modified by this routine. + template void AddToEndWithTail(T * & head, V * node, T * & tail ) + { + Assert((head && tail) || ((!head) && (!tail))); + node->m_pNext=0; + if (! head) + { + head=tail=node; + } + else + { + tail->m_pNext=node; + tail=node; + } + } + + // Add a node to a singly linked list, sorting by the m_Name field + template void AddSortedByName(T * & head, T * node) + { + if ( (! head) || // empty list? + (stricmp(node->m_Name,head->m_Name)==-1)) // or we should be first? + { + node->m_pNext=head; // make us head + head=node; + } + else + { + T * t; + for( t = head ; t->m_pNext ; t = t->m_pNext ) // find the node we should be before + { + if ( stricmp( t->m_pNext->m_Name, node->m_Name) >= 0 ) + { + break; + } + } + node->m_pNext = t->m_pNext; + t->m_pNext = node; + } + } + + // count # of elements in list + template int ListLength(T *head) + { + int len=0; + while(head) + { + len++; + head = static_cast< T* >( head->m_pNext ); + } + return len; + } + + // this will kill a list if the list is of objects which automatically + // remove themselves from the list when delete is called + template void KillList(T * & head) + { + while(head) + { + delete head; + } + } + + + // this will kill all elements in a list if + // the elements are of a type which does NOT remove itself from + // the list when the destructor is called. + template void DeleteList(T * & head) + { + while (head) + { + T* tmp=head->m_pNext; + delete head; + head=tmp; + } + } + + // find a named node in any list which has both a Next field and a Name field. + template FORCEINLINE T * FindNamedNode(T * head, char const *name) + { + for(;head && stricmp(head->m_Name,name); head=head->m_pNext) + { + } + return head; + } + + template FORCEINLINE T * FindNamedNodeCaseSensitive(T * head, char const *name) + { + for(;head && strcmp(head->m_Name,name); head=head->m_pNext) + { + } + return head; + } + + // find data in a singly linked list, using equality match on any field + // usage: FindNodeByField(listptr,data,&list::fieldname) + template FORCEINLINE T * FindNodeByField(T * head, U data, U V::*field) + { + while( head ) + { + if (data == (*head).*field) + return head; + head = head->m_pNext; + } + return 0; + } + + // find a node and its predecessor, matching on equality of a given field. + // usage: FindNodeByFieldWithPrev(listptr,data,&list::fieldname, prevptr) + template FORCEINLINE T * FindNodeByFieldWithPrev(T * head, U data, U V::*field, T * & prev) + { + prev=0; + for(T *i=head; i; i = i->m_pNext) + { + if( data == (*i).*field) + return i; + prev = i; + } + prev = 0; + return 0; + } + + + /// sort a list. comparefn should return 0 if the items are equal, 1 if A goes first, and -1 if A goes last. + // NOT fast. + template void SortList(T * &head, int (*comparefn)(T * a, T * b)) + { + int didswap=1; + while(didswap) + { + didswap=0; + T *prev=0; + for(T *i=head;i && i->m_pNext; i=i->m_pNext) + { + /// compare i and i+1 + int rslt=(*comparefn)(i,i->m_pNext); + if (rslt==-1) + { + /// need to swap + didswap=1; + T *newfirst=i->m_pNext; + if (prev) + { + prev->m_pNext=newfirst; + i->m_pNext=newfirst->m_pNext; + newfirst->m_pNext=i; + } + else + { + head=i->m_pNext; + i->m_pNext=newfirst->m_pNext; + newfirst->m_pNext=i; + } + i=newfirst; + } + prev=i; + } + } + } + + // sort a doubly linked list. NOt fast. + template void SortDList(T * & head, int (*comparefn)(T * a, T * b)) + { + SortList(head,comparefn); + /// now, regen prev ptrs + T *prev=0; + for(T *i=head;i;i=i->m_pNext) + { + i->m_pPrev=prev; + prev=i; + } + } + + // reverse a singly linked list. not recommended for anything other than valve programming + // interview :-) + template T *ReversedList( T * head ) + { + T * pNewHead=NULL; + while( head ) + { + T *pNext=head->m_pNext; +#ifdef INTERVIEW_QUESTION + head->m_pNext=pNewHead; + pNewHead = head; +#else + AddToHead( pNewHead, head ); +#endif + head = pNext; + } + return pNewHead; + } +}; + +// singly linked list +template class CUtlIntrusiveList +{ +public: + T *m_pHead; + + FORCEINLINE T *Head( void ) const + { + return m_pHead; + } + + FORCEINLINE CUtlIntrusiveList(void) + { + m_pHead = NULL; + } + + + FORCEINLINE void RemoveAll( void ) + { + // empty list. doesn't touch nodes at all + m_pHead = NULL; + } + FORCEINLINE void AddToHead( T * node ) + { + IntrusiveList::AddToHead( m_pHead, node ); + } + + FORCEINLINE void AddToHeadTS( T * pNode) + { + IntrusiveList::AddToHeadTS( m_pHead, pNode ); + } + + FORCEINLINE void AddToTail( T * node ) + { + IntrusiveList::AddToTail( m_pHead, node ); + } + + void RemoveNode(T *which) + { + IntrusiveList::RemoveFromList( m_pHead, which ); + } + + // this will kill a list if the list is of objects which automatically + // remove themselves from the list when delete is called + void KillList( void ) + { + while(m_pHead) + { + delete m_pHead; + } + } + + + // return the node in head before in a singly linked list. returns null if head is empty, n is + // null, or if n is the first node. not fast. Fast for dlists + T * PrevNode(T *node) + { + return IntrusiveList::PrevNode( m_pHead, node ); + } + + int NthNode( int n ) + { + return NthNode( m_pHead, n ); + } + + // this will kill all elements in a list if + // the elements are of a type which does NOT remove itself from + // the list when the destructor is called. + void Purge( void ) + { + while (m_pHead) + { + T* tmp=m_pHead->m_pNext; + delete m_pHead; + m_pHead=tmp; + } + } + + int Count( void ) const + { + return IntrusiveList::ListLength( m_pHead ); + } + + FORCEINLINE T * FindNamedNodeCaseSensitive( char const *pName ) const + { + return IntrusiveList::FindNamedNodeCaseSensitive( m_pHead, pName ); + + } + + // find data in a singly linked list, using equality match on any field + // usage: FindNodeByField(data,&list::fieldname) + template FORCEINLINE T * FindNodeByField( U data, U V::*field) + { + return IntrusiveList::FindNodeByField( m_pHead, data, field ); + } + T *RemoveHead( void ) + { + if ( m_pHead ) + { + T *pRet = m_pHead; + m_pHead = static_cast< T* >( pRet->m_pNext ); + return pRet; + } + else + return NULL; + } + + +}; + +// doubly linked list +template class CUtlIntrusiveDList : public CUtlIntrusiveList +{ +public: + + FORCEINLINE void AddToHead( T * node ) + { + IntrusiveList::AddToDHead( CUtlIntrusiveList::m_pHead, node ); + } + FORCEINLINE void AddToTail( T * node ) + { + IntrusiveList::AddToDTail( CUtlIntrusiveList::m_pHead, node ); + } + + void RemoveNode(T *which) + { + IntrusiveList::RemoveFromDList( CUtlIntrusiveList::m_pHead, which ); + } + + T *RemoveHead( void ) + { + if ( CUtlIntrusiveList::m_pHead ) + { + T *pRet = CUtlIntrusiveList::m_pHead; + CUtlIntrusiveList::m_pHead = CUtlIntrusiveList::m_pHead->m_pNext; + if ( CUtlIntrusiveList::m_pHead ) + CUtlIntrusiveList::m_pHead->m_pPrev = NULL; + return pRet; + } + else + return NULL; + } + + T * PrevNode(T *node) + { + return ( node )?node->m_Prev:NULL; + } + +}; + +// doubly linked list with a tial ptr for fast addtotail. +template class CUtlIntrusiveDListWithTailPtr : public CUtlIntrusiveDList +{ +public: + + T *m_pTailPtr; + + FORCEINLINE CUtlIntrusiveDListWithTailPtr( void ) : CUtlIntrusiveDList() + { + m_pTailPtr = NULL; + } + + FORCEINLINE void AddToHead( T * node ) + { + IntrusiveList::AddToDHeadWithTailPtr( CUtlIntrusiveList::m_pHead, node, m_pTailPtr ); + } + FORCEINLINE void AddToTail( T * node ) + { + IntrusiveList::AddToDTailWithTailPtr( CUtlIntrusiveList::m_pHead, node, m_pTailPtr ); + } + + void RemoveNode( T *pWhich ) + { + IntrusiveList::RemoveFromDListWithTailPtr( CUtlIntrusiveList::m_pHead, pWhich, m_pTailPtr ); + } + + void Purge( void ) + { + CUtlIntrusiveList::Purge(); + m_pTailPtr = NULL; + } + + void Kill( void ) + { + CUtlIntrusiveList::Purge(); + m_pTailPtr = NULL; + } + + T *RemoveHead( void ) + { + if ( CUtlIntrusiveDList::m_pHead ) + { + T *pRet = CUtlIntrusiveDList::m_pHead; + CUtlIntrusiveDList::m_pHead = CUtlIntrusiveDList::m_pHead->m_pNext; + if ( CUtlIntrusiveDList::m_pHead ) + CUtlIntrusiveDList::m_pHead->m_pPrev = NULL; + if (! CUtlIntrusiveDList::m_pHead ) + m_pTailPtr = NULL; + IntrusiveList::ValidateDList( CUtlIntrusiveDList::m_pHead ); + return pRet; + } + else + return NULL; + } + + T * PrevNode(T *node) + { + return ( node )?node->m_Prev:NULL; + } + +}; + +template void PrependDListWithTailToDList( CUtlIntrusiveDListWithTailPtr &src, + CUtlIntrusiveDList &dest ) +{ + if ( src.m_pHead ) + { + src.m_pTailPtr->m_pNext = dest.m_pHead; + if ( dest.m_pHead ) + dest.m_pHead->m_pPrev = src.m_pTailPtr; + dest.m_pHead = src.m_pHead; + IntrusiveList::ValidateDList( dest.m_pHead ); + } +} + +template class CUtlIntrusiveListWithTailPtr : public CUtlIntrusiveList +{ +public: + + T *m_pTailPtr; + + FORCEINLINE CUtlIntrusiveListWithTailPtr( void ) : CUtlIntrusiveList() + { + m_pTailPtr = NULL; + } + + FORCEINLINE void AddToHead( T * pNode ) + { + if ( !this->m_pHead ) + { + m_pTailPtr = pNode; + } + IntrusiveList::AddToHead( CUtlIntrusiveList::m_pHead, pNode ); + } + FORCEINLINE void AddToTail( T * node ) + { + IntrusiveList::AddToEndWithTail( CUtlIntrusiveList::m_pHead, node, m_pTailPtr ); + } + + void Purge( void ) + { + CUtlIntrusiveList::Purge(); + m_pTailPtr = NULL; + } + + void Kill( void ) + { + CUtlIntrusiveList::Purge(); + m_pTailPtr = NULL; + } + + T *RemoveHead( void ) + { + if ( CUtlIntrusiveList::m_pHead ) + { + T *pRet = CUtlIntrusiveList::RemoveHead(); + if ( CUtlIntrusiveList::m_pHead == NULL ) + { + m_pTailPtr = NULL; + } + return pRet; + } + else + return NULL; + } + + int Count( void ) const + { + return CUtlIntrusiveList::Count(); + } + +}; + +template class CUtlSymbolStore +{ + struct SymbolNode_t + { + SymbolNode_t *m_pNext; + T *m_pData; + }; + + CUtlIntrusiveList m_Buckets[BUCKETSIZE]; + +public: + T const *FindOrAdd( T const *pData ) + { + // see if it is there. add if not. return permanent pointer + uint hVal = HashItem( *pData ) % BUCKETSIZE; + for( SymbolNode_t *i = m_Buckets[hVal].m_pHead; i; i = i->m_pNext ) + { + if ( memcmp( pData, i->m_pData, sizeof( T ) ) == 0 ) + return i->m_pData; + } + // need to add it + SymbolNode_t *pNew = new SymbolNode_t; + pNew->m_pData = new T; + *( pNew->m_pData ) = ( *pData ); + m_Buckets[hVal].AddToHead( pNew ); + return pNew->m_pData; + } +}; + + + + +#endif diff --git a/public/tier1/utllinkedlist.h b/public/tier1/utllinkedlist.h new file mode 100644 index 0000000..40d0f46 --- /dev/null +++ b/public/tier1/utllinkedlist.h @@ -0,0 +1,1070 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Linked list container class +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLLINKEDLIST_H +#define UTLLINKEDLIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/basetypes.h" +#include "utlmemory.h" +#include "utlfixedmemory.h" +#include "utlblockmemory.h" +#include "tier0/dbg.h" + +// define to enable asserts griping about things you shouldn't be doing with multilists +// #define MULTILIST_PEDANTIC_ASSERTS 1 + +// This is a useful macro to iterate from head to tail in a linked list. +#define FOR_EACH_LL( listName, iteratorName ) \ + for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) + +//----------------------------------------------------------------------------- +// class CUtlLinkedList: +// description: +// A lovely index-based linked list! T is the class type, I is the index +// type, which usually should be an unsigned short or smaller. However, +// you must avoid using 16- or 8-bit arithmetic on PowerPC architectures; +// therefore you should not use UtlLinkedListElem_t::I as the type of +// a local variable... ever. PowerPC integer arithmetic must be 32- or +// 64-bit only; otherwise performance plummets. +//----------------------------------------------------------------------------- + +template +struct UtlLinkedListElem_t +{ + T m_Element; + I m_Previous; + I m_Next; + +private: + // No copy constructor for these... + UtlLinkedListElem_t( const UtlLinkedListElem_t& ); +}; + + +// Class S is the storage type; the type you can use to save off indices in +// persistent memory. Class I is the iterator type, which is what should be used +// in local scopes. I defaults to be S, but be aware that on the 360, 16-bit +// arithmetic is catastrophically slow. Therefore you should try to save shorts +// in memory, but always operate on 32's or 64's in local scope. +// The ideal parameter order would be TSMI (you are more likely to override M than I) +// but since M depends on I we can't have the defaults in that order, alas. +template , I > > +class CUtlLinkedList +{ +public: + typedef T ElemType_t; + typedef S IndexType_t; // should really be called IndexStorageType_t, but that would be a huge change + typedef I IndexLocalType_t; + typedef M MemoryAllocator_t; + + // constructor, destructor + CUtlLinkedList( int growSize = 0, int initSize = 0 ); + ~CUtlLinkedList(); + + // gets particular elements + T& Element( I i ); + T const& Element( I i ) const; + T& operator[]( I i ); + T const& operator[]( I i ) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity( int num ); + + void SetGrowSize( int growSize ); + + // Memory deallocation + void Purge(); + + // Delete all the elements then call Purge. + void PurgeAndDeleteElements(); + + // Insertion methods.... + I InsertBefore( I before ); + I InsertAfter( I after ); + I AddToHead( ); + I AddToTail( ); + + I InsertBefore( I before, T const& src ); + I InsertAfter( I after, T const& src ); + I AddToHead( T const& src ); + I AddToTail( T const& src ); + + // Find an element and return its index or InvalidIndex() if it couldn't be found. + I Find( const T &src ) const; + + // Look for the element. If it exists, remove it and return true. Otherwise, return false. + bool FindAndRemove( const T &src ); + + // Removal methods + void Remove( I elem ); + void RemoveAll(); + + // Allocation/deallocation methods + // If multilist == true, then list list may contain many + // non-connected lists, and IsInList and Head + Tail are meaningless... + I Alloc( bool multilist = false ); + void Free( I elem ); + + // list modification + void LinkBefore( I before, I elem ); + void LinkAfter( I after, I elem ); + void Unlink( I elem ); + void LinkToHead( I elem ); + void LinkToTail( I elem ); + + // invalid index (M will never allocate an element at this index) + inline static S InvalidIndex() { return ( S )M::InvalidIndex(); } + + // Is a given index valid to use? (representible by S and not the invalid index) + static bool IndexInRange( I index ); + + inline static size_t ElementSize() { return sizeof( ListElem_t ); } + + // list statistics + int Count() const; + I MaxElementIndex() const; + I NumAllocated( void ) const { return m_NumAlloced; } + + // Traversing the list + I Head() const; + I Tail() const; + I Previous( I i ) const; + I Next( I i ) const; + + // Are nodes in the list or valid? + bool IsValidIndex( I i ) const; + bool IsInList( I i ) const; + +protected: + + // What the linked list element looks like + typedef UtlLinkedListElem_t ListElem_t; + + // constructs the class + I AllocInternal( bool multilist = false ); + void ConstructList(); + + // Gets at the list element.... + ListElem_t& InternalElement( I i ) { return m_Memory[i]; } + ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } + + // copy constructors not allowed + CUtlLinkedList( CUtlLinkedList const& list ) { Assert(0); } + + M m_Memory; + I m_Head; + I m_Tail; + I m_FirstFree; + I m_ElementCount; // The number actually in the list + I m_NumAlloced; // The number of allocated elements + typename M::Iterator_t m_LastAlloc; // the last index allocated + + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + ListElem_t *m_pElements; + + FORCEINLINE M const &Memory( void ) const + { + return m_Memory; + } + + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + } +}; + + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T > +class CUtlFixedLinkedList : public CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > > +{ +public: + CUtlFixedLinkedList( int growSize = 0, int initSize = 0 ) + : CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > >( growSize, initSize ) {} + + bool IsValidIndex( intp i ) const + { + if ( !this->Memory().IsIdxValid( i ) ) + return false; + + return ( this->Memory()[ i ].m_Previous != i ) || ( this->Memory()[ i ].m_Next == i ); + } + +private: + int MaxElementIndex() const { Assert( 0 ); return this->InvalidIndex(); } // fixedmemory containers don't support iteration from 0..maxelements-1 + void ResetDbgInfo() {} +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = unsigned short > +class CUtlBlockLinkedList : public CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > > +{ +public: + CUtlBlockLinkedList( int growSize = 0, int initSize = 0 ) + : CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > >( growSize, initSize ) {} +protected: + void ResetDbgInfo() {} +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlLinkedList::CUtlLinkedList( int growSize, int initSize ) : + m_Memory( growSize, initSize ), m_LastAlloc( m_Memory.InvalidIterator() ) +{ + // Prevent signed non-int datatypes + COMPILE_TIME_ASSERT( sizeof(S) == 4 || ( ( (S)-1 ) > 0 ) ); + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlLinkedList::~CUtlLinkedList( ) +{ + RemoveAll(); +} + +template +void CUtlLinkedList::ConstructList() +{ + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_FirstFree = InvalidIndex(); + m_ElementCount = 0; + m_NumAlloced = 0; +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- + +template +inline T& CUtlLinkedList::Element( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::Element( I i ) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlLinkedList::operator[]( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlLinkedList::operator[]( I i ) const +{ + return m_Memory[i].m_Element; +} + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- + +template +inline int CUtlLinkedList::Count() const +{ +#ifdef MULTILIST_PEDANTIC_ASSERTS + AssertMsg( !ML, "CUtlLinkedList::Count() is meaningless for linked lists." ); +#endif + return m_ElementCount; +} + +template +inline I CUtlLinkedList::MaxElementIndex() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- + +template +inline I CUtlLinkedList::Head() const +{ + return m_Head; +} + +template +inline I CUtlLinkedList::Tail() const +{ + return m_Tail; +} + +template +inline I CUtlLinkedList::Previous( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Previous; +} + +template +inline I CUtlLinkedList::Next( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Next; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- + +#pragma warning(push) +#pragma warning( disable: 4310 ) // Allows "(I)(S)M::INVALID_INDEX" below +template +inline bool CUtlLinkedList::IndexInRange( I index ) // Static method +{ + // Since S is not necessarily the type returned by M, we need to check that M returns indices + // which are representable by S. A common case is 'S === unsigned short', 'I == int', in which + // case CUtlMemory will have 'InvalidIndex == (int)-1' (which casts to 65535 in S), and will + // happily return elements at index 65535 and above. + + // Do some static checks here: + // 'I' needs to be able to store 'S' + COMPILE_TIME_ASSERT( sizeof(I) >= sizeof(S) ); + // 'S' should be unsigned (to avoid signed arithmetic errors for plausibly exhaustible ranges) + COMPILE_TIME_ASSERT( ( sizeof(S) > 2 ) || ( ( (S)-1 ) > 0 ) ); + // M::INVALID_INDEX should be storable in S to avoid ambiguities (e.g. with 65536) + COMPILE_TIME_ASSERT( ( M::INVALID_INDEX == -1 ) || ( M::INVALID_INDEX == (S)M::INVALID_INDEX ) ); + + return ( ( (S)index == index ) && ( (S)index != InvalidIndex() ) ); +} +#pragma warning(pop) + +template +inline bool CUtlLinkedList::IsValidIndex( I i ) const +{ + if ( !m_Memory.IsIdxValid( i ) ) + return false; + + if ( m_Memory.IsIdxAfter( i, m_LastAlloc ) ) + return false; // don't read values that have been allocated, but not constructed + + return ( m_Memory[ i ].m_Previous != i ) || ( m_Memory[ i ].m_Next == i ); +} + +template +inline bool CUtlLinkedList::IsInList( I i ) const +{ + if ( !m_Memory.IsIdxValid( i ) || m_Memory.IsIdxAfter( i, m_LastAlloc ) ) + return false; // don't read values that have been allocated, but not constructed + + return Previous( i ) != i; +} + +/* +template +inline bool CUtlFixedLinkedList::IsInList( int i ) const +{ + return m_Memory.IsIdxValid( i ) && (Previous( i ) != i); +} +*/ + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- + +template< class T, class S, bool ML, class I, class M > +void CUtlLinkedList::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + +template< class T, class S, bool ML, class I, class M > +void CUtlLinkedList::SetGrowSize( int growSize ) +{ + RemoveAll(); + m_Memory.Init( growSize ); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::Purge() +{ + RemoveAll(); + + m_Memory.Purge(); + m_FirstFree = InvalidIndex(); + m_NumAlloced = 0; + + //Routing "m_LastAlloc = m_Memory.InvalidIterator();" through a local const to sidestep an internal compiler error on 360 builds + const typename M::Iterator_t scInvalidIterator = m_Memory.InvalidIterator(); + m_LastAlloc = scInvalidIterator; + ResetDbgInfo(); +} + + +template +void CUtlLinkedList::PurgeAndDeleteElements() +{ + I iNext; + for( I i=Head(); i != InvalidIndex(); i=iNext ) + { + iNext = Next(i); + delete Element(i); + } + + Purge(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlLinkedList::AllocInternal( bool multilist ) RESTRICT +{ + Assert( !multilist || ML ); +#ifdef MULTILIST_PEDANTIC_ASSERTS + Assert( multilist == ML ); +#endif + I elem; + if ( m_FirstFree == InvalidIndex() ) + { + Assert( m_Memory.IsValidIterator( m_LastAlloc ) || m_ElementCount == 0 ); + + typename M::Iterator_t it = m_Memory.IsValidIterator( m_LastAlloc ) ? m_Memory.Next( m_LastAlloc ) : m_Memory.First(); + + if ( !m_Memory.IsValidIterator( it ) ) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow(); + ResetDbgInfo(); + + it = m_Memory.IsValidIterator( m_LastAlloc ) ? m_Memory.Next( m_LastAlloc ) : m_Memory.First(); + + Assert( m_Memory.IsValidIterator( it ) ); + if ( !m_Memory.IsValidIterator( it ) ) + { + ExecuteNTimes( 10, Warning( "CUtlLinkedList overflow! (exhausted memory allocator)\n" ) ); + return InvalidIndex(); + } + } + + // We can overflow before the utlmemory overflows, since S != I + if ( !IndexInRange( m_Memory.GetIndex( it ) ) ) + { + ExecuteNTimes( 10, Warning( "CUtlLinkedList overflow! (exhausted index range)\n" ) ); + return InvalidIndex(); + } + + m_LastAlloc = it; + elem = m_Memory.GetIndex( m_LastAlloc ); + m_NumAlloced++; + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalElement( m_FirstFree ).m_Next; + } + + if ( !multilist ) + { + InternalElement( elem ).m_Next = elem; + InternalElement( elem ).m_Previous = elem; + } + else + { + InternalElement( elem ).m_Next = InvalidIndex(); + InternalElement( elem ).m_Previous = InvalidIndex(); + } + + return elem; +} + +template +I CUtlLinkedList::Alloc( bool multilist ) +{ + I elem = AllocInternal( multilist ); + if ( elem == InvalidIndex() ) + return elem; + + Construct( &Element(elem) ); + + return elem; +} + +template +void CUtlLinkedList::Free( I elem ) +{ + Assert( IsValidIndex(elem) && IndexInRange( elem ) ); + Unlink(elem); + + ListElem_t &internalElem = InternalElement(elem); + Destruct( &internalElem.m_Element ); + internalElem.m_Next = m_FirstFree; + m_FirstFree = elem; +} + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses default constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore( I before ) +{ + // Make a new node + I newNode = AllocInternal(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkBefore( before, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter( I after ) +{ + // Make a new node + I newNode = AllocInternal(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkAfter( after, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead( ) +{ + return InsertAfter( InvalidIndex() ); +} + +template +inline I CUtlLinkedList::AddToTail( ) +{ + return InsertBefore( InvalidIndex() ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses copy constructor) +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::InsertBefore( I before, T const& src ) +{ + // Make a new node + I newNode = AllocInternal(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkBefore( before, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +I CUtlLinkedList::InsertAfter( I after, T const& src ) +{ + // Make a new node + I newNode = AllocInternal(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkAfter( after, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +inline I CUtlLinkedList::AddToHead( T const& src ) +{ + return InsertAfter( InvalidIndex(), src ); +} + +template +inline I CUtlLinkedList::AddToTail( T const& src ) +{ + return InsertBefore( InvalidIndex(), src ); +} + + +//----------------------------------------------------------------------------- +// Removal methods +//----------------------------------------------------------------------------- + +template +I CUtlLinkedList::Find( const T &src ) const +{ + for ( I i=Head(); i != InvalidIndex(); i = Next( i ) ) + { + if ( Element( i ) == src ) + return i; + } + return InvalidIndex(); +} + + +template +bool CUtlLinkedList::FindAndRemove( const T &src ) +{ + I i = Find( src ); + if ( i == InvalidIndex() ) + { + return false; + } + else + { + Remove( i ); + return true; + } +} + + +template +void CUtlLinkedList::Remove( I elem ) +{ + Free( elem ); +} + +template +void CUtlLinkedList::RemoveAll() +{ + // Have to do some convoluted stuff to invoke the destructor on all + // valid elements for the multilist case (since we don't have all elements + // connected to each other in a list). + + if ( m_LastAlloc == m_Memory.InvalidIterator() ) + { + Assert( m_Head == InvalidIndex() ); + Assert( m_Tail == InvalidIndex() ); + Assert( m_FirstFree == InvalidIndex() ); + Assert( m_ElementCount == 0 ); + return; + } + + if ( ML ) + { + for ( typename M::Iterator_t it = m_Memory.First(); it != m_Memory.InvalidIterator(); it = m_Memory.Next( it ) ) + { + I i = m_Memory.GetIndex( it ); + if ( IsValidIndex( i ) ) // skip elements already in the free list + { + ListElem_t &internalElem = InternalElement( i ); + Destruct( &internalElem.m_Element ); + internalElem.m_Previous = i; + internalElem.m_Next = m_FirstFree; + m_FirstFree = i; + } + + if ( it == m_LastAlloc ) + break; // don't destruct elements that haven't ever been constructed + } + } + else + { + I i = Head(); + I next; + while ( i != InvalidIndex() ) + { + next = Next( i ); + ListElem_t &internalElem = InternalElement( i ); + Destruct( &internalElem.m_Element ); + internalElem.m_Previous = i; + internalElem.m_Next = next == InvalidIndex() ? m_FirstFree : next; + i = next; + } + if ( Head() != InvalidIndex() ) + { + m_FirstFree = Head(); + } + } + + // Clear everything else out + m_Head = InvalidIndex(); + m_Tail = InvalidIndex(); + m_ElementCount = 0; +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- + +template +void CUtlLinkedList::LinkBefore( I before, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + Unlink(elem); + + ListElem_t * RESTRICT pNewElem = &InternalElement(elem); + + // The element *after* our newly linked one is the one we linked before. + pNewElem->m_Next = before; + + S newElem_mPrevious; // we need to hang on to this for the compairson against InvalidIndex() + // below; otherwise we get a a load-hit-store on pNewElem->m_Previous, even + // with RESTRICT + if (before == InvalidIndex()) + { + // In this case, we're linking to the end of the list, so reset the tail + newElem_mPrevious = m_Tail; + pNewElem->m_Previous = m_Tail; + m_Tail = elem; + } + else + { + // Here, we're not linking to the end. Set the prev pointer to point to + // the element we're linking. + Assert( IsInList(before) ); + ListElem_t * RESTRICT beforeElem = &InternalElement(before); + pNewElem->m_Previous = newElem_mPrevious = beforeElem->m_Previous; + beforeElem->m_Previous = elem; + } + + // Reset the head if we linked to the head of the list + if (newElem_mPrevious == InvalidIndex()) + m_Head = elem; + else + InternalElement(newElem_mPrevious).m_Next = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::LinkAfter( I after, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + if ( IsInList(elem) ) + Unlink(elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *before* our newly linked one is the one we linked after + newElem.m_Previous = after; + if (after == InvalidIndex()) + { + // In this case, we're linking to the head of the list, reset the head + newElem.m_Next = m_Head; + m_Head = elem; + } + else + { + // Here, we're not linking to the end. Set the next pointer to point to + // the element we're linking. + Assert( IsInList(after) ); + ListElem_t& afterElem = InternalElement(after); + newElem.m_Next = afterElem.m_Next; + afterElem.m_Next = elem; + } + + // Reset the tail if we linked to the tail of the list + if (newElem.m_Next == InvalidIndex()) + m_Tail = elem; + else + InternalElement(newElem.m_Next).m_Previous = elem; + + // one more element baby + ++m_ElementCount; +} + +template +void CUtlLinkedList::Unlink( I elem ) +{ + Assert( IsValidIndex(elem) ); + if (IsInList(elem)) + { + ListElem_t * RESTRICT pOldElem = &m_Memory[ elem ]; + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if ( pOldElem->m_Previous != InvalidIndex() ) + { + m_Memory[ pOldElem->m_Previous ].m_Next = pOldElem->m_Next; + } + else + { + m_Head = pOldElem->m_Next; + } + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if ( pOldElem->m_Next != InvalidIndex() ) + { + m_Memory[ pOldElem->m_Next ].m_Previous = pOldElem->m_Previous; + } + else + { + m_Tail = pOldElem->m_Previous; + } + + // This marks this node as not in the list, + // but not in the free list either + pOldElem->m_Previous = pOldElem->m_Next = elem; + + // One less puppy + --m_ElementCount; + } +} + +template +inline void CUtlLinkedList::LinkToHead( I elem ) +{ + LinkAfter( InvalidIndex(), elem ); +} + +template +inline void CUtlLinkedList::LinkToTail( I elem ) +{ + LinkBefore( InvalidIndex(), elem ); +} + + +//----------------------------------------------------------------------------- +// Class to drop in to replace a CUtlLinkedList that needs to be more memory agressive +//----------------------------------------------------------------------------- + +DECLARE_POINTER_HANDLE( UtlPtrLinkedListIndex_t ); // to enforce correct usage + +template < typename T > +class CUtlPtrLinkedList +{ +public: + CUtlPtrLinkedList() + : m_pFirst( NULL ), + m_nElems( 0 ) + { + COMPILE_TIME_ASSERT( sizeof(IndexType_t) == sizeof(Node_t *) ); + } + + ~CUtlPtrLinkedList() + { + RemoveAll(); + } + + typedef UtlPtrLinkedListIndex_t IndexType_t; + + T &operator[]( IndexType_t i ) + { + return (( Node_t * )i)->elem; + } + + const T &operator[]( IndexType_t i ) const + { + return (( Node_t * )i)->elem; + } + + IndexType_t AddToTail() + { + return DoInsertBefore( (IndexType_t)m_pFirst, NULL ); + } + + IndexType_t AddToTail( T const& src ) + { + return DoInsertBefore( (IndexType_t)m_pFirst, &src ); + } + + IndexType_t AddToHead() + { + IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, NULL ); + m_pFirst = ((Node_t *)result); + return result; + } + + IndexType_t AddToHead( T const& src ) + { + IndexType_t result = DoInsertBefore( (IndexType_t)m_pFirst, &src ); + m_pFirst = ((Node_t *)result); + return result; + } + + IndexType_t InsertBefore( IndexType_t before ) + { + return DoInsertBefore( before, NULL ); + } + + IndexType_t InsertAfter( IndexType_t after ) + { + Node_t *pBefore = ((Node_t *)after)->next; + return DoInsertBefore( pBefore, NULL ); + } + + IndexType_t InsertBefore( IndexType_t before, T const& src ) + { + return DoInsertBefore( before, &src ); + } + + IndexType_t InsertAfter( IndexType_t after, T const& src ) + { + Node_t *pBefore = ((Node_t *)after)->next; + return DoInsertBefore( pBefore, &src ); + } + + void Remove( IndexType_t elem ) + { + Node_t *p = (Node_t *)elem; + + if ( p->pNext == p ) + { + m_pFirst = NULL; + } + else + { + if ( m_pFirst == p ) + { + m_pFirst = p->pNext; + } + p->pNext->pPrev = p->pPrev; + p->pPrev->pNext = p->pNext; + } + + delete p; + m_nElems--; + } + + void RemoveAll() + { + Node_t *p = m_pFirst; + if ( p ) + { + do + { + Node_t *pNext = p->pNext; + delete p; + p = pNext; + } while( p != m_pFirst ); + } + + m_pFirst = NULL; + m_nElems = 0; + } + + int Count() const + { + return m_nElems; + } + + IndexType_t Head() const + { + return (IndexType_t)m_pFirst; + } + + IndexType_t Next( IndexType_t i ) const + { + Node_t *p = ((Node_t *)i)->pNext; + if ( p != m_pFirst ) + { + return (IndexType_t)p; + } + return NULL; + } + + bool IsValidIndex( IndexType_t i ) const + { + Node_t *p = ((Node_t *)i); + return ( p && p->pNext && p->pPrev ); + } + + inline static IndexType_t InvalidIndex() + { + return NULL; + } +private: + + struct Node_t + { + Node_t() {} + Node_t( const T &_elem ) : elem( _elem ) {} + + T elem; + Node_t *pPrev, *pNext; + }; + + Node_t *AllocNode( const T *pCopyFrom ) + { + MEM_ALLOC_CREDIT_CLASS(); + Node_t *p; + + if ( !pCopyFrom ) + { + p = new Node_t; + } + else + { + p = new Node_t( *pCopyFrom ); + } + + return p; + } + + IndexType_t DoInsertBefore( IndexType_t before, const T *pCopyFrom ) + { + Node_t *p = AllocNode( pCopyFrom ); + Node_t *pBefore = (Node_t *)before; + if ( pBefore ) + { + p->pNext = pBefore; + p->pPrev = pBefore->pPrev; + pBefore->pPrev = p; + p->pPrev->pNext = p; + } + else + { + Assert( !m_pFirst ); + m_pFirst = p->pNext = p->pPrev = p; + } + + m_nElems++; + return (IndexType_t)p; + } + + Node_t *m_pFirst; + unsigned m_nElems; +}; + +//----------------------------------------------------------------------------- + +#endif // UTLLINKEDLIST_H diff --git a/public/tier1/utlmap.h b/public/tier1/utlmap.h new file mode 100644 index 0000000..992a57e --- /dev/null +++ b/public/tier1/utlmap.h @@ -0,0 +1,203 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLMAP_H +#define UTLMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include "utlrbtree.h" + +//----------------------------------------------------------------------------- +// +// Purpose: An associative container. Pretty much identical to std::map. +// +//----------------------------------------------------------------------------- + +// This is a useful macro to iterate from start to end in order in a map +#define FOR_EACH_MAP( mapName, iteratorName ) \ + for ( int iteratorName = mapName.FirstInorder(); iteratorName != mapName.InvalidIndex(); iteratorName = mapName.NextInorder( iteratorName ) ) + +// faster iteration, but in an unspecified order +#define FOR_EACH_MAP_FAST( mapName, iteratorName ) \ + for ( int iteratorName = 0; iteratorName < mapName.MaxElement(); ++iteratorName ) if ( !mapName.IsValidIndex( iteratorName ) ) continue; else + +template +class CUtlMap +{ +public: + typedef K KeyType_t; + typedef T ElemType_t; + typedef I IndexType_t; + + // Less func typedef + // Returns true if the first parameter is "less" than the second + typedef bool (*LessFunc_t)( const KeyType_t &, const KeyType_t & ); + + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below + CUtlMap( int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0 ) + : m_Tree( growSize, initSize, CKeyLess( lessfunc ) ) + { + } + + CUtlMap( LessFunc_t lessfunc ) + : m_Tree( CKeyLess( lessfunc ) ) + { + } + + void EnsureCapacity( int num ) { m_Tree.EnsureCapacity( num ); } + + // gets particular elements + ElemType_t & Element( IndexType_t i ) { return m_Tree.Element( i ).elem; } + const ElemType_t & Element( IndexType_t i ) const { return m_Tree.Element( i ).elem; } + ElemType_t & operator[]( IndexType_t i ) { return m_Tree.Element( i ).elem; } + const ElemType_t & operator[]( IndexType_t i ) const { return m_Tree.Element( i ).elem; } + KeyType_t & Key( IndexType_t i ) { return m_Tree.Element( i ).key; } + const KeyType_t & Key( IndexType_t i ) const { return m_Tree.Element( i ).key; } + + + // Num elements + unsigned int Count() const { return m_Tree.Count(); } + + // Max "size" of the vector + IndexType_t MaxElement() const { return m_Tree.MaxElement(); } + + // Checks if a node is valid and in the map + bool IsValidIndex( IndexType_t i ) const { return m_Tree.IsValidIndex( i ); } + + // Checks if the map as a whole is valid + bool IsValid() const { return m_Tree.IsValid(); } + + // Invalid index + static IndexType_t InvalidIndex() { return CTree::InvalidIndex(); } + + // Sets the less func + void SetLessFunc( LessFunc_t func ) + { + m_Tree.SetLessFunc( CKeyLess( func ) ); + } + + // Insert method (inserts in order) + IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) + { + Node_t node; + node.key = key; + node.elem = insert; + return m_Tree.Insert( node ); + } + + IndexType_t Insert( const KeyType_t &key ) + { + Node_t node; + node.key = key; + return m_Tree.Insert( node ); + } + + // Find method + IndexType_t Find( const KeyType_t &key ) const + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Find( dummyNode ); + } + + // Remove methods + void RemoveAt( IndexType_t i ) { m_Tree.RemoveAt( i ); } + bool Remove( const KeyType_t &key ) + { + Node_t dummyNode; + dummyNode.key = key; + return m_Tree.Remove( dummyNode ); + } + + void RemoveAll( ) { m_Tree.RemoveAll(); } + void Purge( ) { m_Tree.Purge(); } + + // Iteration + IndexType_t FirstInorder() const { return m_Tree.FirstInorder(); } + IndexType_t NextInorder( IndexType_t i ) const { return m_Tree.NextInorder( i ); } + IndexType_t PrevInorder( IndexType_t i ) const { return m_Tree.PrevInorder( i ); } + IndexType_t LastInorder() const { return m_Tree.LastInorder(); } + + // If you change the search key, this can be used to reinsert the + // element into the map. + void Reinsert( const KeyType_t &key, IndexType_t i ) + { + m_Tree[i].key = key; + m_Tree.Reinsert(i); + } + + IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) + { + IndexType_t i = Find( key ); + if ( i != InvalidIndex() ) + { + Element( i ) = insert; + return i; + } + + return Insert( key, insert ); + } + + void Swap( CUtlMap< K, T, I > &that ) + { + m_Tree.Swap( that.m_Tree ); + } + + + struct Node_t + { + Node_t() + { + } + + Node_t( const Node_t &from ) + : key( from.key ), + elem( from.elem ) + { + } + + KeyType_t key; + ElemType_t elem; + }; + + class CKeyLess + { + public: + CKeyLess( LessFunc_t lessFunc ) : m_LessFunc(lessFunc) {} + + bool operator!() const + { + return !m_LessFunc; + } + + bool operator()( const Node_t &left, const Node_t &right ) const + { + return m_LessFunc( left.key, right.key ); + } + + LessFunc_t m_LessFunc; + }; + + typedef CUtlRBTree CTree; + + CTree *AccessTree() { return &m_Tree; } + +protected: + CTree m_Tree; +}; + +//----------------------------------------------------------------------------- + +#endif // UTLMAP_H diff --git a/public/tier1/utlmemory.h b/public/tier1/utlmemory.h new file mode 100644 index 0000000..10de675 --- /dev/null +++ b/public/tier1/utlmemory.h @@ -0,0 +1,1084 @@ +//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A growable memory class. +//===========================================================================// + +#ifndef UTLMEMORY_H +#define UTLMEMORY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/dbg.h" +#include +#include "tier0/platform.h" + +#include "tier0/memalloc.h" +#include "tier0/memdbgon.h" + +#pragma warning (disable:4100) +#pragma warning (disable:4514) + + +//----------------------------------------------------------------------------- + + +#ifdef UTLMEMORY_TRACK +#define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#else +#define UTLMEMORY_TRACK_ALLOC() ((void)0) +#define UTLMEMORY_TRACK_FREE() ((void)0) +#endif + + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, class I = int > +class CUtlMemory +{ +public: + // constructor, destructor + CUtlMemory( int nGrowSize = 0, int nInitSize = 0 ); + CUtlMemory( T* pMemory, int numElements ); + CUtlMemory( const T* pMemory, int numElements ); + ~CUtlMemory(); + + // Set the size by which the memory grows + void Init( int nGrowSize = 0, int nInitSize = 0 ); + + class Iterator_t + { + public: + Iterator_t( I i ) : index( i ) {} + I index; + + bool operator==( const Iterator_t it ) const { return index == it.index; } + bool operator!=( const Iterator_t it ) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); } + Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); } + I GetIndex( const Iterator_t &it ) const { return it.index; } + bool IsIdxAfter( I i, const Iterator_t &it ) const { return i > it.index; } + bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); } + Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); } + + // element access + T& operator[]( I i ); + const T& operator[]( I i ) const; + T& Element( I i ); + const T& Element( I i ) const; + + // Can we use this index? + bool IsIdxValid( I i ) const; + + // Specify the invalid ('null') index that we'll only return on failure + static const I INVALID_INDEX = ( I )-1; // For use with COMPILE_TIME_ASSERT + static I InvalidIndex() { return INVALID_INDEX; } + + // Gets the base address (can change when adding elements!) + T* Base(); + const T* Base() const; + + // Attaches the buffer to external memory.... + void SetExternalBuffer( T* pMemory, int numElements ); + void SetExternalBuffer( const T* pMemory, int numElements ); + void AssumeMemory( T *pMemory, int nSize ); + T* Detach(); + void *DetachMemory(); + + // Fast swap + void Swap( CUtlMemory< T, I > &mem ); + + // Switches the buffer from an external memory buffer to a reallocatable buffer + // Will copy the current contents of the external buffer to the reallocatable buffer + void ConvertToGrowableMemory( int nGrowSize ); + + // Size + int NumAllocated() const; + int Count() const; + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow( int num = 1 ); + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements + void Purge( int numElements ); + + // is the memory externally allocated? + bool IsExternallyAllocated() const; + + // is the memory read only? + bool IsReadOnly() const; + + // Set the size by which the memory grows + void SetGrowSize( int size ); + +protected: + void ValidateGrowSize() + { +#ifdef _X360 + if ( m_nGrowSize && m_nGrowSize != EXTERNAL_BUFFER_MARKER ) + { + // Max grow size at 128 bytes on XBOX + const int MAX_GROW = 128; + if ( m_nGrowSize * sizeof(T) > MAX_GROW ) + { + m_nGrowSize = max( 1, MAX_GROW / sizeof(T) ); + } + } +#endif + } + + enum + { + EXTERNAL_BUFFER_MARKER = -1, + EXTERNAL_CONST_BUFFER_MARKER = -2, + }; + + T* m_pMemory; + int m_nAllocationCount; + int m_nGrowSize; +}; + + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, size_t SIZE, class I = int > +class CUtlMemoryFixedGrowable : public CUtlMemory< T, I > +{ + typedef CUtlMemory< T, I > BaseClass; + +public: + CUtlMemoryFixedGrowable( int nGrowSize = 0, int nInitSize = SIZE ) : BaseClass( m_pFixedMemory, SIZE ) + { + Assert( nInitSize == 0 || nInitSize == SIZE ); + m_nMallocGrowSize = nGrowSize; + } + + void Grow( int nCount = 1 ) + { + if ( this->IsExternallyAllocated() ) + { + this->ConvertToGrowableMemory( m_nMallocGrowSize ); + } + BaseClass::Grow( nCount ); + } + + void EnsureCapacity( int num ) + { + if ( CUtlMemory::m_nAllocationCount >= num ) + return; + + if ( this->IsExternallyAllocated() ) + { + // Can't grow a buffer whose memory was externally allocated + this->ConvertToGrowableMemory( m_nMallocGrowSize ); + } + + BaseClass::EnsureCapacity( num ); + } + +private: + int m_nMallocGrowSize; + T m_pFixedMemory[ SIZE ]; +}; + +//----------------------------------------------------------------------------- +// The CUtlMemoryFixed class: +// A fixed memory class +//----------------------------------------------------------------------------- +template< typename T, size_t SIZE, int nAlignment = 0 > +class CUtlMemoryFixed +{ +public: + // constructor, destructor + CUtlMemoryFixed( int nGrowSize = 0, int nInitSize = 0 ) { Assert( nInitSize == 0 || nInitSize == SIZE ); } + CUtlMemoryFixed( T* pMemory, int numElements ) { Assert( 0 ); } + + // Can we use this index? + bool IsIdxValid( int i ) const { return (i >= 0) && (i < SIZE); } + + // Specify the invalid ('null') index that we'll only return on failure + static const int INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT + static int InvalidIndex() { return INVALID_INDEX; } + + // Gets the base address + T* Base() { if ( nAlignment == 0 ) return (T*)(&m_Memory[0]); else return (T*)AlignValue( &m_Memory[0], nAlignment ); } + const T* Base() const { if ( nAlignment == 0 ) return (T*)(&m_Memory[0]); else return (T*)AlignValue( &m_Memory[0], nAlignment ); } + + // element access + T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + + // Attaches the buffer to external memory.... + void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); } + + // Size + int NumAllocated() const { return SIZE; } + int Count() const { return SIZE; } + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow( int num = 1 ) { Assert( 0 ); } + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ) { Assert( num <= SIZE ); } + + // Memory deallocation + void Purge() {} + + // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryFixed) + void Purge( int numElements ) { Assert( 0 ); } + + // is the memory externally allocated? + bool IsExternallyAllocated() const { return false; } + + // Set the size by which the memory grows + void SetGrowSize( int size ) {} + + class Iterator_t + { + public: + Iterator_t( int i ) : index( i ) {} + int index; + bool operator==( const Iterator_t it ) const { return index == it.index; } + bool operator!=( const Iterator_t it ) const { return index != it.index; } + }; + Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); } + Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); } + int GetIndex( const Iterator_t &it ) const { return it.index; } + bool IsIdxAfter( int i, const Iterator_t &it ) const { return i > it.index; } + bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); } + Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); } + +private: + char m_Memory[ SIZE*sizeof(T) + nAlignment ]; +}; + +#ifdef _LINUX +#define REMEMBER_ALLOC_SIZE_FOR_VALGRIND 1 +#endif + +//----------------------------------------------------------------------------- +// The CUtlMemoryConservative class: +// A dynamic memory class that tries to minimize overhead (itself small, no custom grow factor) +//----------------------------------------------------------------------------- +template< typename T > +class CUtlMemoryConservative +{ + +public: + // constructor, destructor + CUtlMemoryConservative( int nGrowSize = 0, int nInitSize = 0 ) : m_pMemory( NULL ) + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + m_nCurAllocSize = 0; +#endif + + } + CUtlMemoryConservative( T* pMemory, int numElements ) { Assert( 0 ); } + ~CUtlMemoryConservative() { if ( m_pMemory ) free( m_pMemory ); } + + // Can we use this index? + bool IsIdxValid( int i ) const { return ( IsDebug() ) ? ( i >= 0 && i < NumAllocated() ) : ( i >= 0 ); } + static int InvalidIndex() { return -1; } + + // Gets the base address + T* Base() { return m_pMemory; } + const T* Base() const { return m_pMemory; } + + // element access + T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + + // Attaches the buffer to external memory.... + void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); } + + // Size + FORCEINLINE void RememberAllocSize( size_t sz ) + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + m_nCurAllocSize = sz; +#endif + } + + size_t AllocSize( void ) const + { +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + return m_nCurAllocSize; +#else + return ( m_pMemory ) ? g_pMemAlloc->GetSize( m_pMemory ) : 0; +#endif + } + + int NumAllocated() const + { + return AllocSize() / sizeof( T ); + } + int Count() const + { + return NumAllocated(); + } + + FORCEINLINE void ReAlloc( size_t sz ) + { + m_pMemory = (T*)realloc( m_pMemory, sz ); + RememberAllocSize( sz ); + } + // Grows the memory, so that at least allocated + num elements are allocated + void Grow( int num = 1 ) + { + int nCurN = NumAllocated(); + ReAlloc( ( nCurN + num ) * sizeof( T ) ); + } + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ) + { + size_t nSize = sizeof( T ) * MAX( num, Count() ); + ReAlloc( nSize ); + } + + // Memory deallocation + void Purge() + { + free( m_pMemory ); + RememberAllocSize( 0 ); + m_pMemory = NULL; + } + + // Purge all but the given number of elements + void Purge( int numElements ) { ReAlloc( numElements * sizeof(T) ); } + + // is the memory externally allocated? + bool IsExternallyAllocated() const { return false; } + + // Set the size by which the memory grows + void SetGrowSize( int size ) {} + + class Iterator_t + { + public: + Iterator_t( int i, int _limit ) : index( i ), limit( _limit ) {} + int index; + int limit; + bool operator==( const Iterator_t it ) const { return index == it.index; } + bool operator!=( const Iterator_t it ) const { return index != it.index; } + }; + Iterator_t First() const { int limit = NumAllocated(); return Iterator_t( limit ? 0 : InvalidIndex(), limit ); } + Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( ( it.index + 1 < it.limit ) ? it.index + 1 : InvalidIndex(), it.limit ); } + int GetIndex( const Iterator_t &it ) const { return it.index; } + bool IsIdxAfter( int i, const Iterator_t &it ) const { return i > it.index; } + bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ) && ( it.index < it.limit ); } + Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex(), 0 ); } + +private: + T *m_pMemory; +#ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND + size_t m_nCurAllocSize; +#endif + +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class I > +CUtlMemory::CUtlMemory( int nGrowSize, int nInitAllocationCount ) : m_pMemory(0), + m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize ) +{ + ValidateGrowSize(); + Assert( nGrowSize >= 0 ); + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); + } +} + +template< class T, class I > +CUtlMemory::CUtlMemory( T* pMemory, int numElements ) : m_pMemory(pMemory), + m_nAllocationCount( numElements ) +{ + // Special marker indicating externally supplied modifyable memory + m_nGrowSize = EXTERNAL_BUFFER_MARKER; +} + +template< class T, class I > +CUtlMemory::CUtlMemory( const T* pMemory, int numElements ) : m_pMemory( (T*)pMemory ), + m_nAllocationCount( numElements ) +{ + // Special marker indicating externally supplied modifyable memory + m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; +} + +template< class T, class I > +CUtlMemory::~CUtlMemory() +{ + Purge(); +} + +template< class T, class I > +void CUtlMemory::Init( int nGrowSize /*= 0*/, int nInitSize /*= 0*/ ) +{ + Purge(); + + m_nGrowSize = nGrowSize; + m_nAllocationCount = nInitSize; + ValidateGrowSize(); + Assert( nGrowSize >= 0 ); + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); + } +} + +//----------------------------------------------------------------------------- +// Fast swap +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::Swap( CUtlMemory &mem ) +{ + V_swap( m_nGrowSize, mem.m_nGrowSize ); + V_swap( m_pMemory, mem.m_pMemory ); + V_swap( m_nAllocationCount, mem.m_nAllocationCount ); +} + + +//----------------------------------------------------------------------------- +// Switches the buffer from an external memory buffer to a reallocatable buffer +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::ConvertToGrowableMemory( int nGrowSize ) +{ + if ( !IsExternallyAllocated() ) + return; + + m_nGrowSize = nGrowSize; + if (m_nAllocationCount) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + + int nNumBytes = m_nAllocationCount * sizeof(T); + T *pMemory = (T*)malloc( nNumBytes ); + memcpy( pMemory, m_pMemory, nNumBytes ); + m_pMemory = pMemory; + } + else + { + m_pMemory = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::SetExternalBuffer( T* pMemory, int numElements ) +{ + // Blow away any existing allocated memory + Purge(); + + m_pMemory = pMemory; + m_nAllocationCount = numElements; + + // Indicate that we don't own the memory + m_nGrowSize = EXTERNAL_BUFFER_MARKER; +} + +template< class T, class I > +void CUtlMemory::SetExternalBuffer( const T* pMemory, int numElements ) +{ + // Blow away any existing allocated memory + Purge(); + + m_pMemory = const_cast( pMemory ); + m_nAllocationCount = numElements; + + // Indicate that we don't own the memory + m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; +} + +template< class T, class I > +void CUtlMemory::AssumeMemory( T* pMemory, int numElements ) +{ + // Blow away any existing allocated memory + Purge(); + + // Simply take the pointer but don't mark us as external + m_pMemory = pMemory; + m_nAllocationCount = numElements; +} + +template< class T, class I > +void *CUtlMemory::DetachMemory() +{ + if ( IsExternallyAllocated() ) + return NULL; + + void *pMemory = m_pMemory; + m_pMemory = 0; + m_nAllocationCount = 0; + return pMemory; +} + +template< class T, class I > +inline T* CUtlMemory::Detach() +{ + return (T*)DetachMemory(); +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< class T, class I > +inline T& CUtlMemory::operator[]( I i ) +{ + Assert( !IsReadOnly() ); + Assert( IsIdxValid(i) ); + return m_pMemory[i]; +} + +template< class T, class I > +inline const T& CUtlMemory::operator[]( I i ) const +{ + Assert( IsIdxValid(i) ); + return m_pMemory[i]; +} + +template< class T, class I > +inline T& CUtlMemory::Element( I i ) +{ + Assert( !IsReadOnly() ); + Assert( IsIdxValid(i) ); + return m_pMemory[i]; +} + +template< class T, class I > +inline const T& CUtlMemory::Element( I i ) const +{ + Assert( IsIdxValid(i) ); + return m_pMemory[i]; +} + + +//----------------------------------------------------------------------------- +// is the memory externally allocated? +//----------------------------------------------------------------------------- +template< class T, class I > +bool CUtlMemory::IsExternallyAllocated() const +{ + return (m_nGrowSize < 0); +} + + +//----------------------------------------------------------------------------- +// is the memory read only? +//----------------------------------------------------------------------------- +template< class T, class I > +bool CUtlMemory::IsReadOnly() const +{ + return (m_nGrowSize == EXTERNAL_CONST_BUFFER_MARKER); +} + + +template< class T, class I > +void CUtlMemory::SetGrowSize( int nSize ) +{ + Assert( !IsExternallyAllocated() ); + Assert( nSize >= 0 ); + m_nGrowSize = nSize; + ValidateGrowSize(); +} + + +//----------------------------------------------------------------------------- +// Gets the base address (can change when adding elements!) +//----------------------------------------------------------------------------- +template< class T, class I > +inline T* CUtlMemory::Base() +{ + Assert( !IsReadOnly() ); + return m_pMemory; +} + +template< class T, class I > +inline const T *CUtlMemory::Base() const +{ + return m_pMemory; +} + + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- +template< class T, class I > +inline int CUtlMemory::NumAllocated() const +{ + return m_nAllocationCount; +} + +template< class T, class I > +inline int CUtlMemory::Count() const +{ + return m_nAllocationCount; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< class T, class I > +inline bool CUtlMemory::IsIdxValid( I i ) const +{ + // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). + // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. + long x = i; + return ( x >= 0 ) && ( x < m_nAllocationCount ); +} + +//----------------------------------------------------------------------------- +// Grows the memory +//----------------------------------------------------------------------------- +inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem ) +{ + if ( nGrowSize ) + { + nAllocationCount = ((1 + ((nNewSize - 1) / nGrowSize)) * nGrowSize); + } + else + { + if ( !nAllocationCount ) + { + // Compute an allocation which is at least as big as a cache line... + nAllocationCount = (31 + nBytesItem) / nBytesItem; + } + + while (nAllocationCount < nNewSize) + { +#ifndef _X360 + nAllocationCount *= 2; +#else + int nNewAllocationCount = ( nAllocationCount * 9) / 8; // 12.5 % + if ( nNewAllocationCount > nAllocationCount ) + nAllocationCount = nNewAllocationCount; + else + nAllocationCount *= 2; +#endif + } + } + + return nAllocationCount; +} + +template< class T, class I > +void CUtlMemory::Grow( int num ) +{ + Assert( num > 0 ); + + if ( IsExternallyAllocated() ) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + // Make sure we have at least numallocated + num allocations. + // Use the grow rules specified for this memory (in m_nGrowSize) + int nAllocationRequested = m_nAllocationCount + num; + + UTLMEMORY_TRACK_FREE(); + + int nNewAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T) ); + + // if m_nAllocationRequested wraps index type I, recalculate + if ( ( int )( I )nNewAllocationCount < nAllocationRequested ) + { + if ( ( int )( I )nNewAllocationCount == 0 && ( int )( I )( nNewAllocationCount - 1 ) >= nAllocationRequested ) + { + --nNewAllocationCount; // deal w/ the common case of m_nAllocationCount == MAX_USHORT + 1 + } + else + { + if ( ( int )( I )nAllocationRequested != nAllocationRequested ) + { + // we've been asked to grow memory to a size s.t. the index type can't address the requested amount of memory + Assert( 0 ); + return; + } + while ( ( int )( I )nNewAllocationCount < nAllocationRequested ) + { + nNewAllocationCount = ( nNewAllocationCount + nAllocationRequested ) / 2; + } + } + } + + m_nAllocationCount = nNewAllocationCount; + + UTLMEMORY_TRACK_ALLOC(); + + if (m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); + Assert( m_pMemory ); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); + Assert( m_pMemory ); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, class I > +inline void CUtlMemory::EnsureCapacity( int num ) +{ + if (m_nAllocationCount >= num) + return; + + if ( IsExternallyAllocated() ) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + m_nAllocationCount = num; + + UTLMEMORY_TRACK_ALLOC(); + + if (m_pMemory) + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); + } +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMemory::Purge() +{ + if ( !IsExternallyAllocated() ) + { + if (m_pMemory) + { + UTLMEMORY_TRACK_FREE(); + free( (void*)m_pMemory ); + m_pMemory = 0; + } + m_nAllocationCount = 0; + } +} + +template< class T, class I > +void CUtlMemory::Purge( int numElements ) +{ + Assert( numElements >= 0 ); + + if( numElements > m_nAllocationCount ) + { + // Ensure this isn't a grow request in disguise. + Assert( numElements <= m_nAllocationCount ); + return; + } + + // If we have zero elements, simply do a purge: + if( numElements == 0 ) + { + Purge(); + return; + } + + if ( IsExternallyAllocated() ) + { + // Can't shrink a buffer whose memory was externally allocated, fail silently like purge + return; + } + + // If the number of elements is the same as the allocation count, we are done. + if( numElements == m_nAllocationCount ) + { + return; + } + + + if( !m_pMemory ) + { + // Allocation count is non zero, but memory is null. + Assert( m_pMemory ); + return; + } + + UTLMEMORY_TRACK_FREE(); + + m_nAllocationCount = numElements; + + UTLMEMORY_TRACK_ALLOC(); + + // Allocation count > 0, shrink it down. + MEM_ALLOC_CREDIT_CLASS(); + m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); +} + +//----------------------------------------------------------------------------- +// The CUtlMemory class: +// A growable memory class which doubles in size by default. +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +class CUtlMemoryAligned : public CUtlMemory +{ +public: + // constructor, destructor + CUtlMemoryAligned( int nGrowSize = 0, int nInitSize = 0 ); + CUtlMemoryAligned( T* pMemory, int numElements ); + CUtlMemoryAligned( const T* pMemory, int numElements ); + ~CUtlMemoryAligned(); + + // Attaches the buffer to external memory.... + void SetExternalBuffer( T* pMemory, int numElements ); + void SetExternalBuffer( const T* pMemory, int numElements ); + + // Grows the memory, so that at least allocated + num elements are allocated + void Grow( int num = 1 ); + + // Makes sure we've got at least this much memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryAligned) + void Purge( int numElements ) { Assert( 0 ); } + +private: + void *Align( const void *pAddr ); +}; + + +//----------------------------------------------------------------------------- +// Aligns a pointer +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +void *CUtlMemoryAligned::Align( const void *pAddr ) +{ + size_t nAlignmentMask = nAlignment - 1; + return (void*)( ((size_t)pAddr + nAlignmentMask) & (~nAlignmentMask) ); +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned( int nGrowSize, int nInitAllocationCount ) +{ + CUtlMemory::m_pMemory = 0; + CUtlMemory::m_nAllocationCount = nInitAllocationCount; + CUtlMemory::m_nGrowSize = nGrowSize; + this->ValidateGrowSize(); + + // Alignment must be a power of two + COMPILE_TIME_ASSERT( (nAlignment & (nAlignment-1)) == 0 ); + Assert( (nGrowSize >= 0) && (nGrowSize != CUtlMemory::EXTERNAL_BUFFER_MARKER) ); + if ( CUtlMemory::m_nAllocationCount ) + { + UTLMEMORY_TRACK_ALLOC(); + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)_aligned_malloc( nInitAllocationCount * sizeof(T), nAlignment ); + } +} + +template< class T, int nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned( T* pMemory, int numElements ) +{ + // Special marker indicating externally supplied memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; + + CUtlMemory::m_pMemory = (T*)Align( pMemory ); + CUtlMemory::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory::m_pMemory ) / sizeof(T); +} + +template< class T, int nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned( const T* pMemory, int numElements ) +{ + // Special marker indicating externally supplied memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; + + CUtlMemory::m_pMemory = (T*)Align( pMemory ); + CUtlMemory::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory::m_pMemory ) / sizeof(T); +} + +template< class T, int nAlignment > +CUtlMemoryAligned::~CUtlMemoryAligned() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// Attaches the buffer to external memory.... +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +void CUtlMemoryAligned::SetExternalBuffer( T* pMemory, int numElements ) +{ + // Blow away any existing allocated memory + Purge(); + + CUtlMemory::m_pMemory = (T*)Align( pMemory ); + CUtlMemory::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory::m_pMemory ) / sizeof(T); + + // Indicate that we don't own the memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; +} + +template< class T, int nAlignment > +void CUtlMemoryAligned::SetExternalBuffer( const T* pMemory, int numElements ) +{ + // Blow away any existing allocated memory + Purge(); + + CUtlMemory::m_pMemory = (T*)Align( pMemory ); + CUtlMemory::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory::m_pMemory ) / sizeof(T); + + // Indicate that we don't own the memory + CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; +} + + +//----------------------------------------------------------------------------- +// Grows the memory +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +void CUtlMemoryAligned::Grow( int num ) +{ + Assert( num > 0 ); + + if ( this->IsExternallyAllocated() ) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + // Make sure we have at least numallocated + num allocations. + // Use the grow rules specified for this memory (in m_nGrowSize) + int nAllocationRequested = CUtlMemory::m_nAllocationCount + num; + + CUtlMemory::m_nAllocationCount = UtlMemory_CalcNewAllocationCount( CUtlMemory::m_nAllocationCount, CUtlMemory::m_nGrowSize, nAllocationRequested, sizeof(T) ); + + UTLMEMORY_TRACK_ALLOC(); + + if ( CUtlMemory::m_pMemory ) + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_ReallocAligned( CUtlMemory::m_pMemory, CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment ); + Assert( CUtlMemory::m_pMemory ); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_AllocAligned( CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment ); + Assert( CUtlMemory::m_pMemory ); + } +} + + +//----------------------------------------------------------------------------- +// Makes sure we've got at least this much memory +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +inline void CUtlMemoryAligned::EnsureCapacity( int num ) +{ + if ( CUtlMemory::m_nAllocationCount >= num ) + return; + + if ( this->IsExternallyAllocated() ) + { + // Can't grow a buffer whose memory was externally allocated + Assert(0); + return; + } + + UTLMEMORY_TRACK_FREE(); + + CUtlMemory::m_nAllocationCount = num; + + UTLMEMORY_TRACK_ALLOC(); + + if ( CUtlMemory::m_pMemory ) + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_ReallocAligned( CUtlMemory::m_pMemory, CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment ); + } + else + { + MEM_ALLOC_CREDIT_CLASS(); + CUtlMemory::m_pMemory = (T*)MemAlloc_AllocAligned( CUtlMemory::m_nAllocationCount * sizeof(T), nAlignment ); + } +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- +template< class T, int nAlignment > +void CUtlMemoryAligned::Purge() +{ + if ( !this->IsExternallyAllocated() ) + { + if ( CUtlMemory::m_pMemory ) + { + UTLMEMORY_TRACK_FREE(); + MemAlloc_FreeAligned( CUtlMemory::m_pMemory ); + CUtlMemory::m_pMemory = 0; + } + CUtlMemory::m_nAllocationCount = 0; + } +} + +#include "tier0/memdbgoff.h" + +#endif // UTLMEMORY_H diff --git a/public/tier1/utlmultilist.h b/public/tier1/utlmultilist.h new file mode 100644 index 0000000..68e2a98 --- /dev/null +++ b/public/tier1/utlmultilist.h @@ -0,0 +1,769 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Multiple linked list container class +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLMULTILIST_H +#define UTLMULTILIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "utllinkedlist.h" + +// memdbgon must be the last include file in a .h file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// class CUtlMultiList: +// description: +// A lovely index-based linked list! T is the class type, I is the index +// type, which usually should be an unsigned short or smaller. +// This list can contain multiple lists +//----------------------------------------------------------------------------- +template +class CUtlMultiList +{ +protected: + // What the linked list element looks like + struct ListElem_t + { + T m_Element; + I m_Previous; + I m_Next; + }; + + struct List_t + { + I m_Head; + I m_Tail; + I m_Count; + }; + + typedef CUtlMemory M; // Keep naming similar to CUtlLinkedList +public: + typedef I ListHandle_t; + + // constructor, destructor + CUtlMultiList( int growSize = 0, int initSize = 0 ); + CUtlMultiList( void *pMemory, int memsize ); + ~CUtlMultiList( ); + + // gets particular elements + T& Element( I i ); + T const& Element( I i ) const; + T& operator[]( I i ); + T const& operator[]( I i ) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity( int num ); + + // Memory deallocation + void Purge(); + + // List Creation/deletion + ListHandle_t CreateList(); + void DestroyList( ListHandle_t list ); + bool IsValidList( ListHandle_t list ) const; + + // Insertion methods (call default constructor).... + I InsertBefore( ListHandle_t list, I before ); + I InsertAfter( ListHandle_t list, I after ); + I AddToHead( ListHandle_t list ); + I AddToTail( ListHandle_t list ); + + // Insertion methods (call copy constructor).... + I InsertBefore( ListHandle_t list, I before, T const& src ); + I InsertAfter( ListHandle_t list, I after, T const& src ); + I AddToHead( ListHandle_t list, T const& src ); + I AddToTail( ListHandle_t list, T const& src ); + + // Removal methods + void Remove( ListHandle_t list, I elem ); + + // Removes all items in a single list + void RemoveAll( ListHandle_t list ); + + // Removes all items in all lists + void RemoveAll(); + + // Allocation/deallocation methods + // NOTE: To free, it must *not* be in a list! + I Alloc( ); + void Free( I elem ); + + // list modification + void LinkBefore( ListHandle_t list, I before, I elem ); + void LinkAfter( ListHandle_t list, I after, I elem ); + void Unlink( ListHandle_t list, I elem ); + void LinkToHead( ListHandle_t list, I elem ); + void LinkToTail( ListHandle_t list, I elem ); + + // invalid index + static I InvalidIndex() { return (I)~0; } + static bool IndexInRange( int index ); + static size_t ElementSize() { return sizeof(ListElem_t); } + + // list statistics + int Count( ListHandle_t list ) const; + int TotalCount( ) const; + I MaxElementIndex() const; + + // Traversing the list + I Head( ListHandle_t list ) const; + I Tail( ListHandle_t list ) const; + I Previous( I element ) const; + I Next( I element ) const; + + // Are nodes in a list or valid? + bool IsValidIndex( I i ) const; + bool IsInList( I i ) const; + +protected: + // constructs the class + void ConstructList( ); + + // Gets at the list element.... + ListElem_t& InternalElement( I i ) { return m_Memory[i]; } + ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } + + // A test for debug mode only... + bool IsElementInList( ListHandle_t list, I elem ) const; + + // copy constructors not allowed + CUtlMultiList( CUtlMultiList const& list ) { Assert(0); } + + M m_Memory; + CUtlLinkedList m_List; + I* m_pElementList; + + I m_FirstFree; + I m_TotalElements; + int m_MaxElementIndex; // The number allocated (use int so we can catch overflow) + + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + +#ifdef _DEBUG + // Allocate space for the element list (which list is each element in) + if (m_Memory.NumAllocated() > 0) + { + if (!m_pElementList) + { + m_pElementList = (I*)malloc( m_Memory.NumAllocated() * sizeof(I) ); + } + else + { + m_pElementList = (I*)realloc( m_pElementList, m_Memory.NumAllocated() * sizeof(I) ); + } + } +#endif + } + + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + ListElem_t *m_pElements; +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlMultiList::CUtlMultiList( int growSize, int initSize ) : + m_Memory(growSize, initSize), m_pElementList(0) +{ + ConstructList(); +} + +template +CUtlMultiList::CUtlMultiList( void* pMemory, int memsize ) : + m_Memory((ListElem_t *)pMemory, memsize/sizeof(ListElem_t)), m_pElementList(0) +{ + ConstructList(); +} + +template +CUtlMultiList::~CUtlMultiList( ) +{ + RemoveAll(); + if (m_pElementList) + free(m_pElementList); +} + +template +void CUtlMultiList::ConstructList( ) +{ + m_FirstFree = InvalidIndex(); + m_TotalElements = 0; + m_MaxElementIndex = 0; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlMultiList::Element( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlMultiList::Element( I i ) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlMultiList::operator[]( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline T const& CUtlMultiList::operator[]( I i ) const +{ + return m_Memory[i].m_Element; +} + + +//----------------------------------------------------------------------------- +// list creation/destruction +//----------------------------------------------------------------------------- +template +typename CUtlMultiList::ListHandle_t CUtlMultiList::CreateList() +{ + ListHandle_t l = m_List.AddToTail(); + m_List[l].m_Head = m_List[l].m_Tail = InvalidIndex(); + m_List[l].m_Count = 0; + return l; +} + +template +void CUtlMultiList::DestroyList( ListHandle_t list ) +{ + Assert( IsValidList(list) ); + RemoveAll( list ); + m_List.Remove(list); +} + +template +bool CUtlMultiList::IsValidList( ListHandle_t list ) const +{ + return m_List.IsValidIndex(list); +} + + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- +template +inline int CUtlMultiList::TotalCount() const +{ + return m_TotalElements; +} + +template +inline int CUtlMultiList::Count( ListHandle_t list ) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Count; +} + +template +inline I CUtlMultiList::MaxElementIndex() const +{ + return m_MaxElementIndex; +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- +template +inline I CUtlMultiList::Head(ListHandle_t list) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Head; +} + +template +inline I CUtlMultiList::Tail(ListHandle_t list) const +{ + Assert( IsValidList(list) ); + return m_List[list].m_Tail; +} + +template +inline I CUtlMultiList::Previous( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Previous; +} + +template +inline I CUtlMultiList::Next( I i ) const +{ + Assert( IsValidIndex(i) ); + return InternalElement(i).m_Next; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- + +template +inline bool CUtlMultiList::IndexInRange( int index ) // Static method +{ + // Since I is not necessarily the type returned by M (int), we need to check that M returns + // indices which are representable by I. A common case is 'I === unsigned short', in which case + // case CUtlMemory will have 'InvalidIndex == (int)-1' (which casts to 65535 in I), and will + // happily return elements at index 65535 and above. + + // Do a couple of static checks here: the invalid index should be (I)~0 given how we use m_MaxElementIndex, + // and 'I' should be unsigned (to avoid signed arithmetic errors for plausibly exhaustible ranges). + COMPILE_TIME_ASSERT( (I)M::INVALID_INDEX == (I)~0 ); + COMPILE_TIME_ASSERT( ( sizeof(I) > 2 ) || ( ( (I)-1 ) > 0 ) ); + + return ( ( (I)index == index ) && ( (I)index != InvalidIndex() ) ); +} + +template +inline bool CUtlMultiList::IsValidIndex( I i ) const +{ + // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). + // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. + long x = i; + + return (i < m_MaxElementIndex) && (x >= 0) && + ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); +} + +template +inline bool CUtlMultiList::IsInList( I i ) const +{ + // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). + // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. + long x = i; + return (i < m_MaxElementIndex) && (x >= 0) && (Previous(i) != i); +} + + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlMultiList::EnsureCapacity( int num ) +{ + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- +template +void CUtlMultiList::Purge() +{ + RemoveAll(); + m_List.Purge(); + m_Memory.Purge( ); + m_List.Purge(); + m_FirstFree = InvalidIndex(); + m_TotalElements = 0; + m_MaxElementIndex = 0; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlMultiList::Alloc( ) +{ + I elem; + if (m_FirstFree == InvalidIndex()) + { + // We can overflow before the utlmemory overflows, since we have have I != int + if ( !IndexInRange( m_MaxElementIndex ) ) + { + ExecuteNTimes( 10, Warning( "CUtlMultiList overflow! (exhausted index range)\n" ) ); + return InvalidIndex(); + } + + // Nothing in the free list; add. + // Since nothing is in the free list, m_TotalElements == total # of elements + // the list knows about. + if (m_MaxElementIndex == m_Memory.NumAllocated()) + { + m_Memory.Grow(); + ResetDbgInfo(); + + if ( m_MaxElementIndex >= m_Memory.NumAllocated() ) + { + ExecuteNTimes( 10, Warning( "CUtlMultiList overflow! (exhausted memory allocator)\n" ) ); + return InvalidIndex(); + } + } + + elem = (I)m_MaxElementIndex; + ++m_MaxElementIndex; + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalElement(m_FirstFree).m_Next; + } + + // Mark the element as not being in a list + InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; + + ++m_TotalElements; + + Construct( &Element(elem) ); + + return elem; +} + +template +void CUtlMultiList::Free( I elem ) +{ + Assert( IsValidIndex(elem) && !IsInList(elem) ); + Destruct( &Element(elem) ); + InternalElement(elem).m_Next = m_FirstFree; + m_FirstFree = elem; + --m_TotalElements; +} + + +//----------------------------------------------------------------------------- +// A test for debug mode only... +//----------------------------------------------------------------------------- +template +inline bool CUtlMultiList::IsElementInList( ListHandle_t list, I elem ) const +{ + if (!m_pElementList) + return true; + + return m_pElementList[elem] == list; +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- +template +void CUtlMultiList::LinkBefore( ListHandle_t list, I before, I elem ) +{ + Assert( IsValidIndex(elem) && IsValidList(list) ); + + // Unlink it if it's in the list at the moment + Unlink(list, elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *after* our newly linked one is the one we linked before. + newElem.m_Next = before; + + if (before == InvalidIndex()) + { + // In this case, we're linking to the end of the list, so reset the tail + newElem.m_Previous = m_List[list].m_Tail; + m_List[list].m_Tail = elem; + } + else + { + // Here, we're not linking to the end. Set the prev pointer to point to + // the element we're linking. + Assert( IsInList(before) ); + ListElem_t& beforeElem = InternalElement(before); + newElem.m_Previous = beforeElem.m_Previous; + beforeElem.m_Previous = elem; + } + + // Reset the head if we linked to the head of the list + if (newElem.m_Previous == InvalidIndex()) + m_List[list].m_Head = elem; + else + InternalElement(newElem.m_Previous).m_Next = elem; + + // one more element baby + ++m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = list; +} + +template +void CUtlMultiList::LinkAfter( ListHandle_t list, I after, I elem ) +{ + Assert( IsValidIndex(elem) ); + + // Unlink it if it's in the list at the moment + Unlink(list, elem); + + ListElem_t& newElem = InternalElement(elem); + + // The element *before* our newly linked one is the one we linked after + newElem.m_Previous = after; + if (after == InvalidIndex()) + { + // In this case, we're linking to the head of the list, reset the head + newElem.m_Next = m_List[list].m_Head; + m_List[list].m_Head = elem; + } + else + { + // Here, we're not linking to the end. Set the next pointer to point to + // the element we're linking. + Assert( IsInList(after) ); + ListElem_t& afterElem = InternalElement(after); + newElem.m_Next = afterElem.m_Next; + afterElem.m_Next = elem; + } + + // Reset the tail if we linked to the tail of the list + if (newElem.m_Next == InvalidIndex()) + m_List[list].m_Tail = elem; + else + InternalElement(newElem.m_Next).m_Previous = elem; + + // one more element baby + ++m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = list; +} + +template +void CUtlMultiList::Unlink( ListHandle_t list, I elem ) +{ + Assert( IsValidIndex(elem) && IsValidList(list) ); + + if (IsInList(elem)) + { + // Make sure the element is in the right list + Assert( IsElementInList( list, elem ) ); + ListElem_t& oldElem = InternalElement(elem); + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if (oldElem.m_Previous != InvalidIndex()) + InternalElement(oldElem.m_Previous).m_Next = oldElem.m_Next; + else + m_List[list].m_Head = oldElem.m_Next; + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if (oldElem.m_Next != InvalidIndex()) + InternalElement(oldElem.m_Next).m_Previous = oldElem.m_Previous; + else + m_List[list].m_Tail = oldElem.m_Previous; + + // This marks this node as not in the list, + // but not in the free list either + oldElem.m_Previous = oldElem.m_Next = elem; + + // One less puppy + --m_List[list].m_Count; + + // Store the element into the list + if (m_pElementList) + m_pElementList[elem] = m_List.InvalidIndex(); + } +} + +template +inline void CUtlMultiList::LinkToHead( ListHandle_t list, I elem ) +{ + LinkAfter( list, InvalidIndex(), elem ); +} + +template +inline void CUtlMultiList::LinkToTail( ListHandle_t list, I elem ) +{ + LinkBefore( list, InvalidIndex(), elem ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses default constructor) +//----------------------------------------------------------------------------- +template +I CUtlMultiList::InsertBefore( ListHandle_t list, I before ) +{ + // Make a new node + I newNode = Alloc(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkBefore( list, before, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +I CUtlMultiList::InsertAfter( ListHandle_t list, I after ) +{ + // Make a new node + I newNode = Alloc(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkAfter( list, after, newNode ); + + // Construct the data + Construct( &Element(newNode) ); + + return newNode; +} + +template +inline I CUtlMultiList::AddToHead( ListHandle_t list ) +{ + return InsertAfter( list, InvalidIndex() ); +} + +template +inline I CUtlMultiList::AddToTail( ListHandle_t list ) +{ + return InsertBefore( list, InvalidIndex() ); +} + + +//----------------------------------------------------------------------------- +// Insertion methods; allocates and links (uses copy constructor) +//----------------------------------------------------------------------------- +template +I CUtlMultiList::InsertBefore( ListHandle_t list, I before, T const& src ) +{ + // Make a new node + I newNode = Alloc(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkBefore( list, before, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +I CUtlMultiList::InsertAfter( ListHandle_t list, I after, T const& src ) +{ + // Make a new node + I newNode = Alloc(); + if ( newNode == InvalidIndex() ) + return newNode; + + // Link it in + LinkAfter( list, after, newNode ); + + // Construct the data + CopyConstruct( &Element(newNode), src ); + + return newNode; +} + +template +inline I CUtlMultiList::AddToHead( ListHandle_t list, T const& src ) +{ + return InsertAfter( list, InvalidIndex(), src ); +} + +template +inline I CUtlMultiList::AddToTail( ListHandle_t list, T const& src ) +{ + return InsertBefore( list, InvalidIndex(), src ); +} + + +//----------------------------------------------------------------------------- +// Removal methods +//----------------------------------------------------------------------------- +template +void CUtlMultiList::Remove( ListHandle_t list, I elem ) +{ + if (IsInList(elem)) + Unlink(list, elem); + Free( elem ); +} + +// Removes all items in a single list +template +void CUtlMultiList::RemoveAll( ListHandle_t list ) +{ + Assert( IsValidList(list) ); + I i = Head(list); + I next; + while( i != InvalidIndex() ) + { + next = Next(i); + Remove(list, i); + i = next; + } +} + + +template +void CUtlMultiList::RemoveAll() +{ + if (m_MaxElementIndex == 0) + return; + + // Put everything into the free list + I prev = InvalidIndex(); + for (int i = (int)m_MaxElementIndex; --i >= 0; ) + { + // Invoke the destructor + if (IsValidIndex((I)i)) + Destruct( &Element((I)i) ); + + // next points to the next free list item + InternalElement((I)i).m_Next = prev; + + // Indicates it's in the free list + InternalElement((I)i).m_Previous = (I)i; + prev = (I)i; + } + + // First free points to the first element + m_FirstFree = 0; + + // Clear everything else out + for (I list = m_List.Head(); list != m_List.InvalidIndex(); list = m_List.Next(list) ) + { + m_List[list].m_Head = InvalidIndex(); + m_List[list].m_Tail = InvalidIndex(); + m_List[list].m_Count = 0; + } + + m_TotalElements = 0; +} + + +#include "tier0/memdbgoff.h" + +#endif // UTLMULTILIST_H diff --git a/public/tier1/utlntree.h b/public/tier1/utlntree.h new file mode 100644 index 0000000..9995005 --- /dev/null +++ b/public/tier1/utlntree.h @@ -0,0 +1,624 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: N-way tree container class +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLNTREE_H +#define UTLNTREE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "utlmemory.h" +#include "tier0/dbg.h" + + +#define INVALID_NTREE_IDX ((I)~0) + +//----------------------------------------------------------------------------- +// class CUtlNTree: +// description: +// A lovely index-based linked list! T is the class type, I is the index +// type, which usually should be an unsigned short or smaller. +//----------------------------------------------------------------------------- +template +class CUtlNTree +{ +public: + typedef T ElemType_t; + typedef I IndexType_t; + + // constructor, destructor + CUtlNTree( int growSize = 0, int initSize = 0 ); + CUtlNTree( void *pMemory, int memsize ); + ~CUtlNTree( ); + + // gets particular elements + T& Element( I i ); + const T& Element( I i ) const; + T& operator[]( I i ); + const T& operator[]( I i ) const; + + // Make sure we have a particular amount of memory + void EnsureCapacity( int num ); + + // Clears the tree, doesn't deallocate memory + void RemoveAll(); + + // Memory deallocation + void Purge(); + + // Allocation/deallocation methods + I Alloc( ); + void Free( I elem ); + void FreeSubTree( I elem ); + + // list modification + void SetRoot( I root ); + void LinkChildBefore( I parent, I before, I elem ); + void LinkChildAfter( I parent, I after, I elem ); + void Unlink( I elem ); + + // Alloc + link combined + I InsertChildBefore( I parent, I before ); + I InsertChildAfter( I parent, I after ); + I InsertChildBefore( I parent, I before, const T &elem ); + I InsertChildAfter( I parent, I after, const T &elem ); + + // Unlink + free combined + void Remove( I elem ); + void RemoveSubTree( I elem ); + + // invalid index + inline static I InvalidIndex() { return INVALID_NTREE_IDX; } + inline static size_t ElementSize() { return sizeof(Node_t); } + + // list statistics + int Count() const; + I MaxElementIndex() const; + + // Traversing the list + I Root() const; + I FirstChild( I i ) const; + I PrevSibling( I i ) const; + I NextSibling( I i ) const; + I Parent( I i ) const; + + // Are nodes in the list or valid? + bool IsValidIndex( I i ) const; + bool IsInTree( I i ) const; + +protected: + // What the linked list element looks like + struct Node_t + { + T m_Element; + I m_Parent; + I m_FirstChild; + I m_PrevSibling; + I m_NextSibling; + + private: + // No copy constructor for these... + Node_t( const Node_t& ); + }; + + // constructs the class + void ConstructList(); + + // Allocates the element, doesn't call the constructor + I AllocInternal(); + + // Gets at the node element.... + Node_t& InternalNode( I i ) { return m_Memory[i]; } + const Node_t& InternalNode( I i ) const { return m_Memory[i]; } + + void ResetDbgInfo() + { + m_pElements = m_Memory.Base(); + } + + // copy constructors not allowed + CUtlNTree( CUtlNTree const& tree ) { Assert(0); } + + CUtlMemory m_Memory; + I m_Root; + I m_FirstFree; + I m_ElementCount; // The number actually in the tree + I m_MaxElementIndex; // The max index we've ever assigned + + // For debugging purposes; + // it's in release builds so this can be used in libraries correctly + Node_t *m_pElements; +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template +CUtlNTree::CUtlNTree( int growSize, int initSize ) : + m_Memory(growSize, initSize) +{ + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlNTree::CUtlNTree( void* pMemory, int memsize ) : + m_Memory(pMemory, memsize/sizeof(T)) +{ + ConstructList(); + ResetDbgInfo(); +} + +template +CUtlNTree::~CUtlNTree( ) +{ + RemoveAll(); +} + +template +void CUtlNTree::ConstructList() +{ + m_Root = InvalidIndex(); + m_FirstFree = InvalidIndex(); + m_ElementCount = m_MaxElementIndex = 0; +} + + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- +template +inline T& CUtlNTree::Element( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline const T& CUtlNTree::Element( I i ) const +{ + return m_Memory[i].m_Element; +} + +template +inline T& CUtlNTree::operator[]( I i ) +{ + return m_Memory[i].m_Element; +} + +template +inline const T& CUtlNTree::operator[]( I i ) const +{ + return m_Memory[i].m_Element; +} + + +//----------------------------------------------------------------------------- +// list statistics +//----------------------------------------------------------------------------- +template +inline int CUtlNTree::Count() const +{ + return m_ElementCount; +} + +template +inline I CUtlNTree::MaxElementIndex() const +{ + return m_MaxElementIndex; +} + + +//----------------------------------------------------------------------------- +// Traversing the list +//----------------------------------------------------------------------------- +template +inline I CUtlNTree::Root() const +{ + return m_Root; +} + +template +inline I CUtlNTree::FirstChild( I i ) const +{ + Assert( IsInTree(i) ); + return InternalNode(i).m_FirstChild; +} + +template +inline I CUtlNTree::PrevSibling( I i ) const +{ + Assert( IsInTree(i) ); + return InternalNode(i).m_PrevSibling; +} + +template +inline I CUtlNTree::NextSibling( I i ) const +{ + Assert( IsInTree(i) ); + return InternalNode(i).m_NextSibling; +} + +template +inline I CUtlNTree::Parent( I i ) const +{ + Assert( IsInTree(i) ); + return InternalNode(i).m_Parent; +} + + +//----------------------------------------------------------------------------- +// Are nodes in the list or valid? +//----------------------------------------------------------------------------- +template +inline bool CUtlNTree::IsValidIndex( I i ) const +{ + return (i < m_MaxElementIndex) && (i >= 0); +} + +template +inline bool CUtlNTree::IsInTree( I i ) const +{ + return (i < m_MaxElementIndex) && (i >= 0) && (InternalNode(i).m_PrevSibling != i); +} + + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- +template< class T, class I > +void CUtlNTree::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Deallocate memory +//----------------------------------------------------------------------------- +template +void CUtlNTree::Purge() +{ + RemoveAll(); + m_Memory.Purge( ); + m_FirstFree = InvalidIndex(); + m_MaxElementIndex = 0; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Node allocation/deallocation +//----------------------------------------------------------------------------- +template +I CUtlNTree::AllocInternal( ) +{ + I elem; + if ( m_FirstFree == INVALID_NTREE_IDX ) + { + // Nothing in the free list; add. + // Since nothing is in the free list, m_MaxElementIndex == total # of elements + // the list knows about. + if ((int)m_MaxElementIndex == m_Memory.NumAllocated()) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow(); + } + + Assert( m_MaxElementIndex != INVALID_NTREE_IDX ); + + elem = (I)m_MaxElementIndex; + ++m_MaxElementIndex; + + if ( elem == InvalidIndex() ) + { + Error("CUtlNTree overflow!\n"); + } + } + else + { + elem = m_FirstFree; + m_FirstFree = InternalNode( m_FirstFree ).m_NextSibling; + } + + Node_t &node = InternalNode( elem ); + node.m_NextSibling = node.m_PrevSibling = node.m_Parent = node.m_FirstChild = INVALID_NTREE_IDX; + ResetDbgInfo(); + + // one more element baby + ++m_ElementCount; + + return elem; +} + +template +I CUtlNTree::Alloc( ) +{ + I elem = AllocInternal(); + Construct( &Element(elem) ); + return elem; +} + +template +void CUtlNTree::Free( I elem ) +{ + Assert( IsInTree( elem ) ); + Unlink( elem ); + + // If there's children, this will result in leaks. Use FreeSubTree instead. + Assert( FirstChild( elem ) == INVALID_NTREE_IDX ); + + Node_t &node = InternalNode( elem ); + Destruct( &node.m_Element ); + node.m_NextSibling = m_FirstFree; + node.m_PrevSibling = elem; // Marks it as being in the free list + node.m_Parent = node.m_FirstChild = INVALID_NTREE_IDX; + m_FirstFree = elem; + + // one less element baby + --m_ElementCount; +} + +template +void CUtlNTree::FreeSubTree( I elem ) +{ + Assert( IsValidIndex( elem ) ); + + I child = FirstChild( elem ); + while ( child != INVALID_NTREE_IDX ) + { + I next = NextSibling( child ); + FreeSubTree( child ); + child = next; + } + + Free( elem ); +} + + +//----------------------------------------------------------------------------- +// Clears the tree +//----------------------------------------------------------------------------- +template +void CUtlNTree::RemoveAll() +{ + if ( m_MaxElementIndex == 0 ) + return; + + // Put everything into the free list (even unlinked things ) + I prev = InvalidIndex(); + for (int i = (int)m_MaxElementIndex; --i >= 0; prev = (I)i ) + { + Node_t &node = InternalNode( i ); + if ( IsInTree( i ) ) + { + Destruct( &node.m_Element ); + } + + node.m_NextSibling = prev; + node.m_PrevSibling = (I)i; // Marks it as being in the free list + node.m_Parent = node.m_FirstChild = INVALID_NTREE_IDX; + } + + // First free points to the first element + m_FirstFree = 0; + + // Clear everything else out + m_Root = INVALID_NTREE_IDX; + m_ElementCount = 0; +} + + +//----------------------------------------------------------------------------- +// list modification +//----------------------------------------------------------------------------- +template +void CUtlNTree::SetRoot( I root ) +{ + // Resetting the root while it's got stuff in it is bad... + Assert( m_Root == InvalidIndex() ); + m_Root = root; +} + + +//----------------------------------------------------------------------------- +// Links a node after a particular node +//----------------------------------------------------------------------------- +template +void CUtlNTree::LinkChildAfter( I parent, I after, I elem ) +{ + Assert( IsInTree(elem) ); + + // Unlink it if it's in the list at the moment + Unlink(elem); + + Node_t& newElem = InternalNode(elem); + newElem.m_Parent = parent; + newElem.m_PrevSibling = after; + if ( after != INVALID_NTREE_IDX ) + { + Node_t& prevSiblingNode = InternalNode( after ); + newElem.m_NextSibling = prevSiblingNode.m_NextSibling; + prevSiblingNode.m_NextSibling = elem; + } + else + { + if ( parent != INVALID_NTREE_IDX ) + { + Node_t& parentNode = InternalNode( parent ); + newElem.m_NextSibling = parentNode.m_FirstChild; + parentNode.m_FirstChild = elem; + } + else + { + newElem.m_NextSibling = m_Root; + if ( m_Root != INVALID_NTREE_IDX ) + { + Node_t& rootNode = InternalNode( m_Root ); + rootNode.m_PrevSibling = elem; + } + m_Root = elem; + } + } + + if ( newElem.m_NextSibling != INVALID_NTREE_IDX ) + { + Node_t& nextSiblingNode = InternalNode( newElem.m_NextSibling ); + nextSiblingNode.m_PrevSibling = elem; + } +} + + +//----------------------------------------------------------------------------- +// Links a node before a particular node +//----------------------------------------------------------------------------- +template +void CUtlNTree::LinkChildBefore( I parent, I before, I elem ) +{ + Assert( IsValidIndex(elem) ); + + if ( before != INVALID_NTREE_IDX ) + { + LinkChildAfter( parent, InternalNode( before ).m_PrevSibling, elem ); + return; + } + + // NOTE: I made the choice to do an O(n) operation here + // instead of store more data per node (LastChild). + // This might not be the right choice. Revisit if we get perf problems. + I after; + if ( parent != INVALID_NTREE_IDX ) + { + after = InternalNode( parent ).m_FirstChild; + } + else + { + after = m_Root; + } + + if ( after == INVALID_NTREE_IDX ) + { + LinkChildAfter( parent, after, elem ); + return; + } + + I next = InternalNode( after ).m_NextSibling; + while ( next != InvalidIndex() ) + { + after = next; + next = InternalNode( next ).m_NextSibling; + } + + LinkChildAfter( parent, after, elem ); +} + + +//----------------------------------------------------------------------------- +// Unlinks a node from the tree +//----------------------------------------------------------------------------- +template +void CUtlNTree::Unlink( I elem ) +{ + Assert( IsInTree(elem) ); + + Node_t *pOldNode = &InternalNode( elem ); + + // If we're the first guy, reset the head + // otherwise, make our previous node's next pointer = our next + if ( pOldNode->m_PrevSibling != INVALID_NTREE_IDX ) + { + InternalNode( pOldNode->m_PrevSibling ).m_NextSibling = pOldNode->m_NextSibling; + } + else + { + if ( pOldNode->m_Parent != INVALID_NTREE_IDX ) + { + InternalNode( pOldNode->m_Parent ).m_FirstChild = pOldNode->m_NextSibling; + } + else if ( m_Root == elem ) + { + m_Root = pOldNode->m_NextSibling; + } + } + + // If we're the last guy, reset the tail + // otherwise, make our next node's prev pointer = our prev + if ( pOldNode->m_NextSibling != INVALID_NTREE_IDX ) + { + InternalNode( pOldNode->m_NextSibling ).m_PrevSibling = pOldNode->m_PrevSibling; + } + + // Unlink everything except children + pOldNode->m_Parent = pOldNode->m_PrevSibling = pOldNode->m_NextSibling = INVALID_NTREE_IDX; +} + + +//----------------------------------------------------------------------------- +// Alloc + link combined +//----------------------------------------------------------------------------- +template +I CUtlNTree::InsertChildBefore( I parent, I before ) +{ + I elem = AllocInternal(); + Construct( &Element( elem ) ); + LinkChildBefore( parent, before, elem ); + return elem; +} + +template +I CUtlNTree::InsertChildAfter( I parent, I after ) +{ + I elem = AllocInternal(); + Construct( &Element( elem ) ); + LinkChildAfter( parent, after, elem ); + return elem; +} + +template +I CUtlNTree::InsertChildBefore( I parent, I before, const T &data ) +{ + I elem = AllocInternal(); + CopyConstruct( &Element( elem ), data ); + LinkChildBefore( parent, before, elem ); + return elem; +} + +template +I CUtlNTree::InsertChildAfter( I parent, I after, const T &data ) +{ + I elem = AllocInternal(); + CopyConstruct( &Element( elem ), data ); + LinkChildAfter( parent, after, elem ); + return elem; +} + + +//----------------------------------------------------------------------------- +// Unlink + free combined +//----------------------------------------------------------------------------- +template +void CUtlNTree::Remove( I elem ) +{ + Unlink( elem ); + Free( elem ); +} + +template +void CUtlNTree::RemoveSubTree( I elem ) +{ + UnlinkSubTree( elem ); + Free( elem ); +} + + +#endif // UTLNTREE_H diff --git a/public/tier1/utlobjectreference.h b/public/tier1/utlobjectreference.h new file mode 100644 index 0000000..b695988 --- /dev/null +++ b/public/tier1/utlobjectreference.h @@ -0,0 +1,284 @@ +//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======// +// +// $Revision: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTLOBJECTREFERENCE_H +#define UTLOBJECTREFERENCE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlintrusivelist.h" +#include "mathlib/mathlib.h" +#include "tier1/utlvector.h" + + +// Purpose: class for keeping track of all the references that exist to an object. When the object +// being referenced is freed, all of the references pointing at it will become null. +// +// To Use: +// Add a DECLARE_REFERENCED_CLASS to the class that you want to use CutlReferences with. +// Replace pointers to that class with CUtlReferences. +// Check these references for null in appropriate places. +// +// NOTE : You can still happily use pointers instead of references where you want to - these +// pointers will not magically become null like references would, but if you know no one is going +// to delete the underlying object during a partcular section of code, it doesn't +// matter. Basically, CUtlReferences don't rely on every use of an object using one. + + + + +template class CUtlReference +{ +public: + FORCEINLINE CUtlReference(void) + { + m_pNext = m_pPrev = NULL; + m_pObject = NULL; + } + + FORCEINLINE CUtlReference(T *pObj) + { + m_pNext = m_pPrev = NULL; + AddRef( pObj ); + } + + FORCEINLINE CUtlReference( const CUtlReference& other ) + { + CUtlReference(); + + if ( other.IsValid() ) + { + AddRef( (T*)( other.GetObject() ) ); + } + } + + FORCEINLINE ~CUtlReference(void) + { + KillRef(); + } + + FORCEINLINE void Set(T *pObj) + { + if ( m_pObject != pObj ) + { + KillRef(); + AddRef( pObj ); + } + } + + FORCEINLINE T * operator()(void) const + { + return m_pObject; + } + + FORCEINLINE bool IsValid( void) const + { + return ( m_pObject != NULL ); + } + + FORCEINLINE operator T*() + { + return m_pObject; + } + + FORCEINLINE operator const T*() const + { + return m_pObject; + } + + + FORCEINLINE T * GetObject( void ) + { + return m_pObject; + } + + FORCEINLINE const T* GetObject( void ) const + { + return m_pObject; + } + + + FORCEINLINE T* operator->() + { + return m_pObject; + } + + FORCEINLINE const T* operator->() const + { + return m_pObject; + } + + FORCEINLINE CUtlReference &operator=( const CUtlReference& otherRef ) + { + Set( otherRef.m_pObject ); + return *this; + } + + FORCEINLINE CUtlReference &operator=( T *pObj ) + { + Set( pObj ); + return *this; + } + + + FORCEINLINE bool operator==( T const *pOther ) const + { + return ( pOther == m_pObject ); + } + + FORCEINLINE bool operator==( T *pOther ) const + { + return ( pOther == m_pObject ); + } + + FORCEINLINE bool operator==( const CUtlReference& o ) const + { + return ( o.m_pObject == m_pObject ); + } + +public: + CUtlReference *m_pNext; + CUtlReference *m_pPrev; + + T *m_pObject; + + FORCEINLINE void AddRef( T *pObj ) + { + m_pObject = pObj; + if ( pObj ) + { + pObj->m_References.AddToHead( this ); + } + } + + FORCEINLINE void KillRef(void) + { + if ( m_pObject ) + { + m_pObject->m_References.RemoveNode( this ); + m_pObject = NULL; + } + } +}; + +template class CUtlReferenceList : public CUtlIntrusiveDList< CUtlReference > +{ +public: + ~CUtlReferenceList( void ) + { + CUtlReference *i = CUtlIntrusiveDList >::m_pHead; + while( i ) + { + CUtlReference *n = i->m_pNext; + i->m_pNext = NULL; + i->m_pPrev = NULL; + i->m_pObject = NULL; + i = n; + } + CUtlIntrusiveDList >::m_pHead = NULL; + } +}; + + +//----------------------------------------------------------------------------- +// Put this macro in classes that are referenced by CUtlReference +//----------------------------------------------------------------------------- +#define DECLARE_REFERENCED_CLASS( _className ) \ + private: \ + CUtlReferenceList< _className > m_References; \ + template friend class CUtlReference; + + +template < class T > +class CUtlReferenceVector : public CUtlBlockVector< CUtlReference< T > > +{ +public: + void RemoveAll() + { + for ( int i = 0; i < Count(); i++ ) + { + Element( i ).KillRef(); + } + + CUtlBlockVector::RemoveAll(); + } + + void FastRemove( int elem ) + { + Assert( IsValidIndex(elem) ); + + if (m_Size > 0) + { + if ( elem != m_Size -1 ) + { + Element(elem).Set( Element(m_Size-1).GetObject() ); + } + Destruct( &Element(m_Size-1) ); + --m_Size; + } + } + + bool FindAndFastRemove( const CUtlReference< T >& src ) + { + int elem = Find( src ); + if ( elem != -1 ) + { + FastRemove( elem ); + return true; + } + return false; + } + + void Remove( int elem ) + { + Assert( IsValidIndex(elem) ); + + if (m_Size > 0) + { + for ( int i = elem; i < ( m_Size - 1 ); i++ ) + { + Element( i ).Set( Element( i + 1 ).GetObject() ); + } + + Destruct( &Element(m_Size-1) ); + --m_Size; + } + } + + bool FindAndRemove( const CUtlReference< T >& src ) + { + int elem = Find( src ); + if ( elem != -1 ) + { + Remove( elem ); + return true; + } + return false; + } + +private: + // + // Disallow methods of CUtlBlockVector that can cause element addresses to change, thus + // breaking assumptions of CUtlReference. If any of these becomes needed just add a safe + // implementation to the public section. + // + void RemoveMultiple( int elem, int num ); + void RemoveMultipleFromHead(int num); + void RemoveMultipleFromTail(int num); + void Swap( CUtlReferenceVector< T > &vec ); + void Purge(); + void PurgeAndDeleteElements(); + void Compact(); +}; + +#endif + + + + + diff --git a/public/tier1/utlpriorityqueue.h b/public/tier1/utlpriorityqueue.h new file mode 100644 index 0000000..8dc315b --- /dev/null +++ b/public/tier1/utlpriorityqueue.h @@ -0,0 +1,212 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTLPRIORITYQUEUE_H +#define UTLPRIORITYQUEUE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" + +template < typename T > +class CDefUtlPriorityQueueLessFunc +{ +public: + bool operator()( const T &lhs, const T &rhs, bool (*lessFuncPtr)( T const&, T const& ) ) + { + return lessFuncPtr( lhs, rhs ); + } +}; + + +// T is the type stored in the queue, it must include the priority +// The head of the list contains the element with GREATEST priority +// configure the LessFunc_t to get the desired queue order +template< class T, class LessFunc = CDefUtlPriorityQueueLessFunc< T >, class A = CUtlMemory > +class CUtlPriorityQueue +{ +public: + // Less func typedef + // Returns true if the first parameter is "less priority" than the second + // Items that are "less priority" sort toward the tail of the queue + typedef bool (*LessFunc_t)( T const&, T const& ); + + typedef T ElemType_t; + + // constructor: lessfunc is required, but may be set after the constructor with + // SetLessFunc + CUtlPriorityQueue( int growSize = 0, int initSize = 0, LessFunc_t lessfunc = 0 ); + CUtlPriorityQueue( T *pMemory, int numElements, LessFunc_t lessfunc = 0 ); + + // gets particular elements + inline T const& ElementAtHead() const { return m_heap.Element(0); } + + inline bool IsValidIndex(int index) { return m_heap.IsValidIndex(index); } + + // O(lgn) to rebalance the heap + void RemoveAtHead(); + void RemoveAt( int index ); + + // O(lgn) to rebalance heap + void Insert( T const &element ); + // Sets the less func + void SetLessFunc( LessFunc_t func ); + + // Returns the count of elements in the queue + inline int Count() const { return m_heap.Count(); } + + // doesn't deallocate memory + void RemoveAll() { m_heap.RemoveAll(); } + + // Memory deallocation + void Purge() { m_heap.Purge(); } + + inline const T & Element( int index ) const { return m_heap.Element(index); } + +protected: + CUtlVector m_heap; + + void Swap( int index1, int index2 ); + + // Used for sorting. + LessFunc_t m_LessFunc; +}; + +template< class T, class LessFunc, class A > +inline CUtlPriorityQueue::CUtlPriorityQueue( int growSize, int initSize, LessFunc_t lessfunc ) : + m_heap(growSize, initSize), m_LessFunc(lessfunc) +{ +} + +template< class T, class LessFunc, class A > +inline CUtlPriorityQueue::CUtlPriorityQueue( T *pMemory, int numElements, LessFunc_t lessfunc ) : + m_heap(pMemory, numElements), m_LessFunc(lessfunc) +{ +} + +template< class T, class LessFunc, class A > +void CUtlPriorityQueue::RemoveAtHead() +{ + m_heap.FastRemove( 0 ); + int index = 0; + + int count = Count(); + if ( !count ) + return; + + LessFunc lessFunc; + int half = count/2; + int larger = index; + while ( index < half ) + { + int child = ((index+1) * 2) - 1; // if we wasted an element, this math would be more compact (1 based array) + if ( child < count ) + { + // Item has been filtered down to its proper place, terminate. + if ( lessFunc( m_heap[index], m_heap[child], m_LessFunc ) ) + { + // mark the potential swap and check the other child + larger = child; + } + } + // go to sibling + child++; + if ( child < count ) + { + // If this child is larger, swap it instead + if ( lessFunc( m_heap[larger], m_heap[child], m_LessFunc ) ) + larger = child; + } + + if ( larger == index ) + break; + + // swap with the larger child + Swap( index, larger ); + index = larger; + } +} + + +template< class T, class LessFunc, class A > +void CUtlPriorityQueue::RemoveAt( int index ) +{ + Assert(m_heap.IsValidIndex(index)); + m_heap.FastRemove( index ); + + int count = Count(); + if ( !count ) + return; + + LessFunc lessFunc; + int half = count/2; + int larger = index; + while ( index < half ) + { + int child = ((index+1) * 2) - 1; // if we wasted an element, this math would be more compact (1 based array) + if ( child < count ) + { + // Item has been filtered down to its proper place, terminate. + if ( lessFunc( m_heap[index], m_heap[child], m_LessFunc ) ) + { + // mark the potential swap and check the other child + larger = child; + } + } + // go to sibling + child++; + if ( child < count ) + { + // If this child is larger, swap it instead + if ( lessFunc( m_heap[larger], m_heap[child], m_LessFunc ) ) + larger = child; + } + + if ( larger == index ) + break; + + // swap with the larger child + Swap( index, larger ); + index = larger; + } +} + +template< class T, class LessFunc, class A > +void CUtlPriorityQueue::Insert( T const &element ) +{ + int index = m_heap.AddToTail(); + m_heap[index] = element; + + LessFunc lessFunc; + while ( index != 0 ) + { + int parent = ((index+1) / 2) - 1; + if ( lessFunc( m_heap[index], m_heap[parent], m_LessFunc ) ) + break; + + // swap with parent and repeat + Swap( parent, index ); + index = parent; + } +} + +template< class T, class LessFunc, class A > +void CUtlPriorityQueue::Swap( int index1, int index2 ) +{ + T tmp = m_heap[index1]; + m_heap[index1] = m_heap[index2]; + m_heap[index2] = tmp; +} + +template< class T, class LessFunc, class A > +void CUtlPriorityQueue::SetLessFunc( LessFunc_t lessfunc ) +{ + m_LessFunc = lessfunc; +} + +#endif // UTLPRIORITYQUEUE_H diff --git a/public/tier1/utlqueue.h b/public/tier1/utlqueue.h new file mode 100644 index 0000000..e90529e --- /dev/null +++ b/public/tier1/utlqueue.h @@ -0,0 +1,176 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLQUEUE_H +#define UTLQUEUE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" + +// T is the type stored in the stack +template< class T, class M = CUtlMemory< T > > +class CUtlQueue +{ +public: + + // constructor: lessfunc is required, but may be set after the constructor with + // SetLessFunc + CUtlQueue( int growSize = 0, int initSize = 0 ); + CUtlQueue( T *pMemory, int numElements ); + + // element access + T& operator[]( int i ); + T const& operator[]( int i ) const; + T& Element( int i ); + T const& Element( int i ) const; + + // return the item from the front of the queue and delete it + T const& RemoveAtHead(); + // return the item from the end of the queue and delete it + T const& RemoveAtTail(); + + // return item at the front of the queue + T const& Head(); + // return item at the end of the queue + T const& Tail(); + + // put a new item on the stack + void Insert( T const &element ); + + // checks if an element of this value already exists on the stack, returns true if it does + bool Check( T const element ); + + // Returns the count of elements in the stack + int Count() const { return m_heap.Count(); } + + // Is element index valid? + bool IsIdxValid( int i ) const; + + // doesn't deallocate memory + void RemoveAll() { m_heap.RemoveAll(); } + + // Memory deallocation + void Purge() { m_heap.Purge(); } + +protected: + CUtlVector m_heap; + T m_current; +}; + +//----------------------------------------------------------------------------- +// The CUtlQueueFixed class: +// A queue class with a fixed allocation scheme +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CUtlQueueFixed : public CUtlQueue< T, CUtlMemoryFixed > +{ + typedef CUtlQueue< T, CUtlMemoryFixed > BaseClass; +public: + + // constructor, destructor + CUtlQueueFixed( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + CUtlQueueFixed( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} +}; + +template< class T, class M > +inline CUtlQueue::CUtlQueue( int growSize, int initSize ) : + m_heap(growSize, initSize) +{ +} + +template< class T, class M > +inline CUtlQueue::CUtlQueue( T *pMemory, int numElements ) : + m_heap(pMemory, numElements) +{ +} + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- + +template< class T, class M > +inline T& CUtlQueue::operator[]( int i ) +{ + return m_heap[i]; +} + +template< class T, class M > +inline T const& CUtlQueue::operator[]( int i ) const +{ + return m_heap[i]; +} + +template< class T, class M > +inline T& CUtlQueue::Element( int i ) +{ + return m_heap[i]; +} + +template< class T, class M > +inline T const& CUtlQueue::Element( int i ) const +{ + return m_heap[i]; +} + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- + +template< class T, class M > +inline bool CUtlQueue::IsIdxValid( int i ) const +{ + return (i >= 0) && (i < m_heap.Count()); +} + +template +inline T const& CUtlQueue::RemoveAtHead() +{ + m_current = m_heap[0]; + m_heap.Remove((int)0); + return m_current; +} + +template +inline T const& CUtlQueue::RemoveAtTail() +{ + m_current = m_heap[ m_heap.Count() - 1 ]; + m_heap.Remove((int)(m_heap.Count() - 1)); + return m_current; +} + +template +inline T const& CUtlQueue::Head() +{ + m_current = m_heap[0]; + return m_current; +} + +template +inline T const& CUtlQueue::Tail() +{ + m_current = m_heap[ m_heap.Count() - 1 ]; + return m_current; +} + +template +void CUtlQueue::Insert( T const &element ) +{ + int index = m_heap.AddToTail(); + m_heap[index] = element; +} + +template +bool CUtlQueue::Check( T const element ) +{ + int index = m_heap.Find(element); + return ( index != -1 ); +} + + +#endif // UTLQUEUE_H diff --git a/public/tier1/utlrbtree.h b/public/tier1/utlrbtree.h new file mode 100644 index 0000000..3d72d4f --- /dev/null +++ b/public/tier1/utlrbtree.h @@ -0,0 +1,1568 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Header: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef UTLRBTREE_H +#define UTLRBTREE_H + +#include "tier1/utlmemory.h" +#include "tier1/utlfixedmemory.h" +#include "tier1/utlblockmemory.h" + + +// This is a useful macro to iterate from start to end in order in a map +#define FOR_EACH_UTLRBTREE( treeName, iteratorName ) \ + for ( int iteratorName = treeName.FirstInorder(); iteratorName != treeName.InvalidIndex(); iteratorName = treeName.NextInorder( iteratorName ) ) + + +//----------------------------------------------------------------------------- +// Tool to generate a default compare function for any type that implements +// operator<, including all simple types +//----------------------------------------------------------------------------- + +template +class CDefOps +{ +public: + static bool LessFunc( const T &lhs, const T &rhs ) { return ( lhs < rhs ); } +}; + +#define DefLessFunc( type ) CDefOps< type >::LessFunc + +//------------------------------------- + +inline bool StringLessThan( const char * const &lhs, const char * const &rhs) { return ( strcmp( lhs, rhs) < 0 ); } +inline bool CaselessStringLessThan( const char * const &lhs, const char * const &rhs ) { return ( stricmp( lhs, rhs) < 0 ); } + + +// Same as CaselessStringLessThan, but it ignores differences in / and \. +inline bool CaselessStringLessThanIgnoreSlashes( const char * const &lhs, const char * const &rhs ) +{ + const char *pa = lhs; + const char *pb = rhs; + while ( *pa && *pb ) + { + char a = *pa; + char b = *pb; + + // Check for dir slashes. + if ( a == '/' || a == '\\' ) + { + if ( b != '/' && b != '\\' ) + return ('/' < b); + } + else + { + if ( a >= 'a' && a <= 'z' ) + a = 'A' + (a - 'a'); + + if ( b >= 'a' && b <= 'z' ) + b = 'A' + (b - 'a'); + + if ( a > b ) + return false; + else if ( a < b ) + return true; + } + ++pa; + ++pb; + } + + // Filenames also must be the same length. + if ( *pa != *pb ) + { + // If pa shorter than pb then it's "less" + return ( !*pa ); + } + + return false; +} + +//------------------------------------- +// inline these two templates to stop multiple definitions of the same code +template <> inline bool CDefOps::LessFunc( const char * const &lhs, const char * const &rhs ) { return StringLessThan( lhs, rhs ); } +template <> inline bool CDefOps::LessFunc( char * const &lhs, char * const &rhs ) { return StringLessThan( lhs, rhs ); } + +//------------------------------------- + +template +void SetDefLessFunc( RBTREE_T &RBTree ) +{ + RBTree.SetLessFunc( DefLessFunc( typename RBTREE_T::KeyType_t ) ); +} + +//----------------------------------------------------------------------------- +// A red-black binary search tree +//----------------------------------------------------------------------------- + +template < class I > +struct UtlRBTreeLinks_t +{ + I m_Left; + I m_Right; + I m_Parent; + I m_Tag; +}; + +template < class T, class I > +struct UtlRBTreeNode_t : public UtlRBTreeLinks_t< I > +{ + T m_Data; +}; + +template < class T, class I = unsigned short, typename L = bool (*)( const T &, const T & ), class M = CUtlMemory< UtlRBTreeNode_t< T, I >, I > > +class CUtlRBTree +{ +public: + + typedef T KeyType_t; + typedef T ElemType_t; + typedef I IndexType_t; + + // Less func typedef + // Returns true if the first parameter is "less" than the second + typedef L LessFunc_t; + + // constructor, destructor + // Left at growSize = 0, the memory will first allocate 1 element and double in size + // at each increment. + // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below + CUtlRBTree( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ); + CUtlRBTree( const LessFunc_t &lessfunc ); + ~CUtlRBTree( ); + + void EnsureCapacity( int num ); + + void CopyFrom( const CUtlRBTree &other ); + + // gets particular elements + T& Element( I i ); + T const &Element( I i ) const; + T& operator[]( I i ); + T const &operator[]( I i ) const; + + // Gets the root + I Root() const; + + // Num elements + unsigned int Count() const; + + // Max "size" of the vector + // it's not generally safe to iterate from index 0 to MaxElement()-1 + // it IS safe to do so when using CUtlMemory as the allocator, + // but we should really remove patterns using this anyways, for safety and generality + I MaxElement() const; + + // Gets the children + I Parent( I i ) const; + I LeftChild( I i ) const; + I RightChild( I i ) const; + + // Tests if a node is a left or right child + bool IsLeftChild( I i ) const; + bool IsRightChild( I i ) const; + + // Tests if root or leaf + bool IsRoot( I i ) const; + bool IsLeaf( I i ) const; + + // Checks if a node is valid and in the tree + bool IsValidIndex( I i ) const; + + // Checks if the tree as a whole is valid + bool IsValid() const; + + // Invalid index + static I InvalidIndex(); + + // returns the tree depth (not a very fast operation) + int Depth( I node ) const; + int Depth() const; + + // Sets the less func + void SetLessFunc( const LessFunc_t &func ); + + // Allocation method + I NewNode(); + + // Insert method (inserts in order) + I Insert( T const &insert ); + void Insert( const T *pArray, int nItems ); + I InsertIfNotFound( T const &insert ); + + // Find method + I Find( T const &search ) const; + + // Remove methods + void RemoveAt( I i ); + bool Remove( T const &remove ); + void RemoveAll( ); + void Purge(); + + // Allocation, deletion + void FreeNode( I i ); + + // Iteration + I FirstInorder() const; + I NextInorder( I i ) const; + I PrevInorder( I i ) const; + I LastInorder() const; + + I FirstPreorder() const; + I NextPreorder( I i ) const; + I PrevPreorder( I i ) const; + I LastPreorder( ) const; + + I FirstPostorder() const; + I NextPostorder( I i ) const; + + // If you change the search key, this can be used to reinsert the + // element into the tree. + void Reinsert( I elem ); + + // swap in place + void Swap( CUtlRBTree< T, I, L > &that ); + +private: + // Can't copy the tree this way! + CUtlRBTree& operator=( const CUtlRBTree &other ); + +protected: + enum NodeColor_t + { + RED = 0, + BLACK + }; + + typedef UtlRBTreeNode_t< T, I > Node_t; + typedef UtlRBTreeLinks_t< I > Links_t; + + // Sets the children + void SetParent( I i, I parent ); + void SetLeftChild( I i, I child ); + void SetRightChild( I i, I child ); + void LinkToParent( I i, I parent, bool isLeft ); + + // Gets at the links + Links_t const &Links( I i ) const; + Links_t &Links( I i ); + + // Checks if a link is red or black + bool IsRed( I i ) const; + bool IsBlack( I i ) const; + + // Sets/gets node color + NodeColor_t Color( I i ) const; + void SetColor( I i, NodeColor_t c ); + + // operations required to preserve tree balance + void RotateLeft(I i); + void RotateRight(I i); + void InsertRebalance(I i); + void RemoveRebalance(I i); + + // Insertion, removal + I InsertAt( I parent, bool leftchild ); + + // copy constructors not allowed + CUtlRBTree( CUtlRBTree const &tree ); + + // Inserts a node into the tree, doesn't copy the data in. + void FindInsertionPosition( T const &insert, I &parent, bool &leftchild ); + + // Remove and add back an element in the tree. + void Unlink( I elem ); + void Link( I elem ); + + // Used for sorting. + LessFunc_t m_LessFunc; + + M m_Elements; + I m_Root; + I m_NumElements; + I m_FirstFree; + typename M::Iterator_t m_LastAlloc; // the last index allocated + + Node_t* m_pElements; + + FORCEINLINE M const &Elements( void ) const + { + return m_Elements; + } + + + void ResetDbgInfo() + { + m_pElements = (Node_t*)m_Elements.Base(); + } +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = int, typename L = bool (*)( const T &, const T & ) > +class CUtlFixedRBTree : public CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > > +{ +public: + + typedef L LessFunc_t; + + CUtlFixedRBTree( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ) + : CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >( growSize, initSize, lessfunc ) {} + CUtlFixedRBTree( const LessFunc_t &lessfunc ) + : CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > >( lessfunc ) {} + + typedef CUtlRBTree< T, I, L, CUtlFixedMemory< UtlRBTreeNode_t< T, I > > > BaseClass; + bool IsValidIndex( I i ) const + { + if ( !BaseClass::Elements().IsIdxValid( i ) ) + return false; + +#ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElement() + if ( BaseClass::Elements().IsIdxAfter( i, this->m_LastAlloc ) ) + { + Assert( 0 ); + return false; // don't read values that have been allocated, but not constructed + } +#endif + + return LeftChild(i) != i; + } + +protected: + void ResetDbgInfo() {} + +private: + // this doesn't make sense for fixed rbtrees, since there's no useful max pointer, and the index space isn't contiguous anyways + I MaxElement() const; +}; + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T, class I = unsigned short, typename L = bool (*)( const T &, const T & ) > +class CUtlBlockRBTree : public CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > > +{ +public: + typedef L LessFunc_t; + CUtlBlockRBTree( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ) + : CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >( growSize, initSize, lessfunc ) {} + CUtlBlockRBTree( const LessFunc_t &lessfunc ) + : CUtlRBTree< T, I, L, CUtlBlockMemory< UtlRBTreeNode_t< T, I >, I > >( lessfunc ) {} +protected: + void ResetDbgInfo() {} +}; + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline CUtlRBTree::CUtlRBTree( int growSize, int initSize, const LessFunc_t &lessfunc ) : +m_Elements( growSize, initSize ), +m_LessFunc( lessfunc ), +m_Root( InvalidIndex() ), +m_NumElements( 0 ), +m_FirstFree( InvalidIndex() ), +m_LastAlloc( m_Elements.InvalidIterator() ) +{ + ResetDbgInfo(); +} + +template < class T, class I, typename L, class M > +inline CUtlRBTree::CUtlRBTree( const LessFunc_t &lessfunc ) : +m_Elements( 0, 0 ), +m_LessFunc( lessfunc ), +m_Root( InvalidIndex() ), +m_NumElements( 0 ), +m_FirstFree( InvalidIndex() ), +m_LastAlloc( m_Elements.InvalidIterator() ) +{ + ResetDbgInfo(); +} + +template < class T, class I, typename L, class M > +inline CUtlRBTree::~CUtlRBTree() +{ + Purge(); +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::EnsureCapacity( int num ) +{ + m_Elements.EnsureCapacity( num ); +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::CopyFrom( const CUtlRBTree &other ) +{ + Purge(); + m_Elements.EnsureCapacity( other.m_Elements.Count() ); + memcpy( m_Elements.Base(), other.m_Elements.Base(), other.m_Elements.Count() * sizeof( UtlRBTreeNode_t< T, I > ) ); + m_LessFunc = other.m_LessFunc; + m_Root = other.m_Root; + m_NumElements = other.m_NumElements; + m_FirstFree = other.m_FirstFree; + m_LastAlloc = other.m_LastAlloc; + ResetDbgInfo(); +} + +//----------------------------------------------------------------------------- +// gets particular elements +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline T &CUtlRBTree::Element( I i ) +{ + return m_Elements[i].m_Data; +} + +template < class T, class I, typename L, class M > +inline T const &CUtlRBTree::Element( I i ) const +{ + return m_Elements[i].m_Data; +} + +template < class T, class I, typename L, class M > +inline T &CUtlRBTree::operator[]( I i ) +{ + return Element(i); +} + +template < class T, class I, typename L, class M > +inline T const &CUtlRBTree::operator[]( I i ) const +{ + return Element(i); +} + +//----------------------------------------------------------------------------- +// +// various accessors +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Gets the root +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::Root() const +{ + return m_Root; +} + +//----------------------------------------------------------------------------- +// Num elements +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline unsigned int CUtlRBTree::Count() const +{ + return (unsigned int)m_NumElements; +} + +//----------------------------------------------------------------------------- +// Max "size" of the vector +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::MaxElement() const +{ + return ( I )m_Elements.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Gets the children +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::Parent( I i ) const +{ + return Links(i).m_Parent; +} + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::LeftChild( I i ) const +{ + return Links(i).m_Left; +} + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::RightChild( I i ) const +{ + return Links(i).m_Right; +} + +//----------------------------------------------------------------------------- +// Tests if a node is a left or right child +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsLeftChild( I i ) const +{ + return LeftChild(Parent(i)) == i; +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRightChild( I i ) const +{ + return RightChild(Parent(i)) == i; +} + + +//----------------------------------------------------------------------------- +// Tests if root or leaf +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRoot( I i ) const +{ + return i == m_Root; +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsLeaf( I i ) const +{ + return (LeftChild(i) == InvalidIndex()) && (RightChild(i) == InvalidIndex()); +} + + +//----------------------------------------------------------------------------- +// Checks if a node is valid and in the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsValidIndex( I i ) const +{ + if ( !m_Elements.IsIdxValid( i ) ) + return false; + + if ( m_Elements.IsIdxAfter( i, m_LastAlloc ) ) + return false; // don't read values that have been allocated, but not constructed + + return LeftChild(i) != i; +} + + +//----------------------------------------------------------------------------- +// Invalid index +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline I CUtlRBTree::InvalidIndex() +{ + return ( I )M::InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// returns the tree depth (not a very fast operation) +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline int CUtlRBTree::Depth() const +{ + return Depth(Root()); +} + +//----------------------------------------------------------------------------- +// Sets the children +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetParent( I i, I parent ) +{ + Links(i).m_Parent = parent; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetLeftChild( I i, I child ) +{ + Links(i).m_Left = child; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetRightChild( I i, I child ) +{ + Links(i).m_Right = child; +} + +//----------------------------------------------------------------------------- +// Gets at the links +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::Links_t const &CUtlRBTree::Links( I i ) const +{ + // Sentinel node, makes life easier + static Links_t s_Sentinel = + { + InvalidIndex(), InvalidIndex(), InvalidIndex(), CUtlRBTree::BLACK + }; + + return (i != InvalidIndex()) ? *(Links_t*)&m_Elements[i] : *(Links_t*)&s_Sentinel; +} + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::Links_t &CUtlRBTree::Links( I i ) +{ + Assert(i != InvalidIndex()); + return *(Links_t *)&m_Elements[i]; +} + +//----------------------------------------------------------------------------- +// Checks if a link is red or black +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsRed( I i ) const +{ + return (Links(i).m_Tag == RED); +} + +template < class T, class I, typename L, class M > +inline bool CUtlRBTree::IsBlack( I i ) const +{ + return (Links(i).m_Tag == BLACK); +} + + +//----------------------------------------------------------------------------- +// Sets/gets node color +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +inline typename CUtlRBTree::NodeColor_t CUtlRBTree::Color( I i ) const +{ + return (NodeColor_t)Links(i).m_Tag; +} + +template < class T, class I, typename L, class M > +inline void CUtlRBTree::SetColor( I i, typename CUtlRBTree::NodeColor_t c ) +{ + Links(i).m_Tag = (I)c; +} + +//----------------------------------------------------------------------------- +// Allocates/ deallocates nodes +//----------------------------------------------------------------------------- +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +template < class T, class I, typename L, class M > +I CUtlRBTree::NewNode() +{ + I elem; + + // Nothing in the free list; add. + if ( m_FirstFree == InvalidIndex() ) + { + Assert( m_Elements.IsValidIterator( m_LastAlloc ) || m_NumElements == 0 ); + typename M::Iterator_t it = m_Elements.IsValidIterator( m_LastAlloc ) ? m_Elements.Next( m_LastAlloc ) : m_Elements.First(); + if ( !m_Elements.IsValidIterator( it ) ) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Elements.Grow(); + + it = m_Elements.IsValidIterator( m_LastAlloc ) ? m_Elements.Next( m_LastAlloc ) : m_Elements.First(); + + Assert( m_Elements.IsValidIterator( it ) ); + if ( !m_Elements.IsValidIterator( it ) ) + { + Error( "CUtlRBTree overflow!\n" ); + } + } + m_LastAlloc = it; + elem = m_Elements.GetIndex( m_LastAlloc ); + Assert( m_Elements.IsValidIterator( m_LastAlloc ) ); + } + else + { + elem = m_FirstFree; + m_FirstFree = Links( m_FirstFree ).m_Right; + } + +#ifdef _DEBUG + // reset links to invalid.... + Links_t &node = Links( elem ); + node.m_Left = node.m_Right = node.m_Parent = InvalidIndex(); +#endif + + Construct( &Element( elem ) ); + ResetDbgInfo(); + + return elem; +} +#pragma warning(pop) + +template < class T, class I, typename L, class M > +void CUtlRBTree::FreeNode( I i ) +{ + Assert( IsValidIndex(i) && (i != InvalidIndex()) ); + Destruct( &Element(i) ); + SetLeftChild( i, i ); // indicates it's in not in the tree + SetRightChild( i, m_FirstFree ); + m_FirstFree = i; +} + + +//----------------------------------------------------------------------------- +// Rotates node i to the left +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RotateLeft(I elem) +{ + I rightchild = RightChild(elem); + SetRightChild( elem, LeftChild(rightchild) ); + if (LeftChild(rightchild) != InvalidIndex()) + SetParent( LeftChild(rightchild), elem ); + + if (rightchild != InvalidIndex()) + SetParent( rightchild, Parent(elem) ); + if (!IsRoot(elem)) + { + if (IsLeftChild(elem)) + SetLeftChild( Parent(elem), rightchild ); + else + SetRightChild( Parent(elem), rightchild ); + } + else + m_Root = rightchild; + + SetLeftChild( rightchild, elem ); + if (elem != InvalidIndex()) + SetParent( elem, rightchild ); +} + + +//----------------------------------------------------------------------------- +// Rotates node i to the right +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RotateRight(I elem) +{ + I leftchild = LeftChild(elem); + SetLeftChild( elem, RightChild(leftchild) ); + if (RightChild(leftchild) != InvalidIndex()) + SetParent( RightChild(leftchild), elem ); + + if (leftchild != InvalidIndex()) + SetParent( leftchild, Parent(elem) ); + if (!IsRoot(elem)) + { + if (IsRightChild(elem)) + SetRightChild( Parent(elem), leftchild ); + else + SetLeftChild( Parent(elem), leftchild ); + } + else + m_Root = leftchild; + + SetRightChild( leftchild, elem ); + if (elem != InvalidIndex()) + SetParent( elem, leftchild ); +} + + +//----------------------------------------------------------------------------- +// Rebalances the tree after an insertion +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::InsertRebalance(I elem) +{ + while ( !IsRoot(elem) && (Color(Parent(elem)) == RED) ) + { + I parent = Parent(elem); + I grandparent = Parent(parent); + + /* we have a violation */ + if (IsLeftChild(parent)) + { + I uncle = RightChild(grandparent); + if (IsRed(uncle)) + { + /* uncle is RED */ + SetColor(parent, BLACK); + SetColor(uncle, BLACK); + SetColor(grandparent, RED); + elem = grandparent; + } + else + { + /* uncle is BLACK */ + if (IsRightChild(elem)) + { + /* make x a left child, will change parent and grandparent */ + elem = parent; + RotateLeft(elem); + parent = Parent(elem); + grandparent = Parent(parent); + } + /* recolor and rotate */ + SetColor(parent, BLACK); + SetColor(grandparent, RED); + RotateRight(grandparent); + } + } + else + { + /* mirror image of above code */ + I uncle = LeftChild(grandparent); + if (IsRed(uncle)) + { + /* uncle is RED */ + SetColor(parent, BLACK); + SetColor(uncle, BLACK); + SetColor(grandparent, RED); + elem = grandparent; + } + else + { + /* uncle is BLACK */ + if (IsLeftChild(elem)) + { + /* make x a right child, will change parent and grandparent */ + elem = parent; + RotateRight(parent); + parent = Parent(elem); + grandparent = Parent(parent); + } + /* recolor and rotate */ + SetColor(parent, BLACK); + SetColor(grandparent, RED); + RotateLeft(grandparent); + } + } + } + SetColor( m_Root, BLACK ); +} + + +//----------------------------------------------------------------------------- +// Insert a node into the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +I CUtlRBTree::InsertAt( I parent, bool leftchild ) +{ + I i = NewNode(); + LinkToParent( i, parent, leftchild ); + ++m_NumElements; + + Assert(IsValid()); + + return i; +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::LinkToParent( I i, I parent, bool isLeft ) +{ + Links_t &elem = Links(i); + elem.m_Parent = parent; + elem.m_Left = elem.m_Right = InvalidIndex(); + elem.m_Tag = RED; + + /* insert node in tree */ + if (parent != InvalidIndex()) + { + if (isLeft) + Links(parent).m_Left = i; + else + Links(parent).m_Right = i; + } + else + { + m_Root = i; + } + + InsertRebalance(i); +} + +//----------------------------------------------------------------------------- +// Rebalance the tree after a deletion +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveRebalance(I elem) +{ + while (elem != m_Root && IsBlack(elem)) + { + I parent = Parent(elem); + + // If elem is the left child of the parent + if (elem == LeftChild(parent)) + { + // Get our sibling + I sibling = RightChild(parent); + if (IsRed(sibling)) + { + SetColor(sibling, BLACK); + SetColor(parent, RED); + RotateLeft(parent); + + // We may have a new parent now + parent = Parent(elem); + sibling = RightChild(parent); + } + if ( (IsBlack(LeftChild(sibling))) && (IsBlack(RightChild(sibling))) ) + { + if (sibling != InvalidIndex()) + SetColor(sibling, RED); + elem = parent; + } + else + { + if (IsBlack(RightChild(sibling))) + { + SetColor(LeftChild(sibling), BLACK); + SetColor(sibling, RED); + RotateRight(sibling); + + // rotation may have changed this + parent = Parent(elem); + sibling = RightChild(parent); + } + SetColor( sibling, Color(parent) ); + SetColor( parent, BLACK ); + SetColor( RightChild(sibling), BLACK ); + RotateLeft( parent ); + elem = m_Root; + } + } + else + { + // Elem is the right child of the parent + I sibling = LeftChild(parent); + if (IsRed(sibling)) + { + SetColor(sibling, BLACK); + SetColor(parent, RED); + RotateRight(parent); + + // We may have a new parent now + parent = Parent(elem); + sibling = LeftChild(parent); + } + if ( (IsBlack(RightChild(sibling))) && (IsBlack(LeftChild(sibling))) ) + { + if (sibling != InvalidIndex()) + SetColor( sibling, RED ); + elem = parent; + } + else + { + if (IsBlack(LeftChild(sibling))) + { + SetColor( RightChild(sibling), BLACK ); + SetColor( sibling, RED ); + RotateLeft( sibling ); + + // rotation may have changed this + parent = Parent(elem); + sibling = LeftChild(parent); + } + SetColor( sibling, Color(parent) ); + SetColor( parent, BLACK ); + SetColor( LeftChild(sibling), BLACK ); + RotateRight( parent ); + elem = m_Root; + } + } + } + SetColor( elem, BLACK ); +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::Unlink( I elem ) +{ + if ( elem != InvalidIndex() ) + { + I x, y; + + if ((LeftChild(elem) == InvalidIndex()) || + (RightChild(elem) == InvalidIndex())) + { + /* y has a NIL node as a child */ + y = elem; + } + else + { + /* find tree successor with a NIL node as a child */ + y = RightChild(elem); + while (LeftChild(y) != InvalidIndex()) + y = LeftChild(y); + } + + /* x is y's only child */ + if (LeftChild(y) != InvalidIndex()) + x = LeftChild(y); + else + x = RightChild(y); + + /* remove y from the parent chain */ + if (x != InvalidIndex()) + SetParent( x, Parent(y) ); + if (!IsRoot(y)) + { + if (IsLeftChild(y)) + SetLeftChild( Parent(y), x ); + else + SetRightChild( Parent(y), x ); + } + else + m_Root = x; + + // need to store this off now, we'll be resetting y's color + NodeColor_t ycolor = Color(y); + if (y != elem) + { + // Standard implementations copy the data around, we cannot here. + // Hook in y to link to the same stuff elem used to. + SetParent( y, Parent(elem) ); + SetRightChild( y, RightChild(elem) ); + SetLeftChild( y, LeftChild(elem) ); + + if (!IsRoot(elem)) + if (IsLeftChild(elem)) + SetLeftChild( Parent(elem), y ); + else + SetRightChild( Parent(elem), y ); + else + m_Root = y; + + if (LeftChild(y) != InvalidIndex()) + SetParent( LeftChild(y), y ); + if (RightChild(y) != InvalidIndex()) + SetParent( RightChild(y), y ); + + SetColor( y, Color(elem) ); + } + + if ((x != InvalidIndex()) && (ycolor == BLACK)) + RemoveRebalance(x); + } +} + +template < class T, class I, typename L, class M > +void CUtlRBTree::Link( I elem ) +{ + if ( elem != InvalidIndex() ) + { + I parent; + bool leftchild; + + FindInsertionPosition( Element( elem ), parent, leftchild ); + + LinkToParent( elem, parent, leftchild ); + + Assert(IsValid()); + } +} + +//----------------------------------------------------------------------------- +// Delete a node from the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveAt(I elem) +{ + if ( elem != InvalidIndex() ) + { + Unlink( elem ); + + FreeNode(elem); + --m_NumElements; + + Assert(IsValid()); + } +} + + +//----------------------------------------------------------------------------- +// remove a node in the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > bool CUtlRBTree::Remove( T const &search ) +{ + I node = Find( search ); + if (node != InvalidIndex()) + { + RemoveAt(node); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::RemoveAll() +{ + // Have to do some convoluted stuff to invoke the destructor on all + // valid elements for the multilist case (since we don't have all elements + // connected to each other in a list). + + if ( m_LastAlloc == m_Elements.InvalidIterator() ) + { + Assert( m_Root == InvalidIndex() ); + Assert( m_FirstFree == InvalidIndex() ); + Assert( m_NumElements == 0 ); + return; + } + + for ( typename M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next( it ) ) + { + I i = m_Elements.GetIndex( it ); + if ( IsValidIndex( i ) ) // skip elements in the free list + { + Destruct( &Element( i ) ); + SetRightChild( i, m_FirstFree ); + SetLeftChild( i, i ); + m_FirstFree = i; + } + + if ( it == m_LastAlloc ) + break; // don't destruct elements that haven't ever been constucted + } + + // Clear everything else out + m_Root = InvalidIndex(); + m_NumElements = 0; + + Assert( IsValid() ); +} + +//----------------------------------------------------------------------------- +// Removes all nodes from the tree and purges memory +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::Purge() +{ + RemoveAll(); + m_FirstFree = InvalidIndex(); + m_Elements.Purge(); + m_LastAlloc = m_Elements.InvalidIterator(); +} + + +//----------------------------------------------------------------------------- +// iteration +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstInorder() const +{ + I i = m_Root; + while (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextInorder( I i ) const +{ + Assert(IsValidIndex(i)); + + if (RightChild(i) != InvalidIndex()) + { + i = RightChild(i); + while (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + return i; + } + + I parent = Parent(i); + while (IsRightChild(i)) + { + i = parent; + if (i == InvalidIndex()) break; + parent = Parent(i); + } + return parent; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::PrevInorder( I i ) const +{ + Assert(IsValidIndex(i)); + + if (LeftChild(i) != InvalidIndex()) + { + i = LeftChild(i); + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + return i; + } + + I parent = Parent(i); + while (IsLeftChild(i)) + { + i = parent; + if (i == InvalidIndex()) break; + parent = Parent(i); + } + return parent; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::LastInorder() const +{ + I i = m_Root; + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstPreorder() const +{ + return m_Root; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextPreorder( I i ) const +{ + if (LeftChild(i) != InvalidIndex()) + return LeftChild(i); + + if (RightChild(i) != InvalidIndex()) + return RightChild(i); + + I parent = Parent(i); + while( parent != InvalidIndex()) + { + if (IsLeftChild(i) && (RightChild(parent) != InvalidIndex())) + return RightChild(parent); + i = parent; + parent = Parent(parent); + } + return InvalidIndex(); +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::PrevPreorder( I i ) const +{ + Assert(0); // not implemented yet + return InvalidIndex(); +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::LastPreorder() const +{ + I i = m_Root; + while (1) + { + while (RightChild(i) != InvalidIndex()) + i = RightChild(i); + + if (LeftChild(i) != InvalidIndex()) + i = LeftChild(i); + else + break; + } + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::FirstPostorder() const +{ + I i = m_Root; + while (!IsLeaf(i)) + { + if (LeftChild(i)) + i = LeftChild(i); + else + i = RightChild(i); + } + return i; +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::NextPostorder( I i ) const +{ + I parent = Parent(i); + if (parent == InvalidIndex()) + return InvalidIndex(); + + if (IsRightChild(i)) + return parent; + + if (RightChild(parent) == InvalidIndex()) + return parent; + + i = RightChild(parent); + while (!IsLeaf(i)) + { + if (LeftChild(i)) + i = LeftChild(i); + else + i = RightChild(i); + } + return i; +} + + +template < class T, class I, typename L, class M > +void CUtlRBTree::Reinsert( I elem ) +{ + Unlink( elem ); + Link( elem ); +} + + +//----------------------------------------------------------------------------- +// returns the tree depth (not a very fast operation) +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +int CUtlRBTree::Depth( I node ) const +{ + if (node == InvalidIndex()) + return 0; + + int depthright = Depth( RightChild(node) ); + int depthleft = Depth( LeftChild(node) ); + return MAX( depthright, depthleft ) + 1; +} + + +//#define UTLTREE_PARANOID + +//----------------------------------------------------------------------------- +// Makes sure the tree is valid after every operation +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +bool CUtlRBTree::IsValid() const +{ + if ( !Count() ) + return true; + + if ( m_LastAlloc == m_Elements.InvalidIterator() ) + return false; + + if ( !m_Elements.IsIdxValid( Root() ) ) + return false; + + if ( Parent( Root() ) != InvalidIndex() ) + return false; + +#ifdef UTLTREE_PARANOID + + // First check to see that mNumEntries matches reality. + // count items on the free list + int numFree = 0; + for ( int i = m_FirstFree; i != InvalidIndex(); i = RightChild( i ) ) + { + ++numFree; + if ( !m_Elements.IsIdxValid( i ) ) + return false; + } + + // iterate over all elements, looking for validity + // based on the self pointers + int nElements = 0; + int numFree2 = 0; + for ( M::Iterator_t it = m_Elements.First(); it != m_Elements.InvalidIterator(); it = m_Elements.Next( it ) ) + { + I i = m_Elements.GetIndex( it ); + if ( !IsValidIndex( i ) ) + { + ++numFree2; + } + else + { + ++nElements; + + int right = RightChild( i ); + int left = LeftChild( i ); + if ( ( right == left ) && ( right != InvalidIndex() ) ) + return false; + + if ( right != InvalidIndex() ) + { + if ( !IsValidIndex( right ) ) + return false; + if ( Parent( right ) != i ) + return false; + if ( IsRed( i ) && IsRed( right ) ) + return false; + } + + if ( left != InvalidIndex() ) + { + if ( !IsValidIndex( left ) ) + return false; + if ( Parent( left ) != i ) + return false; + if ( IsRed( i ) && IsRed( left ) ) + return false; + } + } + + if ( it == m_LastAlloc ) + break; + } + if ( numFree2 != numFree ) + return false; + + if ( nElements != m_NumElements ) + return false; + +#endif // UTLTREE_PARANOID + + return true; +} + + +//----------------------------------------------------------------------------- +// Sets the less func +//----------------------------------------------------------------------------- + +template < class T, class I, typename L, class M > +void CUtlRBTree::SetLessFunc( const typename CUtlRBTree::LessFunc_t &func ) +{ + if (!m_LessFunc) + { + m_LessFunc = func; + } + else if ( Count() > 0 ) + { + // need to re-sort the tree here.... + Assert(0); + } +} + + +//----------------------------------------------------------------------------- +// inserts a node into the tree +//----------------------------------------------------------------------------- + +// Inserts a node into the tree, doesn't copy the data in. +template < class T, class I, typename L, class M > +void CUtlRBTree::FindInsertionPosition( T const &insert, I &parent, bool &leftchild ) +{ + Assert( m_LessFunc ); + + /* find where node belongs */ + I current = m_Root; + parent = InvalidIndex(); + leftchild = false; + while (current != InvalidIndex()) + { + parent = current; + if (m_LessFunc( insert, Element(current) )) + { + leftchild = true; current = LeftChild(current); + } + else + { + leftchild = false; current = RightChild(current); + } + } +} + +template < class T, class I, typename L, class M > +I CUtlRBTree::Insert( T const &insert ) +{ + // use copy constructor to copy it in + I parent; + bool leftchild; + FindInsertionPosition( insert, parent, leftchild ); + I newNode = InsertAt( parent, leftchild ); + CopyConstruct( &Element( newNode ), insert ); + return newNode; +} + + +template < class T, class I, typename L, class M > +void CUtlRBTree::Insert( const T *pArray, int nItems ) +{ + while ( nItems-- ) + { + Insert( *pArray++ ); + } +} + + +template < class T, class I, typename L, class M > +I CUtlRBTree::InsertIfNotFound( T const &insert ) +{ + // use copy constructor to copy it in + I parent; + bool leftchild; + + I current = m_Root; + parent = InvalidIndex(); + leftchild = false; + while (current != InvalidIndex()) + { + parent = current; + if (m_LessFunc( insert, Element(current) )) + { + leftchild = true; current = LeftChild(current); + } + else if (m_LessFunc( Element(current), insert )) + { + leftchild = false; current = RightChild(current); + } + else + // Match found, no insertion + return InvalidIndex(); + } + + I newNode = InsertAt( parent, leftchild ); + CopyConstruct( &Element( newNode ), insert ); + return newNode; +} + + +//----------------------------------------------------------------------------- +// finds a node in the tree +//----------------------------------------------------------------------------- +template < class T, class I, typename L, class M > +I CUtlRBTree::Find( T const &search ) const +{ + Assert( m_LessFunc ); + + I current = m_Root; + while (current != InvalidIndex()) + { + if (m_LessFunc( search, Element(current) )) + current = LeftChild(current); + else if (m_LessFunc( Element(current), search )) + current = RightChild(current); + else + break; + } + return current; +} + + +//----------------------------------------------------------------------------- +// swap in place +//----------------------------------------------------------------------------- +template < class T, class I, typename L, class M > +void CUtlRBTree::Swap( CUtlRBTree< T, I, L > &that ) +{ + m_Elements.Swap( that.m_Elements ); + V_swap( m_LessFunc, that.m_LessFunc ); + V_swap( m_Root, that.m_Root ); + V_swap( m_NumElements, that.m_NumElements ); + V_swap( m_FirstFree, that.m_FirstFree ); + V_swap( m_pElements, that.m_pElements ); + V_swap( m_LastAlloc, that.m_LastAlloc ); + Assert( IsValid() ); + Assert( m_Elements.IsValidIterator( m_LastAlloc ) || ( m_NumElements == 0 && m_FirstFree == InvalidIndex() ) ); +} + + +#endif // UTLRBTREE_H diff --git a/public/tier1/utlsoacontainer.h b/public/tier1/utlsoacontainer.h new file mode 100644 index 0000000..80c19cb --- /dev/null +++ b/public/tier1/utlsoacontainer.h @@ -0,0 +1,895 @@ +//====== Copyright © 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A Fixed-allocation class for maintaining a 1d or 2d or 3d array of data in a structure-of-arrays +// (SOA) sse-friendly manner. +// =============================================================================// + +#ifndef UTLSOACONTAINER_H +#define UTLSOACONTAINER_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "tier1/utlmemory.h" +#include "tier1/utlblockmemory.h" +#include "mathlib/ssemath.h" + + + + +// strided pointers. gives you a class that acts like a pointer, but the ++ and += operators do the +// right thing +template class CStridedPtr +{ +protected: + T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CStridedPtr( void *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CStridedPtr( void ) {} + T *operator->(void) const + { + return m_pData; + } + + T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator T *(void) + { + return m_pData; + } + + FORCEINLINE CStridedPtr & operator++(void) + { + m_pData += m_nStride; + return *this; + } + + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements * m_nStride; + } + + FORCEINLINE size_t Stride( void ) const + { + return m_nStride; + + } + +}; + +template class CStridedConstPtr +{ +protected: + const T *m_pData; + size_t m_nStride; + +public: + FORCEINLINE CStridedConstPtr( void const *pData, size_t nByteStride ) + { + m_pData = reinterpret_cast( pData ); + m_nStride = nByteStride / sizeof( T ); + } + + FORCEINLINE CStridedConstPtr( void ) {} + + const T *operator->(void) const + { + return m_pData; + } + + const T & operator*(void) const + { + return *m_pData; + } + + FORCEINLINE operator const T *(void) const + { + return m_pData; + } + + FORCEINLINE CStridedConstPtr &operator++(void) + { + m_pData += m_nStride; + return *this; + } + FORCEINLINE void operator+=( size_t nNumElements ) + { + m_pData += nNumElements*m_nStride; + } + FORCEINLINE size_t Stride( void ) const + { + return m_nStride; + + } +}; + +// allowed field data types. if you change these values, you need to change the tables in the .cpp file +enum EAttributeDataType +{ + ATTRDATATYPE_NONE = -1, // pad and varargs ender + ATTRDATATYPE_FLOAT = 0, // a float attribute + ATTRDATATYPE_4V, // vector data type, stored as class FourVectors + ATTRDATATYPE_INT, // integer. not especially sse-able on all architectures. + ATTRDATATYPE_POINTER, // a pointer. + + ATTRDATATYPE_COUNT, +}; + +#define MAX_SOA_FIELDS 32 + +class KMeansQuantizedValue; +class IKMeansErrorMetric; + +typedef fltx4 (*UNARYSIMDFUNCTION)( fltx4 const & ); +typedef fltx4 (*BINARYSIMDFUNCTION)( fltx4 const &, fltx4 const & ); + +class CSOAAttributeReference; + + + +/// mode of threading for a container. Normalyy automatically set based upon dimensions, but +/// controllable via SetThreadMode. +enum SOAThreadMode_t +{ + SOATHREADMODE_NONE = 0, + SOATHREADMODE_BYROWS = 1, + SOATHREADMODE_BYSLICES = 2, + SOATHREADMODE_BYROWS_AND_SLICES = 3, + + SOATHREADMODE_AUTO = -1, // compute based upon dimensions +}; + + +class CSOAContainer +{ + friend class CSOAAttributeReference; + +public: + // Constructor, destructor + CSOAContainer( void ); // an empty one with no attributes + CSOAContainer( int nCols, int nRows, int nSlices, ... ); + ~CSOAContainer( void ); + + // !!!!! UPDATE SERIALIZATION CODE WHENEVER THE STRUCTURE OF CSOAContainer CHANGES !!!!! + // To avoid dependency on datamodel, serialization is implemented in utlsoacontainer_serialization.cpp, in dmxloader.lib + //bool Serialize( CDmxElement *pRootElement ); + //bool Unserialize( const CDmxElement *pRootElement ); + + + // Set the data type for an attribute. If you set the data type, but tell it not to allocate, + // the data type will be set but writes will assert, and reads will give you back zeros. if + // AllocateData hasn't been called yet, this will set up for AllocateData to reserve space for + // this attribute. If you have already called AllocateData, but wish to add an attribute, you + // can also use this, which will result in separate memory being allocated for this attribute. + void SetAttributeType( int nAttrIdx, EAttributeDataType nDataType, bool bAllocateMemory = true ); + EAttributeDataType GetAttributeType( int nAttrIdx ) const; + + + // Set the attribute type for a field, if that field is not already present (potentially + // allocating memory). You can use this, for instance, to make sure an already loaded image has + // an alpha channel. + void EnsureDataType( int nAttrIdx, EAttributeDataType nDataType ); + + // set back to un-initted state, freeing memory + void Purge( void ); + + // Allocate, purge data + void AllocateData( int nNCols, int nNRows, int nSlices = 1 ); // actually allocate the memory and set the pointers up + void PurgeData( void ); + + // Did the container allocate memory for this attribute? + bool HasAllocatedMemory( int nAttrIdx ) const; + + // easy constructor for 2d using varargs. call like + // #define ATTR_RED 0 + // #define ATTR_GREEN 1 + // #define ATTR_BLUE 2 + // CSOAContainer myimage( 256, 256, ATTR_RED, ATTRDATATYPE_FLOAT, ATTR_GREEN, ATTRDATATYPE_FLOAT, + // ATTR_BLUE, ATTRDATATYPE_FLOAT, -1 ); + + int NumCols( void ) const; + int NumRows( void ) const; + int NumSlices( void ) const; + void AssertDataType( int nAttrIdx, EAttributeDataType nDataType ) const; + + // # of groups of 4 elements per row + int NumQuadsPerRow( void ) const; + int Count( void ) const; // for 1d data + int NumElements( void ) const; + + // how much to step to go from the end of one row to the start of the next one. Basically, how + // many bytes to add at the end of a row when iterating over the whole 2d array with ++ + size_t RowToRowStep( int nAttrIdx ) const; + template T *RowPtr( int nAttributeIdx, int nRowNumber, int nSliceNumber = 0 ) const; + void const *ConstRowPtr( int nAttributeIdx, int nRowNumber, int nSliceNumber = 0 ) const; + template T *ElementPointer( int nAttributeIdx, int nX = 0, int nY = 0, int nZ = 0 ) const; + FourVectors *ElementPointer4V( int nAttributeIdx, int nX = 0, int nY = 0, int nZ = 0 ) const; + size_t ItemByteStride( int nAttributeIdx ) const; + + FORCEINLINE float &FloatValue( int nAttrIdx, int nX, int nY, int nZ ) const + { + AssertDataType( nAttrIdx, ATTRDATATYPE_FLOAT ); + return RowPtr( nAttrIdx, nY, nZ )[nX]; + } + + // return a reference to an attribute, which can have operations performed on it. For instance, + // this is valid code to zero out the red component of a whole image: + // myImage[FBM_ATTR_RED] = 0.; + CSOAAttributeReference operator[]( int nAttrIdx ); + + // this is just an alias for readbaility w/ ptrs. instead of (*p)[FBM_ATTR_RED], you can do p->Attr( FBM_ATTR_RED ); + FORCEINLINE CSOAAttributeReference Attr( int nAttrIdx ); + + // copy the attribute data from another soacontainer. must be compatible geometry. + void CopyAttrFrom( CSOAContainer const &other, int nDestAttributeIdx, int nSrcAttributeIndex = -1 ); + + // copy the attribute data from another attribute. must be compatible data format + void CopyAttrToAttr( int nSrcAttributeIndex, int nDestAttributeIndex); + + // copy a subvolume of attribute data from one container to another. + void CopyRegionFrom( CSOAContainer const &src, int nSrcAttr, int nDestAttr, + int nSrcMinX, int nSrcMaxX, int nSrcMinY, int nSrcMaxY, int nSrcMinZ, int nSrcMaxZ, + int nDestX, int nDestY, int nDestZ ); + + // copy all fields from a region of src to this. + void CopyRegionFrom( CSOAContainer const &src, + int nSrcMinX, int nSrcMaxX, int nSrcMinY, int nSrcMaxY, int nSrcMinZ, int nSrcMaxZ, + int nDestX, int nDestY, int nDestZ ); + + // move all the data from one csoacontainer to another, leaving the source empty. this is just + // a pointer copy. + FORCEINLINE void MoveDataFrom( CSOAContainer other ); + + // arithmetic and data filling functions. All SIMD and hopefully fast + + /// set all elements of a float attribute to random #s + void RandomizeAttribute( int nAttr, float flMin, float flMax ) const; + + /// this.attr = vec + void FillAttr( int nAttr, Vector const &vecValue ); + + /// this.attr = float + void FillAttr( int nAttr, float flValue ); + + /// this.nDestAttr *= src.nSrcAttr + void MulAttr( CSOAContainer const &src, int nSrcAttr, int nDestAttr ); + + /// Returns the result of repeatedly combining attr values with the initial value using the specified function. + /// For instance, SumAttributeValue is just ReduceAttr( attr, FOUR_ZEROS ); + template float ReduceAttr( int nSrcAttr, fltx4 const &fl4InitialValue ) const; + + template void ApplyBinaryFunctionToAttr( int nDestAttr, fltx4 const &flFnArg1 ); + + /// this.attr = fn1( fn2( attr, arg2 ), arg1 ) + template void ApplyTwoComposedBinaryFunctionsToAttr( int nDestAttr, fltx4 const &flFnArg1, fltx4 const &flFnArg2 ); + + /// this.nDestAttr *= flValue + void MulAttr( int nDestAttr, float flScale ) + { + ApplyBinaryFunctionToAttr( nDestAttr, ReplicateX4( flScale ) ); + } + + void AddToAttr( int nDestAttr, float flAddend ) + { + ApplyBinaryFunctionToAttr( nDestAttr, ReplicateX4( flAddend ) ); + } + + // this.attr = max( this.attr, flminvalue ) + void MaxAttr( int nDestAttr, float flMinValue ) + { + ApplyBinaryFunctionToAttr( nDestAttr, ReplicateX4( flMinValue ) ); + } + + /// this.attr = min( this.attr, flminvalue ) + void MinAttr( int nDestAttr, float flMaxValue ) + { + ApplyBinaryFunctionToAttr( nDestAttr, ReplicateX4( flMaxValue ) ); + } + + void ClampAttr( int nDestAttr, float flMinValue, float flMaxValue ) + { + ApplyTwoComposedBinaryFunctionsToAttr( nDestAttr, ReplicateX4( flMaxValue ), ReplicateX4( flMinValue ) ); + } + + /// this.attr = normalize( this.attr ) + void NormalizeAttr( int nAttr ); + + /// fill 2d a rectangle with values interpolated from 4 corner values. + void FillAttrWithInterpolatedValues( int nAttr, float flValue00, float flValue10, float flValue01, float flValue11 ) const; + void FillAttrWithInterpolatedValues( int nAttr, Vector flValue00, Vector flValue10, + Vector const &flValue01, Vector const &flValue11 ) const; + + /// grab 3 scalar attributes from one csoaa and fill in a fourvector attr in. + void PackScalarAttributesToVectorAttribute( CSOAContainer *pInput, + int nVecAttributeOut, + int nScalarAttributeX, + int nScalarAttributeY, + int nScalarAttributeZ ); + + /// grab the 3 components of a vector attribute and store in 3 scalar attributes. + void UnPackVectorAttributeToScalarAttributes( CSOAContainer *pInput, + int nVecAttributeIn, + int nScalarAttributeX, + int nScalarAttributeY, + int nScalarAttributeZ ); + + /// this.attrout = src.attrin * vec (component by component ) + void MultiplyVectorAttribute( CSOAContainer *pInput, int nAttributeIn, Vector const &vecScalar, int nAttributeOut ); + + /// Given an soa container of a different dimension, resize one attribute from it to fit this + /// table's geometry. point sampling only + void ResampleAttribute( CSOAContainer &pInput, int nAttr ); + + /// sum of all floats in an attribute + float SumAttributeValue( int nAttr ) const; + + /// sum(attr) / ( w * h * d ) + float AverageFloatAttributeValue( int nAttr ) const; + + /// maximum float value in a float attr + float MaxAttributeValue( int nAttr ) const; + + /// minimum float value in a float attr + float MinAttributeValue( int nAttr ) const; + + + /// scalartargetattribute += w*exp( vecdir dot ndirection) + void AddGaussianSRBF( float flWeight, Vector vecDir, int nDirectionAttribute, int nScalarTargetAttribute ); + + /// vec3targetattribute += w*exp( vecdir dot ndirection) + void AddGaussianSRBF( Vector vecWeight, Vector vecDir, int nDirectionAttribute, + int nVectorTargetAttribute ); + + + /// find the largest value of a vector attribute + void FindLargestMagnitudeVector( int nAttr, int *nx, int *ny, int *nz ); + + void KMeansQuantization( int const *pFieldIndices, int nNumFields, + KMeansQuantizedValue *pOutValues, + int nNumResultsDesired, IKMeansErrorMetric *pErrorCalculator, + int nFieldToStoreIndexInto, int nNumIterations, + int nChannelToReceiveErrorSignal = -1 ); + + // Calculate the signed distance, in voxels, between all voxels and a surface boundary defined + // by nSrcField being >0. Voxels with nSrcField <0 will end up with negative distances. Voxels + // with nSrcField == 0 will get 0, and nSrcField >0 will yield positive distances. Note the + // min/max x/y/z fields don't reflect the range to be written, but rather represent the bounds + // of updated voxels that you want your distance field modified to take into account. This + // volume will be bloated based upon the nMaxDistance parameter and simd padding. A + // brute-force algorithm is used, but it is threaded and simd'd. Large "nMaxDistance" values + // applied to large images can take a long time, as the execution time per output pixel is + // proportional to maxdistance^2. The rect argument, if passed, will be modified to be the + // entire rectangle modified by the operation. + void GenerateDistanceField( int nSrcField, int nDestField, + int nMaxDistance, + Rect3D_t *pRect = NULL ); + + void SetThreadMode( SOAThreadMode_t eThreadMode ); + +protected: + int m_nColumns; // # of rows and columns created with + int m_nRows; + int m_nSlices; + int m_nPaddedColumns; // # of columns rounded up for sse + int m_nNumQuadsPerRow; // # of groups of 4 elements per row + uint8 *m_pDataMemory; // the actual data memory + uint8 *m_pAttributePtrs[MAX_SOA_FIELDS]; + EAttributeDataType m_nDataType[MAX_SOA_FIELDS]; + size_t m_nStrideInBytes[MAX_SOA_FIELDS]; // stride from one field datum to another + size_t m_nRowStrideInBytes[MAX_SOA_FIELDS]; // stride from one row datum to another per field + size_t m_nSliceStrideInBytes[MAX_SOA_FIELDS]; // stride from one slice datum to another per field + uint32 m_nFieldPresentMask; + uint8 *m_pConstantDataMemory; + uint8 *m_pSeparateDataMemory[MAX_SOA_FIELDS]; // for fields allocated separately from the main allocation + SOAThreadMode_t m_eThreadMode; // set thread mode + + FORCEINLINE void Init( void ) + { + memset( m_nDataType, 0xff, sizeof( m_nDataType ) ); + memset( m_pSeparateDataMemory, 0, sizeof( m_pSeparateDataMemory ) ); + +#ifdef _DEBUG + memset( m_pAttributePtrs, 0xFF, sizeof( m_pAttributePtrs ) ); + memset( m_nStrideInBytes, 0xFF, sizeof( m_nStrideInBytes ) ); + memset( m_nRowStrideInBytes, 0xFF, sizeof( m_nRowStrideInBytes ) ); + memset( m_nSliceStrideInBytes, 0xFF, sizeof( m_nSliceStrideInBytes ) ); +#endif + + m_pConstantDataMemory = NULL; + m_pDataMemory = 0; + m_nNumQuadsPerRow = 0; + m_nColumns = m_nPaddedColumns = m_nRows = m_nSlices = 0; + m_nFieldPresentMask = 0; + m_eThreadMode = SOATHREADMODE_NONE; + } + + void UpdateDistanceRow( int nSearchRadius, int nMinX, int nMaxX, int nY, int nZ, + int nSrcField, int nDestField ); + + // parallel helper functions. These do the work, and all take a row/column range as their first arguments. + void CopyAttrFromPartial( int nStartRow, int nNumRows, int nStartSlice, int nEndSlice, CSOAContainer const *pOther, int nDestAttributeIndex, int nSrcAttributeIndex ); + void FillAttrPartial( int nStartRow, int nNumRows, int nStartSlice, int nEndSlice, int nAttr, fltx4 fl4Value ); + + // Allocation utility funcs (NOTE: all allocs are multiples of 16, and are aligned allocs) + size_t DataMemorySize( void ) const; // total bytes of data memory to allocate at m_pDataMemory (if all attributes were allocated in a single block) + size_t ConstantMemorySize( void ) const; // total bytes of constant memory to allocate at m_pConstantDataMemory (if all constant attributes were allocated in a single block) + size_t AttributeMemorySize( int nAttrIndex ) const; // total bytes of data memory allocated to a single attribute (constant or otherwise) + void AllocateDataMemory( void ); + void AllocateConstantMemory( void ); +}; + +class CSOAAttributeReference; + +// define binary op class to allow this construct without temps: +// dest( FBM_ATTR_RED ) = src( FBM_ATTR_BLUE ) + src( FBM_ATTR_GREEN ) +template class CSOAAttributeReferenceBinaryOp +{ +public: + CSOAAttributeReference m_opA; + CSOAAttributeReference m_opB; + + CSOAAttributeReferenceBinaryOp( CSOAAttributeReference const &a, CSOAAttributeReference const & b ) + { + a.CopyTo( m_opA ); + b.CopyTo( m_opB ); + } + +}; + +#define DEFINE_OP( opname, fnname ) \ +FORCEINLINE CSOAAttributeReferenceBinaryOp operator opname( CSOAAttributeReference const &other ) const \ +{ \ + return CSOAAttributeReferenceBinaryOp( *this, other ); \ +} + +class CSOAAttributeReference +{ + friend class CSOAContainer; + + class CSOAContainer *m_pContainer; + int m_nAttributeID; + +public: + FORCEINLINE void operator *=( float flScale ) const + { + m_pContainer->MulAttr( m_nAttributeID, flScale ); + } + FORCEINLINE void operator +=( float flAddend ) const + { + m_pContainer->AddToAttr( m_nAttributeID, flAddend ); + } + FORCEINLINE void operator -=( float flAddend ) const + { + m_pContainer->AddToAttr( m_nAttributeID, -flAddend ); + } + FORCEINLINE void operator =( float flValue ) const + { + m_pContainer->FillAttr( m_nAttributeID, flValue ); + } + + FORCEINLINE void operator =( CSOAAttributeReference const &other ) const + { + m_pContainer->CopyAttrFrom( *other.m_pContainer, m_nAttributeID, other.m_nAttributeID ); + } + + // these operator overloads let you do + // dst[ATT1] = src1[ATT] + src2[ATT] with no temporaries generated + DEFINE_OP( +, AddSIMD ); + DEFINE_OP( *, MulSIMD ); + DEFINE_OP( -, SubSIMD ); + DEFINE_OP( /, DivSIMD ); + + template FORCEINLINE void operator =( CSOAAttributeReferenceBinaryOp const &op ); + + FORCEINLINE void CopyTo( CSOAAttributeReference &other ) const; // since operator= is over-ridden +}; + + +template FORCEINLINE void CSOAAttributeReference::operator =( CSOAAttributeReferenceBinaryOp const &op ) +{ + m_pContainer->AssertDataType( m_nAttributeID, ATTRDATATYPE_FLOAT ); + fltx4 *pOut = m_pContainer->RowPtr( m_nAttributeID, 0 ); + fltx4 *pInA = op.m_opA.m_pContainer->RowPtr( op.m_opA.m_nAttributeID, 0 ); + fltx4 *pInB = op.m_opB.m_pContainer->RowPtr( op.m_opB.m_nAttributeID, 0 ); + size_t nRowToRowStride = m_pContainer->RowToRowStep( m_nAttributeID ) / sizeof( fltx4 ); + int nRowCtr = m_pContainer->NumRows() * m_pContainer->NumSlices(); + do + { + int nColCtr = m_pContainer->NumQuadsPerRow(); + do + { + *(pOut++) = fn( *( pInA++ ), *( pInB++ ) ); + } while ( --nColCtr ); + pOut += nRowToRowStride; + pInA += nRowToRowStride; + pInB += nRowToRowStride; + } while ( --nRowCtr ); +} + +FORCEINLINE void CSOAAttributeReference::CopyTo( CSOAAttributeReference &other ) const +{ + other.m_pContainer = m_pContainer; + other.m_nAttributeID = m_nAttributeID; +} + + + +FORCEINLINE CSOAAttributeReference CSOAContainer::operator[]( int nAttrIdx ) +{ + CSOAAttributeReference ret; + ret.m_pContainer = this; + ret.m_nAttributeID = nAttrIdx; + return ret; +} + +FORCEINLINE CSOAAttributeReference CSOAContainer::Attr( int nAttrIdx ) +{ + return (*this)[nAttrIdx]; +} + +template void CSOAContainer::ApplyTwoComposedBinaryFunctionsToAttr( int nDestAttr, fltx4 const &fl4FnArg1, fltx4 const &fl4FnArg2 ) +{ + if ( m_nDataType[nDestAttr] == ATTRDATATYPE_4V ) + { + FourVectors *pOut = RowPtr( nDestAttr, 0 ); + size_t nRowToRowStride = RowToRowStep( nDestAttr ) / sizeof( FourVectors ); + int nRowCtr = NumRows() * NumSlices(); + do + { + int nColCtr = NumQuadsPerRow(); + do + { + pOut->x = fn1( fn2( pOut->x, fl4FnArg2 ), fl4FnArg1 ); + pOut->y = fn1( fn2( pOut->y, fl4FnArg2 ), fl4FnArg1 ); + pOut->z = fn1( fn2( pOut->z, fl4FnArg2 ), fl4FnArg1 ); + } while ( --nColCtr ); + pOut += nRowToRowStride; + } while ( --nRowCtr ); + } + else + { + AssertDataType( nDestAttr, ATTRDATATYPE_FLOAT ); + fltx4 *pOut = RowPtr( nDestAttr, 0 ); + size_t nRowToRowStride = RowToRowStep( nDestAttr ) / sizeof( fltx4 ); + int nRowCtr = NumRows() * NumSlices(); + do + { + int nColCtr = NumQuadsPerRow(); + do + { + *( pOut++ ) = fn1( fn2( *pOut, fl4FnArg2 ), fl4FnArg1 ); + } while ( --nColCtr ); + pOut += nRowToRowStride; + } while ( --nRowCtr ); + } +} + +template void CSOAContainer::ApplyBinaryFunctionToAttr( int nDestAttr, fltx4 const &fl4FnArg1 ) +{ + if ( m_nDataType[nDestAttr] == ATTRDATATYPE_4V ) + { + FourVectors *pOut = RowPtr( nDestAttr, 0 ); + size_t nRowToRowStride = RowToRowStep( nDestAttr ) / sizeof( FourVectors ); + int nRowCtr = NumRows() * NumSlices(); + do + { + int nColCtr = NumQuadsPerRow(); + do + { + pOut->x = fn( pOut->x, fl4FnArg1 ); + pOut->y = fn( pOut->y, fl4FnArg1 ); + pOut->z = fn( pOut->z, fl4FnArg1 ); + } while ( --nColCtr ); + pOut += nRowToRowStride; + } while ( --nRowCtr ); + } + else + { + AssertDataType( nDestAttr, ATTRDATATYPE_FLOAT ); + fltx4 *pOut = RowPtr( nDestAttr, 0 ); + size_t nRowToRowStride = RowToRowStep( nDestAttr ) / sizeof( fltx4 ); + int nRowCtr = NumRows() * NumSlices(); + do + { + int nColCtr = NumQuadsPerRow(); + do + { + *(pOut++) = fn( *pOut, fl4FnArg1 ); + } while ( --nColCtr ); + pOut += nRowToRowStride; + } while ( --nRowCtr ); + } +} + +template float CSOAContainer::ReduceAttr( int nSrcAttr, fltx4 const &fl4InitialValue ) const +{ + AssertDataType( nSrcAttr, ATTRDATATYPE_FLOAT ); + fltx4 fl4Result = fl4InitialValue; + fltx4 const *pIn = RowPtr( nSrcAttr, 0 ); + size_t nRowToRowStride = RowToRowStep( nSrcAttr ) / sizeof( fltx4 ); + int nRowCtr = NumRows() * NumSlices(); + fltx4 fl4LastColumnMask = LoadAlignedSIMD( g_SIMD_SkipTailMask[NumCols() & 3 ] ); + do + { + for( int i = 0; i < NumQuadsPerRow() - 1; i++ ) + { + fl4Result = fn( fl4Result, *( pIn++ ) ); + } + // handle the last column in case its not a multiple of 4 wide + fl4Result = MaskedAssign( fl4LastColumnMask, fn( fl4Result, *( pIn++ ) ), fl4Result ); + pIn += nRowToRowStride; + } while ( --nRowCtr ); + // now, combine the subfields + fl4Result = fn( + fn( fl4Result, SplatYSIMD( fl4Result ) ), + fn( SplatZSIMD( fl4Result ), SplatWSIMD( fl4Result ) ) ); + return SubFloat( fl4Result, 0 ); +} + + + +#define QUANTIZER_NJOBS 1 // # of simultaneous subjobs to execute for kmeans quantizer + +// kmeans quantization classes +// the array of quantized values returned by quantization +class KMeansQuantizedValue +{ +public: + FourVectors m_vecValuePosition; // replicated + fltx4 m_fl4Values[MAX_SOA_FIELDS]; // replicated + + float m_flValueAccumulators[QUANTIZER_NJOBS][MAX_SOA_FIELDS]; + float m_flWeightAccumulators[QUANTIZER_NJOBS]; + + FORCEINLINE float operator()( int n ) + { + return SubFloat( m_fl4Values[n], 0 ); + } + +}; + +class KMeansSampleDescriptor +{ +public: + fltx4 *m_pInputValues[MAX_SOA_FIELDS]; + + FORCEINLINE fltx4 const & operator()( int nField ) const + { + return *m_pInputValues[nField]; + } + +}; + +class IKMeansErrorMetric +{ +public: + virtual void CalculateError( KMeansSampleDescriptor const &sampleAddresses, + FourVectors const &v4SamplePositions, + KMeansQuantizedValue const &valueToCompareAgainst, + fltx4 *pfl4ErrOut ) =0; + + // for things like normalization, etc + virtual void PostAdjustQuantizedValue( KMeansQuantizedValue &valueToAdjust ) + { + } + + // for global fixup after each adjustment step + virtual void PostStep( int const *pFieldIndices, int nNumFields, + KMeansQuantizedValue *pValues, int nNumQuantizedValues, + int nIndexField, CSOAContainer &data ) + { + } + +}; + + + + +FORCEINLINE CSOAContainer::CSOAContainer( void ) +{ + Init(); +} + + +//----------------------------------------------------------------------------- +// Did the container allocate memory for this attribute? +//----------------------------------------------------------------------------- +FORCEINLINE bool CSOAContainer::HasAllocatedMemory( int nAttrIdx ) const +{ + return ( m_nFieldPresentMask & ( 1 << nAttrIdx ) ) != 0; +} + + + +FORCEINLINE EAttributeDataType CSOAContainer::GetAttributeType( int nAttrIdx ) const +{ + Assert( ( nAttrIdx >= 0 ) && ( nAttrIdx < MAX_SOA_FIELDS ) ); + return m_nDataType[nAttrIdx]; +} + +FORCEINLINE void CSOAContainer::EnsureDataType( int nAttrIdx, EAttributeDataType nDataType ) +{ + if ( !HasAllocatedMemory( nAttrIdx ) ) + { + SetAttributeType( nAttrIdx, nDataType ); + } +} + +FORCEINLINE int CSOAContainer::NumRows( void ) const +{ + return m_nRows; +} + +FORCEINLINE int CSOAContainer::NumCols( void ) const +{ + return m_nColumns; +} +FORCEINLINE int CSOAContainer::NumSlices( void ) const +{ + return m_nSlices; +} + + +FORCEINLINE void CSOAContainer::AssertDataType( int nAttrIdx, EAttributeDataType nDataType ) const +{ + Assert( nAttrIdx >= 0 ); + Assert( nAttrIdx < MAX_SOA_FIELDS ); + Assert( m_nDataType[ nAttrIdx ] == nDataType ); +} + + +// # of groups of 4 elements per row +FORCEINLINE int CSOAContainer::NumQuadsPerRow( void ) const +{ + return m_nNumQuadsPerRow; +} + +FORCEINLINE int CSOAContainer::Count( void ) const // for 1d data +{ + return NumCols(); +} + +FORCEINLINE int CSOAContainer::NumElements( void ) const +{ + return NumCols() * NumRows() * NumSlices(); +} + + +// how much to step to go from the end of one row to the start of the next one. Basically, how +// many bytes to add at the end of a row when iterating over the whole 2d array with ++ +FORCEINLINE size_t CSOAContainer::RowToRowStep( int nAttrIdx ) const +{ + return 0; +} + +template FORCEINLINE T *CSOAContainer::RowPtr( int nAttributeIdx, int nRowNumber, int nSliceNumber ) const +{ + Assert( nRowNumber < m_nRows ); + Assert( nAttributeIdx < MAX_SOA_FIELDS ); + Assert( m_nDataType[nAttributeIdx] != ATTRDATATYPE_NONE ); + Assert( ( m_nFieldPresentMask & ( 1 << nAttributeIdx ) ) || ( ( nRowNumber == 0 ) && ( nSliceNumber == 0 ) ) ); + return reinterpret_cast( + m_pAttributePtrs[nAttributeIdx] + + + nRowNumber * m_nRowStrideInBytes[nAttributeIdx] + + nSliceNumber * m_nSliceStrideInBytes[nAttributeIdx] ); +} + +FORCEINLINE void const *CSOAContainer::ConstRowPtr( int nAttributeIdx, int nRowNumber, int nSliceNumber ) const +{ + Assert( nRowNumber < m_nRows ); + Assert( nAttributeIdx < MAX_SOA_FIELDS ); + Assert( m_nDataType[nAttributeIdx] != ATTRDATATYPE_NONE ); + return m_pAttributePtrs[nAttributeIdx] + + nRowNumber * m_nRowStrideInBytes[nAttributeIdx] + + nSliceNumber * m_nSliceStrideInBytes[nAttributeIdx]; +} + + +template FORCEINLINE T *CSOAContainer::ElementPointer( int nAttributeIdx, int nX, int nY, int nZ ) const +{ + Assert( nAttributeIdx < MAX_SOA_FIELDS ); + Assert( nX < m_nColumns ); + Assert( nY < m_nRows ); + Assert( nZ < m_nSlices ); + Assert( m_nDataType[nAttributeIdx] != ATTRDATATYPE_NONE ); + Assert( m_nDataType[nAttributeIdx] != ATTRDATATYPE_4V ); + return reinterpret_cast( m_pAttributePtrs[nAttributeIdx] + + nX * m_nStrideInBytes[nAttributeIdx] + + nY * m_nRowStrideInBytes[nAttributeIdx] + + nZ * m_nSliceStrideInBytes[nAttributeIdx] + ); +} + +FORCEINLINE FourVectors *CSOAContainer::ElementPointer4V( int nAttributeIdx, int nX, int nY, int nZ ) const +{ + Assert( nAttributeIdx < MAX_SOA_FIELDS ); + Assert( nX < m_nColumns ); + Assert( nY < m_nRows ); + Assert( nZ < m_nSlices ); + Assert( m_nDataType[nAttributeIdx] == ATTRDATATYPE_4V ); + int nXIdx = nX / 4; + uint8 *pRet = m_pAttributePtrs[nAttributeIdx] + + nXIdx * 4 * m_nStrideInBytes[nAttributeIdx] + + nY * m_nRowStrideInBytes[nAttributeIdx] + + nZ * m_nSliceStrideInBytes[nAttributeIdx]; + pRet += 4 * ( nX & 3 ); + return reinterpret_cast( pRet ); +} +FORCEINLINE size_t CSOAContainer::ItemByteStride( int nAttributeIdx ) const +{ + Assert( nAttributeIdx < MAX_SOA_FIELDS ); + Assert( m_nDataType[nAttributeIdx] != ATTRDATATYPE_NONE ); + return m_nStrideInBytes[ nAttributeIdx ]; +} + +// move all the data from one csoacontainer to another, leaving the source empty. +// this is just a pointer copy. +FORCEINLINE void CSOAContainer::MoveDataFrom( CSOAContainer other ) +{ + (*this) = other; + other.Init(); +} + + + + +class CFltX4AttributeIterator : public CStridedConstPtr +{ + FORCEINLINE CFltX4AttributeIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) + : CStridedConstPtr( pContainer->ConstRowPtr( nAttribute, nRowNumber), + pContainer->ItemByteStride( nAttribute ) ) + { + } +}; + +class CFltX4AttributeWriteIterator : public CStridedPtr +{ + FORCEINLINE CFltX4AttributeWriteIterator( CSOAContainer const *pContainer, int nAttribute, int nRowNumber = 0 ) + : CStridedPtr( pContainer->RowPtr( nAttribute, nRowNumber), + pContainer->ItemByteStride( nAttribute ) ) + { + } + +}; + +FORCEINLINE FourVectors CompressSIMD( FourVectors const &a, FourVectors const &b ) +{ + FourVectors ret; + ret.x = CompressSIMD( a.x, b.x ); + ret.y = CompressSIMD( a.y, b.y ); + ret.z = CompressSIMD( a.z, b.z ); + return ret; +} + +FORCEINLINE FourVectors Compress4SIMD( FourVectors const &a, FourVectors const &b, + FourVectors const &c, FourVectors const &d ) +{ + FourVectors ret; + ret.x = Compress4SIMD( a.x, b.x, c.x, d.x ); + ret.y = Compress4SIMD( a.y, b.y, c.y, d.y ); + ret.z = Compress4SIMD( a.z, b.z, c.z, d.z ); + return ret; +} + + + +#endif diff --git a/public/tier1/utlstack.h b/public/tier1/utlstack.h new file mode 100644 index 0000000..c7ab48d --- /dev/null +++ b/public/tier1/utlstack.h @@ -0,0 +1,331 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +// A stack based on a growable array +//=============================================================================// + +#ifndef UTLSTACK_H +#define UTLSTACK_H + +#include +#include +#include "utlmemory.h" + + +//----------------------------------------------------------------------------- +// The CUtlStack class: +// A growable stack class which doubles in size by default. +// It will always keep all elements consecutive in memory, and may move the +// elements around in memory (via a realloc) when elements are pushed or +// popped. Clients should therefore refer to the elements of the stack +// by index (they should *never* maintain pointers to elements in the stack). +//----------------------------------------------------------------------------- + +template< class T, class M = CUtlMemory< T > > +class CUtlStack +{ +public: + // constructor, destructor + CUtlStack( int growSize = 0, int initSize = 0 ); + ~CUtlStack(); + + void CopyFrom( const CUtlStack &from ); + + // element access + T& operator[]( int i ); + T const& operator[]( int i ) const; + T& Element( int i ); + T const& Element( int i ) const; + + // Gets the base address (can change when adding elements!) + T* Base(); + T const* Base() const; + + // Looks at the stack top + T& Top(); + T const& Top() const; + + // Size + int Count() const; + + // Is element index valid? + bool IsIdxValid( int i ) const; + + // Adds an element, uses default constructor + int Push(); + + // Adds an element, uses copy constructor + int Push( T const& src ); + + // Pops the stack + void Pop(); + void Pop( T& oldTop ); + void PopMultiple( int num ); + + // Makes sure we have enough memory allocated to store a requested # of elements + void EnsureCapacity( int num ); + + // Clears the stack, no deallocation + void Clear(); + + // Memory deallocation + void Purge(); + +private: + // Grows the stack allocation + void GrowStack(); + + // For easier access to the elements through the debugger + void ResetDbgInfo(); + + M m_Memory; + int m_Size; + + // For easier access to the elements through the debugger + T* m_pElements; +}; + + +//----------------------------------------------------------------------------- +// For easier access to the elements through the debugger +//----------------------------------------------------------------------------- + +template< class T, class M > +inline void CUtlStack::ResetDbgInfo() +{ + m_pElements = m_Memory.Base(); +} + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- + +template< class T, class M > +CUtlStack::CUtlStack( int growSize, int initSize ) : + m_Memory(growSize, initSize), m_Size(0) +{ + ResetDbgInfo(); +} + +template< class T, class M > +CUtlStack::~CUtlStack() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +// copy into +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::CopyFrom( const CUtlStack &from ) +{ + Purge(); + EnsureCapacity( from.Count() ); + for ( int i = 0; i < from.Count(); i++ ) + { + Push( from[i] ); + } +} + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- + +template< class T, class M > +inline T& CUtlStack::operator[]( int i ) +{ + assert( IsIdxValid(i) ); + return m_Memory[i]; +} + +template< class T, class M > +inline T const& CUtlStack::operator[]( int i ) const +{ + assert( IsIdxValid(i) ); + return m_Memory[i]; +} + +template< class T, class M > +inline T& CUtlStack::Element( int i ) +{ + assert( IsIdxValid(i) ); + return m_Memory[i]; +} + +template< class T, class M > +inline T const& CUtlStack::Element( int i ) const +{ + assert( IsIdxValid(i) ); + return m_Memory[i]; +} + + +//----------------------------------------------------------------------------- +// Gets the base address (can change when adding elements!) +//----------------------------------------------------------------------------- + +template< class T, class M > +inline T* CUtlStack::Base() +{ + return m_Memory.Base(); +} + +template< class T, class M > +inline T const* CUtlStack::Base() const +{ + return m_Memory.Base(); +} + +//----------------------------------------------------------------------------- +// Returns the top of the stack +//----------------------------------------------------------------------------- + +template< class T, class M > +inline T& CUtlStack::Top() +{ + assert( m_Size > 0 ); + return Element(m_Size-1); +} + +template< class T, class M > +inline T const& CUtlStack::Top() const +{ + assert( m_Size > 0 ); + return Element(m_Size-1); +} + +//----------------------------------------------------------------------------- +// Size +//----------------------------------------------------------------------------- + +template< class T, class M > +inline int CUtlStack::Count() const +{ + return m_Size; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- + +template< class T, class M > +inline bool CUtlStack::IsIdxValid( int i ) const +{ + return (i >= 0) && (i < m_Size); +} + +//----------------------------------------------------------------------------- +// Grows the stack +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::GrowStack() +{ + if (m_Size >= m_Memory.NumAllocated()) + m_Memory.Grow(); + + ++m_Size; + + ResetDbgInfo(); +} + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::EnsureCapacity( int num ) +{ + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Adds an element, uses default constructor +//----------------------------------------------------------------------------- + +template< class T, class M > +int CUtlStack::Push() +{ + GrowStack(); + Construct( &Element(m_Size-1) ); + return m_Size - 1; +} + +//----------------------------------------------------------------------------- +// Adds an element, uses copy constructor +//----------------------------------------------------------------------------- + +template< class T, class M > +int CUtlStack::Push( T const& src ) +{ + GrowStack(); + CopyConstruct( &Element(m_Size-1), src ); + return m_Size - 1; +} + + +//----------------------------------------------------------------------------- +// Pops the stack +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::Pop() +{ + assert( m_Size > 0 ); + Destruct( &Element(m_Size-1) ); + --m_Size; +} + +template< class T, class M > +void CUtlStack::Pop( T& oldTop ) +{ + assert( m_Size > 0 ); + oldTop = Top(); + Pop(); +} + +template< class T, class M > +void CUtlStack::PopMultiple( int num ) +{ + assert( m_Size >= num ); + for ( int i = 0; i < num; ++i ) + Destruct( &Element( m_Size - i - 1 ) ); + m_Size -= num; +} + + +//----------------------------------------------------------------------------- +// Element removal +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::Clear() +{ + for (int i = m_Size; --i >= 0; ) + Destruct(&Element(i)); + + m_Size = 0; +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- + +template< class T, class M > +void CUtlStack::Purge() +{ + Clear(); + m_Memory.Purge( ); + ResetDbgInfo(); +} + +#endif // UTLSTACK_H diff --git a/public/tier1/utlstring.h b/public/tier1/utlstring.h new file mode 100644 index 0000000..6df4063 --- /dev/null +++ b/public/tier1/utlstring.h @@ -0,0 +1,222 @@ +//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef UTLSTRING_H +#define UTLSTRING_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utlmemory.h" +#include "tier1/strtools.h" +#include "limits.h" + + +//----------------------------------------------------------------------------- +// Base class, containing simple memory management +//----------------------------------------------------------------------------- +class CUtlBinaryBlock +{ +public: + CUtlBinaryBlock( int growSize = 0, int initSize = 0 ); + + // NOTE: nInitialLength indicates how much of the buffer starts full + CUtlBinaryBlock( void* pMemory, int nSizeInBytes, int nInitialLength ); + CUtlBinaryBlock( const void* pMemory, int nSizeInBytes ); + CUtlBinaryBlock( const CUtlBinaryBlock& src ); + + void Get( void *pValue, int nMaxLen ) const; + void Set( const void *pValue, int nLen ); + const void *Get( ) const; + void *Get( ); + + unsigned char& operator[]( int i ); + const unsigned char& operator[]( int i ) const; + + int Length() const; + void SetLength( int nLength ); // Undefined memory will result + bool IsEmpty() const; + void Clear(); + void Purge(); + + bool IsReadOnly() const; + + CUtlBinaryBlock &operator=( const CUtlBinaryBlock &src ); + + // Test for equality + bool operator==( const CUtlBinaryBlock &src ) const; + +private: + CUtlMemory m_Memory; + int m_nActualLength; +}; + + +//----------------------------------------------------------------------------- +// class inlines +//----------------------------------------------------------------------------- +inline const void *CUtlBinaryBlock::Get( ) const +{ + return m_Memory.Base(); +} + +inline void *CUtlBinaryBlock::Get( ) +{ + return m_Memory.Base(); +} + +inline int CUtlBinaryBlock::Length() const +{ + return m_nActualLength; +} + +inline unsigned char& CUtlBinaryBlock::operator[]( int i ) +{ + return m_Memory[i]; +} + +inline const unsigned char& CUtlBinaryBlock::operator[]( int i ) const +{ + return m_Memory[i]; +} + +inline bool CUtlBinaryBlock::IsReadOnly() const +{ + return m_Memory.IsReadOnly(); +} + +inline bool CUtlBinaryBlock::IsEmpty() const +{ + return Length() == 0; +} + +inline void CUtlBinaryBlock::Clear() +{ + SetLength( 0 ); +} + +inline void CUtlBinaryBlock::Purge() +{ + SetLength( 0 ); + m_Memory.Purge(); +} + + +//----------------------------------------------------------------------------- +// Simple string class. +// NOTE: This is *not* optimal! Use in tools, but not runtime code +//----------------------------------------------------------------------------- +class CUtlString +{ +public: + CUtlString(); + CUtlString( const char *pString ); + CUtlString( const CUtlString& string ); + + // Attaches the string to external memory. Useful for avoiding a copy + CUtlString( void* pMemory, int nSizeInBytes, int nInitialLength ); + CUtlString( const void* pMemory, int nSizeInBytes ); + + const char *Get( ) const; + void Set( const char *pValue ); + + // Set directly and don't look for a null terminator in pValue. + void SetDirect( const char *pValue, int nChars ); + + // Converts to c-strings + operator const char*() const; + + // for compatibility switching items from UtlSymbol + const char *String() const { return Get(); } + + // Returns strlen + int Length() const; + bool IsEmpty() const; + + // Sets the length (used to serialize into the buffer ) + // Note: If nLen != 0, then this adds an extra byte for a null-terminator. + void SetLength( int nLen ); + char *Get(); + void Clear(); + void Purge(); + + // Strips the trailing slash + void StripTrailingSlash(); + + CUtlString &operator=( const CUtlString &src ); + CUtlString &operator=( const char *src ); + + // Test for equality + bool operator==( const CUtlString &src ) const; + bool operator==( const char *src ) const; + bool operator!=( const CUtlString &src ) const { return !operator==( src ); } + bool operator!=( const char *src ) const { return !operator==( src ); } + + CUtlString &operator+=( const CUtlString &rhs ); + CUtlString &operator+=( const char *rhs ); + CUtlString &operator+=( char c ); + CUtlString &operator+=( int rhs ); + CUtlString &operator+=( double rhs ); + + CUtlString operator+( const char *pOther ); + CUtlString operator+( int rhs ); + + int Format( const char *pFormat, ... ); + + // Take a piece out of the string. + // If you only specify nStart, it'll go from nStart to the end. + // You can use negative numbers and it'll wrap around to the start. + CUtlString Slice( int32 nStart=0, int32 nEnd=INT_MAX ); + + // Grab a substring starting from the left or the right side. + CUtlString Left( int32 nChars ); + CUtlString Right( int32 nChars ); + + // Replace all instances of one character with another. + CUtlString Replace( char cFrom, char cTo ); + + // Calls right through to V_MakeAbsolutePath. + CUtlString AbsPath( const char *pStartingDir=NULL ); + + // Gets the filename (everything except the path.. c:\a\b\c\somefile.txt -> somefile.txt). + CUtlString UnqualifiedFilename(); + + // Strips off one directory. Uses V_StripLastDir but strips the last slash also! + CUtlString DirName(); + + // Works like V_ComposeFileName. + static CUtlString PathJoin( const char *pStr1, const char *pStr2 ); + + // These can be used for utlvector sorts. + static int __cdecl SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ); + static int __cdecl SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ); + +private: + CUtlBinaryBlock m_Storage; +}; + + +//----------------------------------------------------------------------------- +// Inline methods +//----------------------------------------------------------------------------- +inline bool CUtlString::IsEmpty() const +{ + return Length() == 0; +} + +inline int __cdecl CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ) +{ + return V_stricmp( pString1->String(), pString2->String() ); +} + +inline int __cdecl CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ) +{ + return V_strcmp( pString1->String(), pString2->String() ); +} + + +#endif // UTLSTRING_H diff --git a/public/tier1/utlsymbol.h b/public/tier1/utlsymbol.h new file mode 100644 index 0000000..a3b65fd --- /dev/null +++ b/public/tier1/utlsymbol.h @@ -0,0 +1,321 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Defines a symbol table +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTLSYMBOL_H +#define UTLSYMBOL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/threadtools.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utllinkedlist.h" +#include "tier1/stringpool.h" + + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +class CUtlSymbolTable; +class CUtlSymbolTableMT; + + +//----------------------------------------------------------------------------- +// This is a symbol, which is a easier way of dealing with strings. +//----------------------------------------------------------------------------- +typedef unsigned short UtlSymId_t; + +#define UTL_INVAL_SYMBOL ((UtlSymId_t)~0) + +class CUtlSymbol +{ +public: + // constructor, destructor + CUtlSymbol() : m_Id(UTL_INVAL_SYMBOL) {} + CUtlSymbol( UtlSymId_t id ) : m_Id(id) {} + CUtlSymbol( const char* pStr ); + CUtlSymbol( CUtlSymbol const& sym ) : m_Id(sym.m_Id) {} + + // operator= + CUtlSymbol& operator=( CUtlSymbol const& src ) { m_Id = src.m_Id; return *this; } + + // operator== + bool operator==( CUtlSymbol const& src ) const { return m_Id == src.m_Id; } + bool operator==( const char* pStr ) const; + + // Is valid? + bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; } + + // Gets at the symbol + operator UtlSymId_t const() const { return m_Id; } + + // Gets the string associated with the symbol + const char* String( ) const; + + // Modules can choose to disable the static symbol table so to prevent accidental use of them. + static void DisableStaticSymbolTable(); + +protected: + UtlSymId_t m_Id; + + // Initializes the symbol table + static void Initialize(); + + // returns the current symbol table + static CUtlSymbolTableMT* CurrTable(); + + // The standard global symbol table + static CUtlSymbolTableMT* s_pSymbolTable; + + static bool s_bAllowStaticSymbolTable; + + friend class CCleanupUtlSymbolTable; +}; + + +//----------------------------------------------------------------------------- +// CUtlSymbolTable: +// description: +// This class defines a symbol table, which allows us to perform mappings +// of strings to symbols and back. The symbol class itself contains +// a static version of this class for creating global strings, but this +// class can also be instanced to create local symbol tables. +// +// This class stores the strings in a series of string pools. The first +// two bytes of each string are decorated with a hash to speed up +// comparisons. +//----------------------------------------------------------------------------- + +class CUtlSymbolTable +{ +public: + // constructor, destructor + CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); + ~CUtlSymbolTable(); + + // Finds and/or creates a symbol based on the string + CUtlSymbol AddString( const char* pString ); + + // Finds the symbol for pString + CUtlSymbol Find( const char* pString ) const; + + // Look up the string associated with a particular symbol + const char* String( CUtlSymbol id ) const; + + // Remove all symbols in the table. + void RemoveAll(); + + int GetNumStrings( void ) const + { + return m_Lookup.Count(); + } + + // We store one of these at the beginning of every string to speed + // up comparisons. + typedef unsigned short hashDecoration_t; + +protected: + class CStringPoolIndex + { + public: + inline CStringPoolIndex() + { + } + + inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset ) + : m_iPool(iPool), m_iOffset(iOffset) + {} + + inline bool operator==( const CStringPoolIndex &other ) const + { + return m_iPool == other.m_iPool && m_iOffset == other.m_iOffset; + } + + unsigned short m_iPool; // Index into m_StringPools. + unsigned short m_iOffset; // Index into the string pool. + }; + + class CLess + { + public: + CLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree + bool operator!() const { return false; } + bool operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const; + }; + + // Stores the symbol lookup + class CTree : public CUtlRBTree + { + public: + CTree( int growSize, int initSize ) : CUtlRBTree( growSize, initSize ) {} + friend class CUtlSymbolTable::CLess; // Needed to allow CLess to calculate pointer to symbol table + }; + + struct StringPool_t + { + int m_TotalLen; // How large is + int m_SpaceUsed; + char m_Data[1]; + }; + + CTree m_Lookup; + + bool m_bInsensitive; + mutable unsigned short m_nUserSearchStringHash; + mutable const char* m_pUserSearchString; + + // stores the string data + CUtlVector m_StringPools; + +private: + int FindPoolWithSpace( int len ) const; + const char* StringFromIndex( const CStringPoolIndex &index ) const; + const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; + + friend class CLess; + friend class CSymbolHash; + +}; + +class CUtlSymbolTableMT : public CUtlSymbolTable +{ +public: + CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false ) + : CUtlSymbolTable( growSize, initSize, caseInsensitive ) + { + } + + CUtlSymbol AddString( const char* pString ) + { + m_lock.LockForWrite(); + CUtlSymbol result = CUtlSymbolTable::AddString( pString ); + m_lock.UnlockWrite(); + return result; + } + + CUtlSymbol Find( const char* pString ) const + { + m_lock.LockForWrite(); + CUtlSymbol result = CUtlSymbolTable::Find( pString ); + m_lock.UnlockWrite(); + return result; + } + + const char* String( CUtlSymbol id ) const + { + m_lock.LockForRead(); + const char *pszResult = CUtlSymbolTable::String( id ); + m_lock.UnlockRead(); + return pszResult; + } + +private: + mutable CThreadSpinRWLock m_lock; +}; + + + +//----------------------------------------------------------------------------- +// CUtlFilenameSymbolTable: +// description: +// This class defines a symbol table of individual filenames, stored more +// efficiently than a standard symbol table. Internally filenames are broken +// up into file and path entries, and a file handle class allows convenient +// access to these. +//----------------------------------------------------------------------------- + +// The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor +// copies them into a static char buffer for return. +typedef void* FileNameHandle_t; + +// Symbol table for more efficiently storing filenames by breaking paths and filenames apart. +// Refactored from BaseFileSystem.h +class CUtlFilenameSymbolTable +{ + // Internal representation of a FileHandle_t + // If we get more than 64K filenames, we'll have to revisit... + // Right now CUtlSymbol is a short, so this packs into an int/void * pointer size... + struct FileNameHandleInternal_t + { + FileNameHandleInternal_t() + { + path = 0; + file = 0; + } + + // Part before the final '/' character + unsigned short path; + // Part after the final '/', including extension + unsigned short file; + }; + +public: + FileNameHandle_t FindOrAddFileName( const char *pFileName ); + FileNameHandle_t FindFileName( const char *pFileName ); + int PathIndex(const FileNameHandle_t &handle) { return (( const FileNameHandleInternal_t * )&handle)->path; } + bool String( const FileNameHandle_t& handle, char *buf, int buflen ); + void RemoveAll(); + void SpewStrings(); + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer ); + +private: + CCountedStringPool m_StringPool; + mutable CThreadSpinRWLock m_lock; +}; + +// This creates a simple class that includes the underlying CUtlSymbol +// as a private member and then instances a private symbol table to +// manage those symbols. Avoids the possibility of the code polluting the +// 'global'/default symbol table, while letting the code look like +// it's just using = and .String() to look at CUtlSymbol type objects +// +// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course) +// +#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \ + class typename \ + { \ + public: \ + typename(); \ + typename( const char* pStr ); \ + typename& operator=( typename const& src ); \ + bool operator==( typename const& src ) const; \ + const char* String( ) const; \ + private: \ + CUtlSymbol m_SymbolId; \ + }; + +// Put this in the .cpp file that uses the above typename +#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \ + static CUtlSymbolTable g_##typename##SymbolTable; \ + typename::typename() \ + { \ + m_SymbolId = UTL_INVAL_SYMBOL; \ + } \ + typename::typename( const char* pStr ) \ + { \ + m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \ + } \ + typename& typename::operator=( typename const& src ) \ + { \ + m_SymbolId = src.m_SymbolId; \ + return *this; \ + } \ + bool typename::operator==( typename const& src ) const \ + { \ + return ( m_SymbolId == src.m_SymbolId ); \ + } \ + const char* typename::String( ) const \ + { \ + return g_##typename##SymbolTable.String( m_SymbolId ); \ + } + +#endif // UTLSYMBOL_H diff --git a/public/tier1/utlsymbollarge.h b/public/tier1/utlsymbollarge.h new file mode 100644 index 0000000..34e4bf9 --- /dev/null +++ b/public/tier1/utlsymbollarge.h @@ -0,0 +1,494 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Defines a large symbol table (intp sized handles, can store more than 64k strings) +// +// $Header: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef UTLSYMBOLLARGE_H +#define UTLSYMBOLLARGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/threadtools.h" +#include "tier1/utltshash.h" +#include "tier1/stringpool.h" +#include "tier0/vprof.h" +#include "tier1/utltshash.h" + +//----------------------------------------------------------------------------- +// CUtlSymbolTableLarge: +// description: +// This class defines a symbol table, which allows us to perform mappings +// of strings to symbols and back. +// +// This class stores the strings in a series of string pools. The returned CUtlSymbolLarge is just a pointer +// to the string data, the hash precedes it in memory and is used to speed up searching, etc. +//----------------------------------------------------------------------------- + +typedef intp UtlSymLargeId_t; + +#define UTL_INVAL_SYMBOL_LARGE ((UtlSymLargeId_t)~0) + +class CUtlSymbolLarge +{ +public: + // constructor, destructor + CUtlSymbolLarge() + { + u.m_Id = UTL_INVAL_SYMBOL_LARGE; + } + + CUtlSymbolLarge( UtlSymLargeId_t id ) + { + u.m_Id = id; + } + CUtlSymbolLarge( CUtlSymbolLarge const& sym ) + { + u.m_Id = sym.u.m_Id; + } + + // operator= + CUtlSymbolLarge& operator=( CUtlSymbolLarge const& src ) + { + u.m_Id = src.u.m_Id; + return *this; + } + + // operator== + bool operator==( CUtlSymbolLarge const& src ) const + { + return u.m_Id == src.u.m_Id; + } + + // operator== + bool operator==( UtlSymLargeId_t const& src ) const + { + return u.m_Id == src; + } + + // operator== + bool operator!=( CUtlSymbolLarge const& src ) const + { + return u.m_Id != src.u.m_Id; + } + + // operator== + bool operator!=( UtlSymLargeId_t const& src ) const + { + return u.m_Id != src; + } + + // Gets at the symbol + operator UtlSymLargeId_t const() const + { + return u.m_Id; + } + + // Gets the string associated with the symbol + inline const char* String( ) const + { + if ( u.m_Id == UTL_INVAL_SYMBOL_LARGE ) + return ""; + return u.m_pAsString; + } + + inline bool IsValid() const + { + return u.m_Id != UTL_INVAL_SYMBOL_LARGE ? true : false; + } + +private: + // Disallowed + CUtlSymbolLarge( const char* pStr ); // they need to go through the table to assign the ptr + bool operator==( const char* pStr ) const; // disallow since we don't know if the table this is from was case sensitive or not... maybe we don't care + + union + { + UtlSymLargeId_t m_Id; + char const *m_pAsString; + } u; +}; + +#define MIN_STRING_POOL_SIZE 2048 + +inline uint32 CUtlSymbolLarge_Hash( bool CASEINSENSITIVE, const char *pString, int len ) +{ + return ( CASEINSENSITIVE ? HashStringCaseless( pString ) : HashString( pString ) ); +} + +typedef uint32 LargeSymbolTableHashDecoration_t; + +// The structure consists of the hash immediately followed by the string data +struct CUtlSymbolTableLargeBaseTreeEntry_t +{ + LargeSymbolTableHashDecoration_t m_Hash; + // Variable length string data + char m_String[1]; + + bool IsEmpty() const + { + return ( ( m_Hash == 0 ) && ( 0 == m_String[0] ) ); + } + + char const *String() const + { + return (const char *)&m_String[ 0 ]; + } + + CUtlSymbolLarge ToSymbol() const + { + return reinterpret_cast< UtlSymLargeId_t >( String() ); + } + + LargeSymbolTableHashDecoration_t HashValue() const + { + return m_Hash; + } +}; + +template< class TreeType, bool CASEINSENSITIVE > +class CTreeEntryLess +{ +public: + CTreeEntryLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree + bool operator!() const { return false; } + bool operator()( CUtlSymbolTableLargeBaseTreeEntry_t * const &left, CUtlSymbolTableLargeBaseTreeEntry_t * const &right ) const + { + // compare the hashes + if ( left->m_Hash == right->m_Hash ) + { + // if the hashes match compare the strings + if ( !CASEINSENSITIVE ) + return strcmp( left->String(), right->String() ) < 0; + else + return V_stricmp( left->String(), right->String() ) < 0; + } + else + { + return left->m_Hash < right->m_Hash; + } + } +}; + +// For non-threaded versions, simply index into CUtlRBTree +template< bool CASEINSENSITIVE > +class CNonThreadsafeTree : public CUtlRBTree, CASEINSENSITIVE > > +{ +public: + typedef CUtlRBTree > CNonThreadsafeTreeType; + + CNonThreadsafeTree() : + CNonThreadsafeTreeType( 0, 16 ) + { + } + inline void Commit() + { + // Nothing, only matters for thread-safe tables + } + inline int Insert( CUtlSymbolTableLargeBaseTreeEntry_t *entry ) + { + return CNonThreadsafeTreeType::Insert( entry ); + } + inline int Find( CUtlSymbolTableLargeBaseTreeEntry_t *entry ) const + { + return CNonThreadsafeTreeType::Find( entry ); + } + inline int InvalidIndex() const + { + return CNonThreadsafeTreeType::InvalidIndex(); + } + inline int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const + { + CUtlVector< CUtlSymbolTableLargeBaseTreeEntry_t * > list; + list.EnsureCount( nCount ); + for ( int i = 0; i < nCount; ++i ) + { + pElements[ i ] = CNonThreadsafeTreeType::Element( i )->ToSymbol(); + } + + return nCount; + } +}; + +// Since CUtlSymbolTableLargeBaseTreeEntry_t already has the hash +// contained inside of it, don't need to recompute a hash here +template < int BUCKET_COUNT, class KEYTYPE, bool CASEINSENSITIVE > +class CCThreadsafeTreeHashMethod +{ +public: + static int Hash( const KEYTYPE &key, int nBucketMask ) + { + uint32 nHash = key->HashValue(); + return ( nHash & nBucketMask ); + } + + static bool Compare( CUtlSymbolTableLargeBaseTreeEntry_t * const &lhs, CUtlSymbolTableLargeBaseTreeEntry_t * const &rhs ) + { + if ( lhs->m_Hash != rhs->m_Hash ) + return false; + if ( !CASEINSENSITIVE ) + { + return ( !Q_strcmp( lhs->String(), rhs->String() ) ? true : false ); + } + + return ( !Q_stricmp( lhs->String(), rhs->String() ) ? true : false ); + } +}; + +/* + NOTE: So the only crappy thing about using a CUtlTSHash here is that the KEYTYPE is a CUtlSymbolTableLargeBaseTreeEntry_t ptr which has both the + hash and the string since with strings there is a good chance of hash collision after you have a fair number of strings so we have to implement + a Compare method (above) which falls back to strcmp/stricmp if the hashes are equal. This means that all of the data is in the KEYTYPE of the hash and the + payload doesn't matter. So I made the payload also be a pointer to a CUtlSymbolTableLargeBaseTreeEntry_t since that makes using the API more convenient + + TODO: If we have a CUtlTSHash that was all about the existence of the KEYTYPE and didn't require a payload (or template on 'void') then we could eliminate + 50% of the pointer overhead used for this data structure. +*/ + +// Thread safe version is based on the +template < bool CASEINSENSITIVE > +class CThreadsafeTree : public CUtlTSHash< CUtlSymbolTableLargeBaseTreeEntry_t *, 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CCThreadsafeTreeHashMethod< 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CASEINSENSITIVE > > +{ +public: + typedef CUtlTSHash< CUtlSymbolTableLargeBaseTreeEntry_t *, 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CCThreadsafeTreeHashMethod< 2048, CUtlSymbolTableLargeBaseTreeEntry_t *, CASEINSENSITIVE > > CThreadsafeTreeType; + + CThreadsafeTree() : + CThreadsafeTreeType( 32 ) + { + } + inline void Commit() + { + CThreadsafeTreeType::Commit(); + } + inline int Insert( CUtlSymbolTableLargeBaseTreeEntry_t *entry ) + { + return CThreadsafeTreeType::Insert( entry, entry ); + } + inline int Find( CUtlSymbolTableLargeBaseTreeEntry_t *entry ) + { + return CThreadsafeTreeType::Find( entry ); + } + inline int InvalidIndex() const + { + return CThreadsafeTreeType::InvalidHandle(); + } + inline int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const + { + CUtlVector< UtlTSHashHandle_t > list; + list.EnsureCount( nCount ); + int c = CThreadsafeTreeType::GetElements( nFirstElement, nCount, list.Base() ); + for ( int i = 0; i < c; ++i ) + { + pElements[ i ] = CThreadsafeTreeType::Element( list[ i ] )->ToSymbol(); + } + + return c; + } +}; + +// Base Class for threaded and non-threaded types +template < class TreeType, bool CASEINSENSITIVE > +class CUtlSymbolTableLargeBase +{ +public: + // constructor, destructor + CUtlSymbolTableLargeBase(); + ~CUtlSymbolTableLargeBase(); + + // Finds and/or creates a symbol based on the string + CUtlSymbolLarge AddString( const char* pString ); + + // Finds the symbol for pString + CUtlSymbolLarge Find( const char* pString ) const; + + // Remove all symbols in the table. + void RemoveAll(); + + int GetNumStrings( void ) const + { + return m_Lookup.Count(); + } + + void Commit() + { + m_Lookup.Commit(); + } + + // Returns elements in the table + int GetElements( int nFirstElement, int nCount, CUtlSymbolLarge *pElements ) const + { + return m_Lookup.GetElements( nFirstElement, nCount, pElements ); + } + + uint64 GetMemoryUsage() const + { + uint64 unBytesUsed = 0u; + + for ( int i=0; i < m_StringPools.Count(); i++ ) + { + StringPool_t *pPool = m_StringPools[i]; + + unBytesUsed += (uint64)pPool->m_TotalLen; + } + return unBytesUsed; + } + + +protected: + + struct StringPool_t + { + int m_TotalLen; // How large is + int m_SpaceUsed; + char m_Data[1]; + }; + + TreeType m_Lookup; + + // stores the string data + CUtlVector< StringPool_t * > m_StringPools; + +private: + int FindPoolWithSpace( int len ) const; +}; + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template< class TreeType, bool CASEINSENSITIVE > +inline CUtlSymbolTableLargeBase::CUtlSymbolTableLargeBase() : + m_StringPools( 8 ) +{ +} + +template< class TreeType, bool CASEINSENSITIVE > +inline CUtlSymbolTableLargeBase::~CUtlSymbolTableLargeBase() +{ + // Release the stringpool string data + RemoveAll(); +} + +template< class TreeType, bool CASEINSENSITIVE > +inline CUtlSymbolLarge CUtlSymbolTableLargeBase::Find( const char* pString ) const +{ + VPROF( "CUtlSymbolLarge::Find" ); + if (!pString) + return CUtlSymbolLarge(); + + // Passing this special invalid symbol makes the comparison function + // use the string passed in the context + int len = Q_strlen( pString ) + 1; + + CUtlSymbolTableLargeBaseTreeEntry_t *search = (CUtlSymbolTableLargeBaseTreeEntry_t *)_alloca( len + sizeof( LargeSymbolTableHashDecoration_t ) ); + search->m_Hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, len ); + Q_memcpy( (char *)&search->m_String[ 0 ], pString, len ); + + int idx = const_cast< TreeType & >(m_Lookup).Find( search ); + + if ( idx == m_Lookup.InvalidIndex() ) + return UTL_INVAL_SYMBOL_LARGE; + + const CUtlSymbolTableLargeBaseTreeEntry_t *entry = m_Lookup[ idx ]; + return entry->ToSymbol(); +} + +template< class TreeType, bool CASEINSENSITIVE > +inline int CUtlSymbolTableLargeBase::FindPoolWithSpace( int len ) const +{ + for ( int i=0; i < m_StringPools.Count(); i++ ) + { + StringPool_t *pPool = m_StringPools[i]; + + if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len ) + { + return i; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Finds and/or creates a symbol based on the string +//----------------------------------------------------------------------------- +template< class TreeType, bool CASEINSENSITIVE > +inline CUtlSymbolLarge CUtlSymbolTableLargeBase::AddString( const char* pString ) +{ + VPROF("CUtlSymbolLarge::AddString"); + if (!pString) + return UTL_INVAL_SYMBOL_LARGE; + + CUtlSymbolLarge id = Find( pString ); + if ( id != UTL_INVAL_SYMBOL_LARGE ) + return id; + + int lenString = Q_strlen(pString) + 1; // length of just the string + int lenDecorated = lenString + sizeof(LargeSymbolTableHashDecoration_t); // and with its hash decoration + // make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly + COMPILE_TIME_ASSERT(sizeof(LargeSymbolTableHashDecoration_t) == sizeof(intp)); + lenDecorated = ALIGN_VALUE(lenDecorated, sizeof( intp ) ); + + // Find a pool with space for this string, or allocate a new one. + int iPool = FindPoolWithSpace( lenDecorated ); + if ( iPool == -1 ) + { + // Add a new pool. + int newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE ); + StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize ); + + pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t ); + pPool->m_SpaceUsed = 0; + iPool = m_StringPools.AddToTail( pPool ); + } + + // Compute a hash + LargeSymbolTableHashDecoration_t hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, lenString ); + + // Copy the string in. + StringPool_t *pPool = m_StringPools[iPool]; + Assert( pPool->m_SpaceUsed < 0xFFFF ); + // This should never happen, because if we had a string > 64k, it + // would have been given its entire own pool. + + CUtlSymbolTableLargeBaseTreeEntry_t *entry = ( CUtlSymbolTableLargeBaseTreeEntry_t * )&pPool->m_Data[ pPool->m_SpaceUsed ]; + + pPool->m_SpaceUsed += lenDecorated; + + entry->m_Hash = hash; + char *pText = (char *)&entry->m_String [ 0 ]; + Q_memcpy( pText, pString, lenString ); + + // insert the string into the database + MEM_ALLOC_CREDIT(); + int idx = m_Lookup.Insert( entry ); + return m_Lookup.Element( idx )->ToSymbol(); +} + +//----------------------------------------------------------------------------- +// Remove all symbols in the table. +//----------------------------------------------------------------------------- +template< class TreeType, bool CASEINSENSITIVE > +inline void CUtlSymbolTableLargeBase::RemoveAll() +{ + m_Lookup.Purge(); + + for ( int i=0; i < m_StringPools.Count(); i++ ) + free( m_StringPools[i] ); + + m_StringPools.RemoveAll(); +} + +// Case-sensitive +typedef CUtlSymbolTableLargeBase< CNonThreadsafeTree< false >, false > CUtlSymbolTableLarge; +// Case-insensitive +typedef CUtlSymbolTableLargeBase< CNonThreadsafeTree< true >, true > CUtlSymbolTableLarge_CI; +// Multi-threaded case-sensitive +typedef CUtlSymbolTableLargeBase< CThreadsafeTree< false >, false > CUtlSymbolTableLargeMT; +// Multi-threaded case-insensitive +typedef CUtlSymbolTableLargeBase< CThreadsafeTree< true >, true > CUtlSymbolTableLargeMT_CI; + +#endif // UTLSYMBOLLARGE_H diff --git a/public/tier1/utltshash.h b/public/tier1/utltshash.h new file mode 100644 index 0000000..e3bd23e --- /dev/null +++ b/public/tier1/utltshash.h @@ -0,0 +1,625 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Thread-safe hash class +//===========================================================================// + +#ifndef UTLTSHASH_H +#define UTLTSHASH_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier0/threadtools.h" +#include "tier1/mempool.h" +#include "generichash.h" + + +//============================================================================= +// +// Threadsafe Hash +// +// Number of buckets must be a power of 2. +// Key must be intp sized (32-bits on x32, 64-bits on x64) +// Designed for a usage pattern where the data is semi-static, and there +// is a well-defined point where we are guaranteed no queries are occurring. +// +// Insertions are added into a thread-safe list, and when Commit() is called, +// the insertions are moved into a lock-free list +// +// Elements are never individually removed; clears must occur at a time +// where we and guaranteed no queries are occurring +// +typedef intp UtlTSHashHandle_t; + +template < class T > +abstract_class ITSHashConstructor +{ +public: + virtual void Construct( T* pElement ) = 0; +}; + +template < class T > +class CDefaultTSHashConstructor : public ITSHashConstructor< T > +{ +public: + virtual void Construct( T* pElement ) + { + ::Construct( pElement ); + } +}; + +template < int BUCKET_COUNT, class KEYTYPE = intp > +class CUtlTSHashGenericHash +{ +public: + static int Hash( const KEYTYPE &key, int nBucketMask ) + { + int nHash = HashIntConventional( (intp)key ); + if ( BUCKET_COUNT <= USHRT_MAX ) + { + nHash ^= ( nHash >> 16 ); + } + if ( BUCKET_COUNT <= UCHAR_MAX ) + { + nHash ^= ( nHash >> 8 ); + } + return ( nHash & nBucketMask ); + } + + static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs ) + { + return lhs == rhs; + } +}; + +template < int BUCKET_COUNT, class KEYTYPE > +class CUtlTSHashUseKeyHashMethod +{ +public: + static int Hash( const KEYTYPE &key, int nBucketMask ) + { + uint32 nHash = key.HashValue(); + return ( nHash & nBucketMask ); + } + + static bool Compare( const KEYTYPE &lhs, const KEYTYPE &rhs ) + { + return lhs == rhs; + } +}; + +template< class T, int BUCKET_COUNT, class KEYTYPE = intp, class HashFuncs = CUtlTSHashGenericHash< BUCKET_COUNT, KEYTYPE >, int nAlignment = 0 > +class CUtlTSHash +{ +public: + // Constructor/Deconstructor. + CUtlTSHash( int nAllocationCount ); + ~CUtlTSHash(); + + // Invalid handle. + static UtlTSHashHandle_t InvalidHandle( void ) { return ( UtlTSHashHandle_t )0; } + + // Retrieval. Super fast, is thread-safe + UtlTSHashHandle_t Find( KEYTYPE uiKey ); + + // Insertion ( find or add ). + UtlTSHashHandle_t Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert = NULL ); + UtlTSHashHandle_t Insert( KEYTYPE uiKey, ITSHashConstructor *pConstructor, bool *pDidInsert = NULL ); + + // This insertion method assumes the element is not in the hash table, skips + UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, const T &data ); + UtlTSHashHandle_t FastInsert( KEYTYPE uiKey, ITSHashConstructor *pConstructor ); + + // Commit recent insertions, making finding them faster. + // Only call when you're certain no threads are accessing the hash table + void Commit( ); + + // Removal. Only call when you're certain no threads are accessing the hash table + void FindAndRemove( KEYTYPE uiKey ); + void Remove( UtlTSHashHandle_t hHash ) { FindAndRemove( GetID( hHash ) ); } + void RemoveAll( void ); + void Purge( void ); + + // Returns the number of elements in the hash table + int Count() const; + + // Returns elements in the table + int GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const; + + // Element access + T &Element( UtlTSHashHandle_t hHash ); + T const &Element( UtlTSHashHandle_t hHash ) const; + T &operator[]( UtlTSHashHandle_t hHash ); + T const &operator[]( UtlTSHashHandle_t hHash ) const; + KEYTYPE GetID( UtlTSHashHandle_t hHash ) const; + + // Convert element * to hashHandle + UtlTSHashHandle_t ElementPtrToHandle( T* pElement ) const; + +private: + // Templatized for memory tracking purposes + template < typename Data_t > + struct HashFixedDataInternal_t + { + KEYTYPE m_uiKey; + HashFixedDataInternal_t< Data_t >* m_pNext; + Data_t m_Data; + }; + + typedef HashFixedDataInternal_t HashFixedData_t; + + enum + { + BUCKET_MASK = BUCKET_COUNT - 1 + }; + + struct HashBucket_t + { + HashFixedData_t *m_pFirst; + HashFixedData_t *m_pFirstUncommitted; + CThreadSpinRWLock m_AddLock; + }; + + UtlTSHashHandle_t Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement ); + UtlTSHashHandle_t InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket ); + CMemoryPoolMT m_EntryMemory; + HashBucket_t m_aBuckets[BUCKET_COUNT]; + bool m_bNeedsCommit; + +#ifdef _DEBUG + CInterlockedInt m_ContentionCheck; +#endif +}; + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template +CUtlTSHash::CUtlTSHash( int nAllocationCount ) : + m_EntryMemory( sizeof( HashFixedData_t ), nAllocationCount, CUtlMemoryPool::GROW_SLOW, MEM_ALLOC_CLASSNAME( HashFixedData_t ), nAlignment ) +{ +#ifdef _DEBUG + m_ContentionCheck = 0; +#endif + m_bNeedsCommit = false; + for ( int i = 0; i < BUCKET_COUNT; i++ ) + { + HashBucket_t &bucket = m_aBuckets[ i ]; + bucket.m_pFirst = NULL; + bucket.m_pFirstUncommitted = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template +CUtlTSHash::~CUtlTSHash() +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template +inline void CUtlTSHash::Purge( void ) +{ + RemoveAll(); +} + + +//----------------------------------------------------------------------------- +// Returns the number of elements in the hash table +//----------------------------------------------------------------------------- +template +inline int CUtlTSHash::Count() const +{ + return m_EntryMemory.Count(); +} + + +//----------------------------------------------------------------------------- +// Returns elements in the table +//----------------------------------------------------------------------------- +template +int CUtlTSHash::GetElements( int nFirstElement, int nCount, UtlTSHashHandle_t *pHandles ) const +{ + int nIndex = 0; + for ( int i = 0; i < BUCKET_COUNT; i++ ) + { + const HashBucket_t &bucket = m_aBuckets[ i ]; + bucket.m_AddLock.LockForRead( ); + for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext ) + { + if ( --nFirstElement >= 0 ) + continue; + + pHandles[ nIndex++ ] = (UtlTSHashHandle_t)pElement; + if ( nIndex >= nCount ) + { + bucket.m_AddLock.UnlockRead( ); + return nIndex; + } + } + bucket.m_AddLock.UnlockRead( ); + } + return nIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (KEYTYPE), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template +inline UtlTSHashHandle_t CUtlTSHash::InsertUncommitted( KEYTYPE uiKey, HashBucket_t &bucket ) +{ + m_bNeedsCommit = true; + HashFixedData_t *pNewElement = static_cast< HashFixedData_t * >( m_EntryMemory.Alloc() ); + pNewElement->m_pNext = bucket.m_pFirstUncommitted; + bucket.m_pFirstUncommitted = pNewElement; + pNewElement->m_uiKey = uiKey; + return (UtlTSHashHandle_t)pNewElement; +} + + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key, with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template +inline UtlTSHashHandle_t CUtlTSHash::Insert( KEYTYPE uiKey, const T &data, bool *pDidInsert ) +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + + if ( pDidInsert ) + { + *pDidInsert = false; + } + + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + HashBucket_t &bucket = m_aBuckets[ iBucket ]; + + // First try lock-free + UtlTSHashHandle_t h = Find( uiKey ); + if ( h != InvalidHandle() ) + return h; + + // Now, try again, but only look in uncommitted elements + bucket.m_AddLock.LockForWrite( ); + + h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); + if ( h == InvalidHandle() ) + { + h = InsertUncommitted( uiKey, bucket ); + CopyConstruct( &Element(h), data ); + if ( pDidInsert ) + { + *pDidInsert = true; + } + } + + bucket.m_AddLock.UnlockWrite( ); + return h; +} + +template +inline UtlTSHashHandle_t CUtlTSHash::Insert( KEYTYPE uiKey, ITSHashConstructor *pConstructor, bool *pDidInsert ) +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + + if ( pDidInsert ) + { + *pDidInsert = false; + } + + // First try lock-free + UtlTSHashHandle_t h = Find( uiKey ); + if ( h != InvalidHandle() ) + return h; + + // Now, try again, but only look in uncommitted elements + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + HashBucket_t &bucket = m_aBuckets[ iBucket ]; + bucket.m_AddLock.LockForWrite( ); + + h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); + if ( h == InvalidHandle() ) + { + // Useful if non-trivial work needs to happen to make data; don't want to + // do it and then have to undo it if it turns out we don't need to add it + h = InsertUncommitted( uiKey, bucket ); + pConstructor->Construct( &Element(h) ); + if ( pDidInsert ) + { + *pDidInsert = true; + } + } + + bucket.m_AddLock.UnlockWrite( ); + return h; +} + + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template +inline UtlTSHashHandle_t CUtlTSHash::FastInsert( KEYTYPE uiKey, const T &data ) +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + HashBucket_t &bucket = m_aBuckets[ iBucket ]; + bucket.m_AddLock.LockForWrite( ); + UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket ); + CopyConstruct( &Element(h), data ); + bucket.m_AddLock.UnlockWrite( ); + return h; +} + +template +inline UtlTSHashHandle_t CUtlTSHash::FastInsert( KEYTYPE uiKey, ITSHashConstructor *pConstructor ) +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + HashBucket_t &bucket = m_aBuckets[ iBucket ]; + bucket.m_AddLock.LockForWrite( ); + UtlTSHashHandle_t h = InsertUncommitted( uiKey, bucket ); + pConstructor->Construct( &Element(h) ); + bucket.m_AddLock.UnlockWrite( ); + return h; +} + + +//----------------------------------------------------------------------------- +// Purpose: Commits all uncommitted insertions +//----------------------------------------------------------------------------- +template +inline void CUtlTSHash::Commit( ) +{ + // FIXME: Is this legal? Want this to be lock-free + if ( !m_bNeedsCommit ) + return; + + // This must occur when no queries are occurring +#ifdef _DEBUG + m_ContentionCheck++; +#endif + + for ( int i = 0; i < BUCKET_COUNT; i++ ) + { + HashBucket_t &bucket = m_aBuckets[ i ]; + bucket.m_AddLock.LockForRead( ); + bucket.m_pFirst = bucket.m_pFirstUncommitted; + bucket.m_AddLock.UnlockRead( ); + } + + m_bNeedsCommit = false; + +#ifdef _DEBUG + m_ContentionCheck--; +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Remove a single element from the hash +//----------------------------------------------------------------------------- +template +inline void CUtlTSHash::FindAndRemove( KEYTYPE uiKey ) +{ + if ( m_EntryMemory.Count() == 0 ) + return; + + // This must occur when no queries are occurring +#ifdef _DEBUG + m_ContentionCheck++; +#endif + + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + HashBucket_t &bucket = m_aBuckets[ iBucket ]; + bucket.m_AddLock.LockForWrite( ); + + HashFixedData_t *pPrev = NULL; + for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pPrev = pElement, pElement = pElement->m_pNext ) + { + if ( !HashFuncs::Compare( pElement->m_uiKey, uiKey ) ) + continue; + + if ( pPrev ) + { + pPrev->m_pNext = pElement->m_pNext; + } + else + { + bucket.m_pFirstUncommitted = pElement->m_pNext; + } + + if ( bucket.m_pFirst == pElement ) + { + bucket.m_pFirst = bucket.m_pFirst->m_pNext; + } + + Destruct( &pElement->m_Data ); + +#ifdef _DEBUG + memset( pElement, 0xDD, sizeof(HashFixedData_t) ); +#endif + + m_EntryMemory.Free( pElement ); + + break; + } + + bucket.m_AddLock.UnlockWrite( ); + +#ifdef _DEBUG + m_ContentionCheck--; +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template +inline void CUtlTSHash::RemoveAll( void ) +{ + m_bNeedsCommit = false; + if ( m_EntryMemory.Count() == 0 ) + return; + + // This must occur when no queries are occurring +#ifdef _DEBUG + m_ContentionCheck++; +#endif + + for ( int i = 0; i < BUCKET_COUNT; i++ ) + { + HashBucket_t &bucket = m_aBuckets[ i ]; + + bucket.m_AddLock.LockForWrite( ); + + for ( HashFixedData_t *pElement = bucket.m_pFirstUncommitted; pElement; pElement = pElement->m_pNext ) + { + Destruct( &pElement->m_Data ); + } + + bucket.m_pFirst = NULL; + bucket.m_pFirstUncommitted = NULL; + bucket.m_AddLock.UnlockWrite( ); + } + + m_EntryMemory.Clear(); + +#ifdef _DEBUG + m_ContentionCheck--; +#endif +} + +//----------------------------------------------------------------------------- +// Finds an element, but only in the committed elements +//----------------------------------------------------------------------------- +template +inline UtlTSHashHandle_t CUtlTSHash::Find( KEYTYPE uiKey, HashFixedData_t *pFirstElement, HashFixedData_t *pLastElement ) +{ +#ifdef _DEBUG + if ( m_ContentionCheck != 0 ) + { + DebuggerBreak(); + } +#endif + + for ( HashFixedData_t *pElement = pFirstElement; pElement != pLastElement; pElement = pElement->m_pNext ) + { + if ( HashFuncs::Compare( pElement->m_uiKey, uiKey ) ) + return (UtlTSHashHandle_t)pElement; + } + return InvalidHandle(); +} + + +//----------------------------------------------------------------------------- +// Finds an element, but only in the committed elements +//----------------------------------------------------------------------------- +template +inline UtlTSHashHandle_t CUtlTSHash::Find( KEYTYPE uiKey ) +{ + int iBucket = HashFuncs::Hash( uiKey, BUCKET_MASK ); + const HashBucket_t &bucket = m_aBuckets[iBucket]; + UtlTSHashHandle_t h = Find( uiKey, bucket.m_pFirst, NULL ); + if ( h != InvalidHandle() ) + return h; + + // Didn't find it in the fast ( committed ) list. Let's try the slow ( uncommitted ) one + bucket.m_AddLock.LockForRead( ); + h = Find( uiKey, bucket.m_pFirstUncommitted, bucket.m_pFirst ); + bucket.m_AddLock.UnlockRead( ); + + return h; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template +inline T &CUtlTSHash::Element( UtlTSHashHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +template +inline T const &CUtlTSHash::Element( UtlTSHashHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +template +inline T &CUtlTSHash::operator[]( UtlTSHashHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +template +inline T const &CUtlTSHash::operator[]( UtlTSHashHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + + +template +inline KEYTYPE CUtlTSHash::GetID( UtlTSHashHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_uiKey; +} + + +// Convert element * to hashHandle +template +inline UtlTSHashHandle_t CUtlTSHash::ElementPtrToHandle( T* pElement ) const +{ + Assert( pElement ); + HashFixedData_t *pFixedData = (HashFixedData_t*)( (uint8*)pElement - offsetof( HashFixedData_t, m_Data ) ); + Assert( m_EntryMemory.IsAllocationWithinPool( pFixedData ) ); + return (UtlTSHashHandle_t)pFixedData; +} + + +#endif // UTLTSHASH_H diff --git a/public/tier1/utlvector.h b/public/tier1/utlvector.h new file mode 100644 index 0000000..c9d8066 --- /dev/null +++ b/public/tier1/utlvector.h @@ -0,0 +1,1209 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// A growable array class that maintains a free list and keeps elements +// in the same location +//=============================================================================// + +#ifndef UTLVECTOR_H +#define UTLVECTOR_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "tier1/utlmemory.h" +#include "tier1/utlblockmemory.h" +#include "tier1/strtools.h" + +#define FOR_EACH_VEC( vecName, iteratorName ) \ + for ( int iteratorName = 0; iteratorName < (vecName).Count(); iteratorName++ ) +#define FOR_EACH_VEC_BACK( vecName, iteratorName ) \ + for ( int iteratorName = (vecName).Count()-1; iteratorName >= 0; iteratorName-- ) + +//----------------------------------------------------------------------------- +// The CUtlVector class: +// A growable array class which doubles in size by default. +// It will always keep all elements consecutive in memory, and may move the +// elements around in memory (via a PvRealloc) when elements are inserted or +// removed. Clients should therefore refer to the elements of the vector +// by index (they should *never* maintain pointers to elements in the vector). +//----------------------------------------------------------------------------- +template< class T, class A = CUtlMemory > +class CUtlVector +{ + typedef A CAllocator; +public: + typedef T ElemType_t; + + // constructor, destructor + CUtlVector( int growSize = 0, int initSize = 0 ); + CUtlVector( T* pMemory, int allocationCount, int numElements = 0 ); + ~CUtlVector(); + + // Copy the array. + CUtlVector& operator=( const CUtlVector &other ); + + // element access + T& operator[]( int i ); + const T& operator[]( int i ) const; + T& Element( int i ); + const T& Element( int i ) const; + T& Head(); + const T& Head() const; + T& Tail(); + const T& Tail() const; + + // Gets the base address (can change when adding elements!) + T* Base() { return m_Memory.Base(); } + const T* Base() const { return m_Memory.Base(); } + + // Returns the number of elements in the vector + int Count() const; + + // Is element index valid? + bool IsValidIndex( int i ) const; + static int InvalidIndex(); + + // Adds an element, uses default constructor + int AddToHead(); + int AddToTail(); + int InsertBefore( int elem ); + int InsertAfter( int elem ); + + // Adds an element, uses copy constructor + int AddToHead( const T& src ); + int AddToTail( const T& src ); + int InsertBefore( int elem, const T& src ); + int InsertAfter( int elem, const T& src ); + + // Adds multiple elements, uses default constructor + int AddMultipleToHead( int num ); + int AddMultipleToTail( int num ); + int AddMultipleToTail( int num, const T *pToCopy ); + int InsertMultipleBefore( int elem, int num ); + int InsertMultipleBefore( int elem, int num, const T *pToCopy ); + int InsertMultipleAfter( int elem, int num ); + + // Calls RemoveAll() then AddMultipleToTail. + void SetSize( int size ); + void SetCount( int count ); + void SetCountNonDestructively( int count ); //sets count by adding or removing elements to tail TODO: This should probably be the default behavior for SetCount + + // Calls SetSize and copies each element. + void CopyArray( const T *pArray, int size ); + + // Fast swap + void Swap( CUtlVector< T, A > &vec ); + + // Add the specified array to the tail. + int AddVectorToTail( CUtlVector const &src ); + + // Finds an element (element needs operator== defined) + int Find( const T& src ) const; + void FillWithValue( const T& src ); + + bool HasElement( const T& src ) const; + + // Makes sure we have enough memory allocated to store a requested # of elements + void EnsureCapacity( int num ); + + // Makes sure we have at least this many elements + void EnsureCount( int num ); + + // Element removal + void FastRemove( int elem ); // doesn't preserve order + void Remove( int elem ); // preserves order, shifts elements + bool FindAndRemove( const T& src ); // removes first occurrence of src, preserves order, shifts elements + bool FindAndFastRemove( const T& src ); // removes first occurrence of src, doesn't preserve order + void RemoveMultiple( int elem, int num ); // preserves order, shifts elements + void RemoveMultipleFromHead(int num); // removes num elements from tail + void RemoveMultipleFromTail(int num); // removes num elements from tail + void RemoveAll(); // doesn't deallocate memory + + // Memory deallocation + void Purge(); + + // Purges the list and calls delete on each element in it. + void PurgeAndDeleteElements(); + + // Compacts the vector to the number of elements actually in use + void Compact(); + + // Set the size by which it grows when it needs to allocate more memory. + void SetGrowSize( int size ) { m_Memory.SetGrowSize( size ); } + + int NumAllocated() const; // Only use this if you really know what you're doing! + + void Sort( int (__cdecl *pfnCompare)(const T *, const T *) ); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +protected: + // Can't copy this unless we explicitly do it! + CUtlVector( CUtlVector const& vec ) { Assert(0); } + + // Grows the vector + void GrowVector( int num = 1 ); + + // Shifts elements.... + void ShiftElementsRight( int elem, int num = 1 ); + void ShiftElementsLeft( int elem, int num = 1 ); + + CAllocator m_Memory; + int m_Size; + +#ifndef _X360 + // For easier access to the elements through the debugger + // it's in release builds so this can be used in libraries correctly + T *m_pElements; + + inline void ResetDbgInfo() + { + m_pElements = Base(); + } +#else + inline void ResetDbgInfo() {} +#endif +}; + + +// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice +template < class T > +class CUtlBlockVector : public CUtlVector< T, CUtlBlockMemory< T, int > > +{ +public: + CUtlBlockVector( int growSize = 0, int initSize = 0 ) + : CUtlVector< T, CUtlBlockMemory< T, int > >( growSize, initSize ) {} +}; + +//----------------------------------------------------------------------------- +// The CUtlVectorMT class: +// A array class with some sort of mutex protection. Not sure which operations are protected from +// which others. +//----------------------------------------------------------------------------- + +template< class BASE_UTLVECTOR, class MUTEX_TYPE = CThreadFastMutex > +class CUtlVectorMT : public BASE_UTLVECTOR, public MUTEX_TYPE +{ + typedef BASE_UTLVECTOR BaseClass; +public: + MUTEX_TYPE Mutex_t; + + // constructor, destructor + CUtlVectorMT( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + CUtlVectorMT( typename BaseClass::ElemType_t* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorFixed class: +// A array class with a fixed allocation scheme +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CUtlVectorFixed : public CUtlVector< T, CUtlMemoryFixed > +{ + typedef CUtlVector< T, CUtlMemoryFixed > BaseClass; +public: + + // constructor, destructor + CUtlVectorFixed( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + CUtlVectorFixed( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorFixedGrowable class: +// A array class with a fixed allocation scheme backed by a dynamic one +//----------------------------------------------------------------------------- +template< class T, size_t MAX_SIZE > +class CUtlVectorFixedGrowable : public CUtlVector< T, CUtlMemoryFixedGrowable > +{ + typedef CUtlVector< T, CUtlMemoryFixedGrowable > BaseClass; + +public: + // constructor, destructor + CUtlVectorFixedGrowable( int growSize = 0 ) : BaseClass( growSize, MAX_SIZE ) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorConservative class: +// A array class with a conservative allocation scheme +//----------------------------------------------------------------------------- +template< class T > +class CUtlVectorConservative : public CUtlVector< T, CUtlMemoryConservative > +{ + typedef CUtlVector< T, CUtlMemoryConservative > BaseClass; +public: + + // constructor, destructor + CUtlVectorConservative( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + CUtlVectorConservative( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} +}; + + +//----------------------------------------------------------------------------- +// The CUtlVectorUltra Conservative class: +// A array class with a very conservative allocation scheme, with customizable allocator +// Especialy useful if you have a lot of vectors that are sparse, or if you're +// carefully packing holders of vectors +//----------------------------------------------------------------------------- +#pragma warning(push) +#pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4815 ) // warning C4815: 'staticData' : zero-sized array in stack object will have no elements + +class CUtlVectorUltraConservativeAllocator +{ +public: + static void *Alloc( size_t nSize ) + { + return malloc( nSize ); + } + + static void *Realloc( void *pMem, size_t nSize ) + { + return realloc( pMem, nSize ); + } + + static void Free( void *pMem ) + { + free( pMem ); + } + + static size_t GetSize( void *pMem ) + { + return mallocsize( pMem ); + } + +}; + +template +class CUtlVectorUltraConservative : private A +{ +public: + CUtlVectorUltraConservative() + { + m_pData = StaticData(); + } + + ~CUtlVectorUltraConservative() + { + RemoveAll(); + } + + int Count() const + { + return m_pData->m_Size; + } + + static int InvalidIndex() + { + return -1; + } + + inline bool IsValidIndex( int i ) const + { + return (i >= 0) && (i < Count()); + } + + T& operator[]( int i ) + { + Assert( IsValidIndex( i ) ); + return m_pData->m_Elements[i]; + } + + const T& operator[]( int i ) const + { + Assert( IsValidIndex( i ) ); + return m_pData->m_Elements[i]; + } + + T& Element( int i ) + { + Assert( IsValidIndex( i ) ); + return m_pData->m_Elements[i]; + } + + const T& Element( int i ) const + { + Assert( IsValidIndex( i ) ); + return m_pData->m_Elements[i]; + } + + void EnsureCapacity( int num ) + { + int nCurCount = Count(); + if ( num <= nCurCount ) + { + return; + } + if ( m_pData == StaticData() ) + { + m_pData = (Data_t *)A::Alloc( sizeof(int) + ( num * sizeof(T) ) ); + m_pData->m_Size = 0; + } + else + { + int nNeeded = sizeof(int) + ( num * sizeof(T) ); + int nHave = A::GetSize( m_pData ); + if ( nNeeded > nHave ) + { + m_pData = (Data_t *)A::Realloc( m_pData, nNeeded ); + } + } + } + + int AddToTail( const T& src ) + { + int iNew = Count(); + EnsureCapacity( Count() + 1 ); + m_pData->m_Elements[iNew] = src; + m_pData->m_Size++; + return iNew; + } + + void RemoveAll() + { + if ( Count() ) + { + for (int i = m_pData->m_Size; --i >= 0; ) + { + Destruct(&m_pData->m_Elements[i]); + } + } + if ( m_pData != StaticData() ) + { + A::Free( m_pData ); + m_pData = StaticData(); + + } + } + + void PurgeAndDeleteElements() + { + if ( m_pData != StaticData() ) + { + for( int i=0; i < m_pData->m_Size; i++ ) + { + delete Element(i); + } + RemoveAll(); + } + } + + void FastRemove( int elem ) + { + Assert( IsValidIndex(elem) ); + + Destruct( &Element(elem) ); + if (Count() > 0) + { + if ( elem != m_pData->m_Size -1 ) + memcpy( &Element(elem), &Element(m_pData->m_Size-1), sizeof(T) ); + --m_pData->m_Size; + } + if ( !m_pData->m_Size ) + { + A::Free( m_pData ); + m_pData = StaticData(); + } + } + + void Remove( int elem ) + { + Destruct( &Element(elem) ); + ShiftElementsLeft(elem); + --m_pData->m_Size; + if ( !m_pData->m_Size ) + { + A::Free( m_pData ); + m_pData = StaticData(); + } + } + + int Find( const T& src ) const + { + int nCount = Count(); + for ( int i = 0; i < nCount; ++i ) + { + if (Element(i) == src) + return i; + } + return -1; + } + + bool FindAndRemove( const T& src ) + { + int elem = Find( src ); + if ( elem != -1 ) + { + Remove( elem ); + return true; + } + return false; + } + + + bool FindAndFastRemove( const T& src ) + { + int elem = Find( src ); + if ( elem != -1 ) + { + FastRemove( elem ); + return true; + } + return false; + } + + struct Data_t + { + int m_Size; + T m_Elements[]; + }; + + Data_t *m_pData; +private: + void ShiftElementsLeft( int elem, int num = 1 ) + { + int Size = Count(); + Assert( IsValidIndex(elem) || ( Size == 0 ) || ( num == 0 )); + int numToMove = Size - elem - num; + if ((numToMove > 0) && (num > 0)) + { + Q_memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) ); + +#ifdef _DEBUG + Q_memset( &Element(Size-num), 0xDD, num * sizeof(T) ); +#endif + } + } + + + + static Data_t *StaticData() + { + static Data_t staticData; + Assert( staticData.m_Size == 0 ); + return &staticData; + } +}; + +#pragma warning(pop) + + +//----------------------------------------------------------------------------- +// The CCopyableUtlVector class: +// A array class that allows copy construction (so you can nest a CUtlVector inside of another one of our containers) +// WARNING - this class lets you copy construct which can be an expensive operation if you don't carefully control when it happens +// Only use this when nesting a CUtlVector() inside of another one of our container classes (i.e a CUtlMap) +//----------------------------------------------------------------------------- +template< class T > +class CCopyableUtlVector : public CUtlVector< T, CUtlMemory > +{ + typedef CUtlVector< T, CUtlMemory > BaseClass; +public: + CCopyableUtlVector( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} + CCopyableUtlVector( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} + virtual ~CCopyableUtlVector() {} + CCopyableUtlVector( CCopyableUtlVector const& vec ) { CopyArray( vec.Base(), vec.Count() ); } +}; + +// TODO (Ilya): It seems like all the functions in CUtlVector are simple enough that they should be inlined. + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline CUtlVector::CUtlVector( int growSize, int initSize ) : + m_Memory(growSize, initSize), m_Size(0) +{ + ResetDbgInfo(); +} + +template< typename T, class A > +inline CUtlVector::CUtlVector( T* pMemory, int allocationCount, int numElements ) : + m_Memory(pMemory, allocationCount), m_Size(numElements) +{ + ResetDbgInfo(); +} + +template< typename T, class A > +inline CUtlVector::~CUtlVector() +{ + Purge(); +} + +template< typename T, class A > +inline CUtlVector& CUtlVector::operator=( const CUtlVector &other ) +{ + int nCount = other.Count(); + SetSize( nCount ); + for ( int i = 0; i < nCount; i++ ) + { + (*this)[ i ] = other[ i ]; + } + return *this; +} + + +//----------------------------------------------------------------------------- +// element access +//----------------------------------------------------------------------------- +template< typename T, class A > +inline T& CUtlVector::operator[]( int i ) +{ + Assert( i < m_Size ); + return m_Memory[ i ]; +} + +template< typename T, class A > +inline const T& CUtlVector::operator[]( int i ) const +{ + Assert( i < m_Size ); + return m_Memory[ i ]; +} + +template< typename T, class A > +inline T& CUtlVector::Element( int i ) +{ + Assert( i < m_Size ); + return m_Memory[ i ]; +} + +template< typename T, class A > +inline const T& CUtlVector::Element( int i ) const +{ + Assert( i < m_Size ); + return m_Memory[ i ]; +} + +template< typename T, class A > +inline T& CUtlVector::Head() +{ + Assert( m_Size > 0 ); + return m_Memory[ 0 ]; +} + +template< typename T, class A > +inline const T& CUtlVector::Head() const +{ + Assert( m_Size > 0 ); + return m_Memory[ 0 ]; +} + +template< typename T, class A > +inline T& CUtlVector::Tail() +{ + Assert( m_Size > 0 ); + return m_Memory[ m_Size - 1 ]; +} + +template< typename T, class A > +inline const T& CUtlVector::Tail() const +{ + Assert( m_Size > 0 ); + return m_Memory[ m_Size - 1 ]; +} + + +//----------------------------------------------------------------------------- +// Count +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::Count() const +{ + return m_Size; +} + + +//----------------------------------------------------------------------------- +// Is element index valid? +//----------------------------------------------------------------------------- +template< typename T, class A > +inline bool CUtlVector::IsValidIndex( int i ) const +{ + return (i >= 0) && (i < m_Size); +} + + +//----------------------------------------------------------------------------- +// Returns in invalid index +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::InvalidIndex() +{ + return -1; +} + + +//----------------------------------------------------------------------------- +// Grows the vector +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::GrowVector( int num ) +{ + if (m_Size + num > m_Memory.NumAllocated()) + { + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.Grow( m_Size + num - m_Memory.NumAllocated() ); + } + + m_Size += num; + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Sorts the vector +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::Sort( int (__cdecl *pfnCompare)(const T *, const T *) ) +{ + typedef int (__cdecl *QSortCompareFunc_t)(const void *, const void *); + if ( Count() <= 1 ) + return; + + if ( Base() ) + { + qsort( Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare) ); + } + else + { + Assert( 0 ); + // this path is untested + // if you want to sort vectors that use a non-sequential memory allocator, + // you'll probably want to patch in a quicksort algorithm here + // I just threw in this bubble sort to have something just in case... + + for ( int i = m_Size - 1; i >= 0; --i ) + { + for ( int j = 1; j <= i; ++j ) + { + if ( pfnCompare( &Element( j - 1 ), &Element( j ) ) < 0 ) + { + V_swap( Element( j - 1 ), Element( j ) ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Makes sure we have enough memory allocated to store a requested # of elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::EnsureCapacity( int num ) +{ + MEM_ALLOC_CREDIT_CLASS(); + m_Memory.EnsureCapacity(num); + ResetDbgInfo(); +} + + +//----------------------------------------------------------------------------- +// Makes sure we have at least this many elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::EnsureCount( int num ) +{ + if (Count() < num) + { + AddMultipleToTail( num - Count() ); + } +} + + +//----------------------------------------------------------------------------- +// Shifts elements +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::ShiftElementsRight( int elem, int num ) +{ + Assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); + int numToMove = m_Size - elem - num; + if ((numToMove > 0) && (num > 0)) + Q_memmove( &Element(elem+num), &Element(elem), numToMove * sizeof(T) ); +} + +template< typename T, class A > +void CUtlVector::ShiftElementsLeft( int elem, int num ) +{ + Assert( IsValidIndex(elem) || ( m_Size == 0 ) || ( num == 0 )); + int numToMove = m_Size - elem - num; + if ((numToMove > 0) && (num > 0)) + { + Q_memmove( &Element(elem), &Element(elem+num), numToMove * sizeof(T) ); + +#ifdef _DEBUG + Q_memset( &Element(m_Size-num), 0xDD, num * sizeof(T) ); +#endif + } +} + + +//----------------------------------------------------------------------------- +// Adds an element, uses default constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddToHead() +{ + return InsertBefore(0); +} + +template< typename T, class A > +inline int CUtlVector::AddToTail() +{ + return InsertBefore( m_Size ); +} + +template< typename T, class A > +inline int CUtlVector::InsertAfter( int elem ) +{ + return InsertBefore( elem + 1 ); +} + +template< typename T, class A > +int CUtlVector::InsertBefore( int elem ) +{ + // Can insert at the end + Assert( (elem == Count()) || IsValidIndex(elem) ); + + GrowVector(); + ShiftElementsRight(elem); + Construct( &Element(elem) ); + return elem; +} + + +//----------------------------------------------------------------------------- +// Adds an element, uses copy constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddToHead( const T& src ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()) ) ); + return InsertBefore( 0, src ); +} + +template< typename T, class A > +inline int CUtlVector::AddToTail( const T& src ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()) ) ); + return InsertBefore( m_Size, src ); +} + +template< typename T, class A > +inline int CUtlVector::InsertAfter( int elem, const T& src ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()) ) ); + return InsertBefore( elem + 1, src ); +} + +template< typename T, class A > +int CUtlVector::InsertBefore( int elem, const T& src ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()) ) ); + + // Can insert at the end + Assert( (elem == Count()) || IsValidIndex(elem) ); + + GrowVector(); + ShiftElementsRight(elem); + CopyConstruct( &Element(elem), src ); + return elem; +} + + +//----------------------------------------------------------------------------- +// Adds multiple elements, uses default constructor +//----------------------------------------------------------------------------- +template< typename T, class A > +inline int CUtlVector::AddMultipleToHead( int num ) +{ + return InsertMultipleBefore( 0, num ); +} + +template< typename T, class A > +inline int CUtlVector::AddMultipleToTail( int num ) +{ + return InsertMultipleBefore( m_Size, num ); +} + +template< typename T, class A > +inline int CUtlVector::AddMultipleToTail( int num, const T *pToCopy ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || !pToCopy || (pToCopy + num <= Base()) || (pToCopy >= (Base() + Count()) ) ); + + return InsertMultipleBefore( m_Size, num, pToCopy ); +} + +template< typename T, class A > +int CUtlVector::InsertMultipleAfter( int elem, int num ) +{ + return InsertMultipleBefore( elem + 1, num ); +} + + +template< typename T, class A > +void CUtlVector::SetCount( int count ) +{ + RemoveAll(); + AddMultipleToTail( count ); +} + +template< typename T, class A > +inline void CUtlVector::SetSize( int size ) +{ + SetCount( size ); +} + +template< typename T, class A > +void CUtlVector::SetCountNonDestructively( int count ) +{ + int delta = count - m_Size; + if(delta > 0) AddMultipleToTail( delta ); + else if(delta < 0) RemoveMultipleFromTail( -delta ); +} + +template< typename T, class A > +void CUtlVector::CopyArray( const T *pArray, int size ) +{ + // Can't insert something that's in the list... reallocation may hose us + Assert( (Base() == NULL) || !pArray || (Base() >= (pArray + size)) || (pArray >= (Base() + Count()) ) ); + + SetSize( size ); + for( int i=0; i < size; i++ ) + { + (*this)[i] = pArray[i]; + } +} + +template< typename T, class A > +void CUtlVector::Swap( CUtlVector< T, A > &vec ) +{ + m_Memory.Swap( vec.m_Memory ); + V_swap( m_Size, vec.m_Size ); +#ifndef _X360 + V_swap( m_pElements, vec.m_pElements ); +#endif +} + +template< typename T, class A > +int CUtlVector::AddVectorToTail( CUtlVector const &src ) +{ + Assert( &src != this ); + + int base = Count(); + + // Make space. + int nSrcCount = src.Count(); + EnsureCapacity( base + nSrcCount ); + + // Copy the elements. + m_Size += nSrcCount; + for ( int i=0; i < nSrcCount; i++ ) + { + CopyConstruct( &Element(base+i), src[i] ); + } + return base; +} + +template< typename T, class A > +inline int CUtlVector::InsertMultipleBefore( int elem, int num ) +{ + if( num == 0 ) + return elem; + + // Can insert at the end + Assert( (elem == Count()) || IsValidIndex(elem) ); + + GrowVector(num); + ShiftElementsRight( elem, num ); + + // Invoke default constructors + for (int i = 0; i < num; ++i ) + { + Construct( &Element( elem+i ) ); + } + + return elem; +} + +template< typename T, class A > +inline int CUtlVector::InsertMultipleBefore( int elem, int num, const T *pToInsert ) +{ + if( num == 0 ) + return elem; + + // Can insert at the end + Assert( (elem == Count()) || IsValidIndex(elem) ); + + GrowVector(num); + ShiftElementsRight( elem, num ); + + // Invoke default constructors + if ( !pToInsert ) + { + for (int i = 0; i < num; ++i ) + { + Construct( &Element( elem+i ) ); + } + } + else + { + for ( int i=0; i < num; i++ ) + { + CopyConstruct( &Element( elem+i ), pToInsert[i] ); + } + } + + return elem; +} + + +//----------------------------------------------------------------------------- +// Finds an element (element needs operator== defined) +//----------------------------------------------------------------------------- +template< typename T, class A > +int CUtlVector::Find( const T& src ) const +{ + for ( int i = 0; i < Count(); ++i ) + { + if (Element(i) == src) + return i; + } + return -1; +} + +template< typename T, class A > +void CUtlVector::FillWithValue( const T& src ) +{ + for ( int i = 0; i < Count(); i++ ) + { + Element(i) = src; + } +} + +template< typename T, class A > +bool CUtlVector::HasElement( const T& src ) const +{ + return ( Find(src) >= 0 ); +} + + +//----------------------------------------------------------------------------- +// Element removal +//----------------------------------------------------------------------------- +template< typename T, class A > +void CUtlVector::FastRemove( int elem ) +{ + Assert( IsValidIndex(elem) ); + + Destruct( &Element(elem) ); + if (m_Size > 0) + { + if ( elem != m_Size -1 ) + memcpy( &Element(elem), &Element(m_Size-1), sizeof(T) ); + --m_Size; + } +} + +template< typename T, class A > +void CUtlVector::Remove( int elem ) +{ + Destruct( &Element(elem) ); + ShiftElementsLeft(elem); + --m_Size; +} + +template< typename T, class A > +bool CUtlVector::FindAndRemove( const T& src ) +{ + int elem = Find( src ); + if ( elem != -1 ) + { + Remove( elem ); + return true; + } + return false; +} + +template< typename T, class A > +bool CUtlVector::FindAndFastRemove( const T& src ) +{ + int elem = Find( src ); + if ( elem != -1 ) + { + FastRemove( elem ); + return true; + } + return false; +} + +template< typename T, class A > +void CUtlVector::RemoveMultiple( int elem, int num ) +{ + Assert( elem >= 0 ); + Assert( elem + num <= Count() ); + + for (int i = elem + num; --i >= elem; ) + Destruct(&Element(i)); + + ShiftElementsLeft(elem, num); + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveMultipleFromHead( int num ) +{ + Assert( num <= Count() ); + + for (int i = num; --i >= 0; ) + Destruct(&Element(i)); + + ShiftElementsLeft(0, num); + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveMultipleFromTail( int num ) +{ + Assert( num <= Count() ); + + for (int i = m_Size-num; i < m_Size; i++) + Destruct(&Element(i)); + + m_Size -= num; +} + +template< typename T, class A > +void CUtlVector::RemoveAll() +{ + for (int i = m_Size; --i >= 0; ) + { + Destruct(&Element(i)); + } + + m_Size = 0; +} + + +//----------------------------------------------------------------------------- +// Memory deallocation +//----------------------------------------------------------------------------- + +template< typename T, class A > +inline void CUtlVector::Purge() +{ + RemoveAll(); + m_Memory.Purge(); + ResetDbgInfo(); +} + + +template< typename T, class A > +inline void CUtlVector::PurgeAndDeleteElements() +{ + for( int i=0; i < m_Size; i++ ) + { + delete Element(i); + } + Purge(); +} + +template< typename T, class A > +inline void CUtlVector::Compact() +{ + m_Memory.Purge(m_Size); +} + +template< typename T, class A > +inline int CUtlVector::NumAllocated() const +{ + return m_Memory.NumAllocated(); +} + + +//----------------------------------------------------------------------------- +// Data and memory validation +//----------------------------------------------------------------------------- +#ifdef DBGFLAG_VALIDATE +template< typename T, class A > +void CUtlVector::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( typeid(*this).name(), this, pchName ); + + m_Memory.Validate( validator, "m_Memory" ); + + validator.Pop(); +} +#endif // DBGFLAG_VALIDATE + +// A vector class for storing pointers, so that the elements pointed to by the pointers are deleted +// on exit. +template class CUtlVectorAutoPurge : public CUtlVector< T, CUtlMemory< T, int> > +{ +public: + ~CUtlVectorAutoPurge( void ) + { + this->PurgeAndDeleteElements(); + } + +}; + +// easy string list class with dynamically allocated strings. For use with V_SplitString, etc. +// Frees the dynamic strings in destructor. +class CUtlStringList : public CUtlVectorAutoPurge< char *> +{ +public: + void CopyAndAddToTail( char const *pString ) // clone the string and add to the end + { + char *pNewStr = new char[1 + strlen( pString )]; + V_strcpy( pNewStr, pString ); + AddToTail( pNewStr ); + } + + static int __cdecl SortFunc( char * const * sz1, char * const * sz2 ) + { + return strcmp( *sz1, *sz2 ); + } + +}; + + + +// placing it here a few days before Cert to minimize disruption to the rest of codebase +class CSplitString: public CUtlVector > +{ +public: + CSplitString(const char *pString, const char *pSeparator); + CSplitString(const char *pString, const char **pSeparators, int nSeparators); + ~CSplitString(); + // + // NOTE: If you want to make Construct() public and implement Purge() here, you'll have to free m_szBuffer there + // +private: + void Construct(const char *pString, const char **pSeparators, int nSeparators); + void PurgeAndDeleteElements(); +private: + char *m_szBuffer; // a copy of original string, with '\0' instead of separators +}; + + +#endif // CCVECTOR_H diff --git a/public/tier2/beamsegdraw.h b/public/tier2/beamsegdraw.h new file mode 100644 index 0000000..0465c0b --- /dev/null +++ b/public/tier2/beamsegdraw.h @@ -0,0 +1,198 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// +#if !defined( BEAMSEGDRAW_H ) +#define BEAMSEGDRAW_H +#ifdef _WIN32 +#pragma once +#endif + +#define NOISE_DIVISIONS 128 + + +#include "mathlib/vector.h" +#include "materialsystem/imesh.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct BeamTrail_t; +class IMaterial; + + +//----------------------------------------------------------------------------- +// CBeamSegDraw is a simple interface to beam rendering. +//----------------------------------------------------------------------------- +struct BeamSeg_t +{ + VectorAligned m_vPos; + color32 m_color; + float m_flTexCoord; // Y texture coordinate + float m_flWidth; + + void SetColor( float r, float g, float b, float a ) + { + // Specify the points. + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) && IsFinite(a) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) && (a >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) && (a <= 1.0) ); + + m_color.r = FastFToC( r ); + m_color.g = FastFToC( g ); + m_color.b = FastFToC( b ); + m_color.a = FastFToC( a ); + } + + void SetColor( float r, float g, float b ) + { + // Specify the points. + Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) ); + Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); + Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); + + m_color.r = FastFToC( r ); + m_color.g = FastFToC( g ); + m_color.b = FastFToC( b ); + } + + void SetAlpha( float a ) + { + // Specify the points. + Assert( IsFinite(a) ); + Assert( (a >= 0.0) ); + Assert( (a <= 1.0) ); + + m_color.a = FastFToC( a ); + } + + void SetColor( const Vector &vecColor, float a ) + { + SetColor( vecColor.x, vecColor.y, vecColor.z, a ); + } + + void SetColor( const Vector4D &vecColor ) + { + SetColor( vecColor.x, vecColor.y, vecColor.z, vecColor.w ); + } + + void SetColor( const Vector &vecColor ) + { + SetColor( vecColor.x, vecColor.y, vecColor.z ); + } + + void GetColor( Vector4D *pColor ) + { + pColor->x = m_color.r / 255.0f; + pColor->y = m_color.g / 255.0f; + pColor->z = m_color.b / 255.0f; + pColor->w = m_color.a / 255.0f; + } + + void GetColor( Vector *pColor ) + { + pColor->x = m_color.r / 255.0f; + pColor->y = m_color.g / 255.0f; + pColor->z = m_color.b / 255.0f; + } +}; + +struct BeamSegRenderInfo_t +{ + Vector m_vecPoint1; + Vector m_vecPoint2; + Vector m_vecCenter; + Vector m_vecTangentS; + Vector m_vecTangentT; + float m_flTexCoord; + color32 m_color; +}; + +class CBeamSegDraw +{ +public: + CBeamSegDraw() : m_pRenderContext( NULL ) {} + // Pass null for pMaterial if you have already set the material you want. + void Start( IMatRenderContext *pRenderContext, int nSegs, IMaterial *pMaterial=0, CMeshBuilder *pMeshBuilder = NULL, int nMeshVertCount = 0 ); + + void ComputeRenderInfo( BeamSegRenderInfo_t *pRenderInfo, const Vector &vecCameraPos, int nSegCount, const BeamSeg_t *pSegs ); + virtual void NextSeg( BeamSeg_t *pSeg ); + void End(); + +protected: + void SpecifySeg( const Vector &vecCameraPos, const Vector &vNextPos ); + void ComputeNormal( const Vector &vecCameraPos, const Vector &vStartPos, const Vector &vNextPos, Vector *pNormal ); + static void LoadSIMDData( FourVectors *pV4StartPos, FourVectors *pV4EndPos, FourVectors *pV4HalfWidth, int nSegCount, const BeamSeg_t *pSegs ); + CMeshBuilder *m_pMeshBuilder; + int m_nMeshVertCount; + + CMeshBuilder m_Mesh; + BeamSeg_t m_Seg; + + int m_nTotalSegs; + int m_nSegsDrawn; + + Vector m_vNormalLast; + IMatRenderContext *m_pRenderContext; + + Vector m_vecCameraPos; +}; + +class CBeamSegDrawArbitrary : public CBeamSegDraw +{ +public: + void SetNormal( const Vector &normal ); + void NextSeg( BeamSeg_t *pSeg ); + +protected: + void SpecifySeg( const Vector &vNextPos ); + + BeamSeg_t m_PrevSeg; +}; + +#if 0 +int ScreenTransform( const Vector& point, Vector& screen ); + +void DrawSegs( int noise_divisions, float *prgNoise, const model_t* spritemodel, + float frame, int rendermode, const Vector& source, const Vector& delta, + float startWidth, float endWidth, float scale, float freq, float speed, int segments, + int flags, float* color, float fadeLength, float flHDRColorScale = 1.0f ); +void DrawTeslaSegs( int noise_divisions, float *prgNoise, const model_t* spritemodel, + float frame, int rendermode, const Vector& source, const Vector& delta, + float startWidth, float endWidth, float scale, float freq, float speed, int segments, + int flags, float* color, float fadeLength, float flHDRColorScale = 1.0f ); +void DrawSplineSegs( int noise_divisions, float *prgNoise, + const model_t* beammodel, const model_t* halomodel, float flHaloScale, + float frame, int rendermode, int numAttachments, Vector* attachment, + float startWidth, float endWidth, float scale, float freq, float speed, int segments, + int flags, float* color, float fadeLength, float flHDRColorScale = 1.0f ); +void DrawHalo(IMaterial* pMaterial, const Vector& source, float scale, float const* color, float flHDRColorScale = 1.0f ); +void BeamDrawHalo( const model_t* spritemodel, float frame, int rendermode, const Vector& source, + float scale, float* color, float flHDRColorScale = 1.0f ); +void DrawDisk( int noise_divisions, float *prgNoise, const model_t* spritemodel, + float frame, int rendermode, const Vector& source, const Vector& delta, + float width, float scale, float freq, float speed, + int segments, float* color, float flHDRColorScale = 1.0f ); +void DrawCylinder( int noise_divisions, float *prgNoise, const model_t* spritemodel, + float frame, int rendermode, const Vector& source, + const Vector& delta, float width, float scale, float freq, + float speed, int segments, float* color, float flHDRColorScale = 1.0f ); +void DrawRing( int noise_divisions, float *prgNoise, void (*pfnNoise)( float *noise, int divs, float scale ), + const model_t* spritemodel, float frame, int rendermode, + const Vector& source, const Vector& delta, float width, float amplitude, + float freq, float speed, int segments, float* color, float flHDRColorScale = 1.0f ); +void DrawBeamFollow( const model_t* spritemodel, BeamTrail_t* pHead, int frame, int rendermode, Vector& delta, + Vector& screen, Vector& screenLast, float die, const Vector& source, + int flags, float width, float amplitude, float freq, float* color, float flHDRColorScale = 1.0f ); + +void DrawBeamQuadratic( const Vector &start, const Vector &control, const Vector &end, float width, const Vector &color, float scrollOffset, float flHDRColorScale = 1.0f ); +#endif + +//----------------------------------------------------------------------------- +// Assumes the material has already been bound +//----------------------------------------------------------------------------- +void DrawSprite( const Vector &vecOrigin, float flWidth, float flHeight, color32 color ); + +#endif // BEAMDRAW_H diff --git a/public/tier2/fileutils.h b/public/tier2/fileutils.h new file mode 100644 index 0000000..906550c --- /dev/null +++ b/public/tier2/fileutils.h @@ -0,0 +1,312 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef FILEUTILS_H +#define FILEUTILS_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifndef TIER2_H +#include "tier2/tier2.h" +#endif + +#ifndef FILESYSTEM_H +#include "filesystem.h" +#endif + +#include "tier0/platform.h" + +// Builds a directory which is a subdirectory of the current mod +void GetModSubdirectory( const char *pSubDir, char *pBuf, int nBufLen ); + +// Builds a directory which is a subdirectory of the current mod's *content* +void GetModContentSubdirectory( const char *pSubDir, char *pBuf, int nBufLen ); + +// Generates a filename under the 'game' subdirectory given a subdirectory of 'content' +void ComputeModFilename( const char *pContentFileName, char *pBuf, size_t nBufLen ); + +// Generates a filename under the 'content' subdirectory given a subdirectory of 'game' +void ComputeModContentFilename( const char *pGameFileName, char *pBuf, size_t nBufLen ); + +// Builds a list of all files under a directory with a particular extension +void AddFilesToList( CUtlVector< CUtlString > &list, const char *pDirectory, const char *pPath, const char *pExtension ); + +// Returns the search path as a list of paths +void GetSearchPath( CUtlVector< CUtlString > &path, const char *pPathID ); + +// Given file name generate a full path using the following rules. +// 1. if its full path already return +// 2. if its a relative path try to find it under the path id +// 3. if find fails treat relative path as relative to the current dir +bool GenerateFullPath( const char *pFileName, char const *pPathID, char *pBuf, int nBufLen ); + + +// Generates a .360 file if it doesn't exist or is out of sync with the pc source file +#define UOC_FAIL -1 +#define UOC_NOT_CREATED 0 +#define UOC_CREATED 1 +typedef bool ( *CreateCallback_t )( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData ); +int UpdateOrCreate( const char *pSourceName, char *pTargetName, int targetLen, const char *pPathID, CreateCallback_t pfnCreate, bool bForce = false, void *pExtraData = NULL ); + +char *CreateX360Filename( const char *pSourceName, char *pTargetName, int targetLen ); + +FORCEINLINE const char *AdjustFileExtensionForPlatform( const char *pSourceName, char *pTargetName, int targetLen ) +{ +#ifdef PLATFORM_X360 + return CreateX360Filename( pSourceName, pTargetName, targetLen ); +#else + return pSourceName; +#endif +} + +// simple file classes. File I/O mode (text/binary, read/write) is based upon the subclass chosen. +// classes with the word Required on them abort with a message if the file can't be opened. +// destructores close the file handle, or it can be explicitly closed with the Close() method. + +class CBaseFile +{ +public: + FileHandle_t m_FileHandle; + + CBaseFile(void) + { + m_FileHandle = FILESYSTEM_INVALID_HANDLE; + } + + ~CBaseFile( void ) + { + Close(); + } + + FileHandle_t Handle( void ) const + { + return m_FileHandle; + } + + void Close( void ) + { + if ( m_FileHandle != FILESYSTEM_INVALID_HANDLE ) + g_pFullFileSystem->Close( m_FileHandle ); + m_FileHandle = FILESYSTEM_INVALID_HANDLE; + } + + void Open( char const *fname, char const *modes ) + { + Close(); + m_FileHandle = g_pFullFileSystem->Open( fname, modes ); + } + + char *ReadLine( char *pOutput, int maxChars ) + { + return g_pFullFileSystem->ReadLine( pOutput, maxChars, m_FileHandle ); + } + + // read every line of the file into a vector of strings + void ReadLines( CUtlStringList &sList, int nMaxLineLength = 2048 ); + + int Read( void* pOutput, int size ) + { + return g_pFullFileSystem->Read( pOutput, size, m_FileHandle ); + } + + void MustRead( void* pOutput, int size ) + { + int ret=Read( pOutput, size ); + if (ret != size ) + Error("failed to read %d bytes\n", size ); + } + + int Write( void const* pInput, int size) + { + return g_pFullFileSystem->Write( pInput, size, m_FileHandle ); + } + + + // {Get|Put}{Int|Float} read and write ints and floats from a file in x86 order, swapping on + // input for big-endian systems. + void PutInt( int n ) + { + int n1=LittleDWord( n ); + Write(&n1, sizeof( n1 ) ); + } + + int GetInt( void ) + { + int ret; + MustRead( &ret, sizeof( ret )); + return LittleDWord( ret ); + } + + float GetFloat( void ) + { + float ret; + MustRead( &ret, sizeof( ret )); + LittleFloat( &ret, &ret ); + return ret; + } + void PutFloat( float f ) + { + LittleFloat( &f, &f ); + Write( &f, sizeof( f ) ); + } + + bool IsOk( void ) + { + return ( m_FileHandle != FILESYSTEM_INVALID_HANDLE) && + ( g_pFullFileSystem->IsOk( m_FileHandle ) ); + } + + void Seek( int pos, FileSystemSeek_t nSeekType = FILESYSTEM_SEEK_HEAD ) + { + g_pFullFileSystem->Seek( m_FileHandle, pos, nSeekType ); + } + + unsigned int Size( void ) + { + Assert( IsOk() ); + return g_pFullFileSystem->Size( m_FileHandle ); + } + + void ReadFile( CUtlBuffer &dataBuf ); +}; + +class COutputFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "wb" ); + } + + COutputFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + + COutputFile( void ) : CBaseFile() + { + } +}; + +class COutputTextFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "w" ); + } + + COutputTextFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + + COutputTextFile( void ) : CBaseFile() + { + } +}; + +class CAppendTextFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "a+" ); + } + + CAppendTextFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + + CAppendTextFile( void ) : CBaseFile() + { + } +}; + +class CInputFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "rb" ); + } + + CInputFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + CInputFile( void ) : CBaseFile() + { + } +}; + +class CInputTextFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "r" ); + } + + CInputTextFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + CInputTextFile( void ) : CBaseFile() + { + } + + +}; + +class CRequiredInputTextFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "r" ); + if ( ! IsOk() ) + { + Error("error opening required file %s\n", pFname ); + } + } + + CRequiredInputTextFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + CRequiredInputTextFile( void ) : CBaseFile() + { + } +}; + +class CRequiredInputFile : public CBaseFile +{ +public: + void Open( char const *pFname ) + { + CBaseFile::Open( pFname, "rb" ); + if ( ! IsOk() ) + { + Error("error opening required file %s\n", pFname ); + } + } + + CRequiredInputFile( char const *pFname ) : CBaseFile() + { + Open( pFname ); + } + CRequiredInputFile( void ) : CBaseFile() + { + } +}; + +#endif // FILEUTILS_H + diff --git a/public/tier2/interval.h b/public/tier2/interval.h new file mode 100644 index 0000000..b7155c2 --- /dev/null +++ b/public/tier2/interval.h @@ -0,0 +1,20 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef INTERVAL_H +#define INTERVAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" + + +interval_t ReadInterval( const char *pString ); +float RandomInterval( const interval_t &interval ); + +#endif // INTERVAL_H diff --git a/public/tier2/keybindings.h b/public/tier2/keybindings.h new file mode 100644 index 0000000..e74b740 --- /dev/null +++ b/public/tier2/keybindings.h @@ -0,0 +1,42 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef KEYBINDINGS_H +#define KEYBINDINGS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlstring.h" +#include "inputsystem/ButtonCode.h" + +class CUtlBuffer; + + +class CKeyBindings +{ +public: + void SetBinding( ButtonCode_t code, const char *pBinding ); + void SetBinding( const char *pButtonName, const char *pBinding ); + + void Unbind( ButtonCode_t code ); + void Unbind( const char *pButtonName ); + void UnbindAll(); + + int GetBindingCount() const; + void WriteBindings( CUtlBuffer &buf ); + const char *ButtonNameForBinding( const char *pBinding ); + const char *GetBindingForButton( ButtonCode_t code ); + +private: + CUtlString m_KeyInfo[ BUTTON_CODE_LAST ]; +}; + + +#endif // KEYBINDINGS_H diff --git a/public/tier2/meshutils.h b/public/tier2/meshutils.h new file mode 100644 index 0000000..8907c6d --- /dev/null +++ b/public/tier2/meshutils.h @@ -0,0 +1,26 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A set of utilities to help with generating meshes +// +//===========================================================================// + +#ifndef MESHUTILS_H +#define MESHUTILS_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Helper methods to create various standard index buffer types +//----------------------------------------------------------------------------- +void GenerateSequentialIndexBuffer( unsigned short* pIndexMemory, int nIndexCount, int nFirstVertex ); +void GenerateQuadIndexBuffer( unsigned short* pIndexMemory, int nIndexCount, int nFirstVertex ); +void GeneratePolygonIndexBuffer( unsigned short* pIndexMemory, int nIndexCount, int nFirstVertex ); +void GenerateLineStripIndexBuffer( unsigned short* pIndexMemory, int nIndexCount, int nFirstVertex ); +void GenerateLineLoopIndexBuffer( unsigned short* pIndexMemory, int nIndexCount, int nFirstVertex ); + + +#endif // MESHUTILS_H + diff --git a/public/tier2/p4helpers.h b/public/tier2/p4helpers.h new file mode 100644 index 0000000..c8fdad3 --- /dev/null +++ b/public/tier2/p4helpers.h @@ -0,0 +1,138 @@ +//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef P4HELPERS_H +#define P4HELPERS_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utlstring.h" +#include "tier1/smartptr.h" + + +// +// Class representing file operations +// +class CP4File +{ +public: + explicit CP4File( char const *szFilename ); + virtual ~CP4File(); + +public: + // Opens the file for edit + virtual bool Edit( void ); + + // Opens the file for add + virtual bool Add( void ); + + // Is the file in perforce? + virtual bool IsFileInPerforce(); + +protected: + // The filename that this class instance represents + CUtlString m_sFilename; +}; + +// +// An override of CP4File performing no Perforce interaction +// +class CP4File_Dummy : public CP4File +{ +public: + explicit CP4File_Dummy( char const *szFilename ) : CP4File( szFilename ) {} + +public: + virtual bool Edit( void ) { return true; } + virtual bool Add( void ) { return true; } + virtual bool IsFileInPerforce() { return false; } +}; + + +// +// Class representing a factory for creating other helper objects +// +class CP4Factory +{ +public: + CP4Factory(); + ~CP4Factory(); + +public: + // Sets whether dummy objects are created by the factory. + // Returns the old state of the dummy mode. + bool SetDummyMode( bool bDummyMode ); + +public: + // Sets the name of the changelist to open files under, + // NULL for "Default" changelist. + void SetOpenFileChangeList( const char *szChangeListName ); + +public: + // Creates a file access object for the given filename. + CP4File *AccessFile( char const *szFilename ) const; + +protected: + // Whether the factory is in the "dummy mode" and is creating dummy objects + bool m_bDummyMode; +}; + +// Default p4 factory +extern CP4Factory *g_p4factory; + + +// +// CP4AutoEditFile - edits the file upon construction +// +class CP4AutoEditFile +{ +public: + explicit CP4AutoEditFile( char const *szFilename ) : m_spImpl( g_p4factory->AccessFile( szFilename ) ) { m_spImpl->Edit(); } + + CP4File * File() const { return m_spImpl.Get(); } + +protected: + CPlainAutoPtr< CP4File > m_spImpl; +}; + +// +// CP4AutoAddFile - adds the file upon construction +// +class CP4AutoAddFile +{ +public: + explicit CP4AutoAddFile( char const *szFilename ) : m_spImpl( g_p4factory->AccessFile( szFilename ) ) { m_spImpl->Add(); } + + CP4File * File() const { return m_spImpl.Get(); } + +protected: + CPlainAutoPtr< CP4File > m_spImpl; +}; + +// +// CP4AutoEditAddFile - edits the file upon construction / adds upon destruction +// +class CP4AutoEditAddFile +{ +public: + explicit CP4AutoEditAddFile( char const *szFilename ) : m_spImpl( g_p4factory->AccessFile( szFilename ) ) + { + m_spImpl->Edit(); + } + ~CP4AutoEditAddFile( void ) { m_spImpl->Add(); } + + CP4File * File() const { return m_spImpl.Get(); } + +protected: + CPlainAutoPtr< CP4File > m_spImpl; +}; + + +#endif // #ifndef P4HELPERS_H diff --git a/public/tier2/renderutils.h b/public/tier2/renderutils.h new file mode 100644 index 0000000..5772c6f --- /dev/null +++ b/public/tier2/renderutils.h @@ -0,0 +1,72 @@ +//===== Copyright � 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A set of utilities to render standard shapes +// +//===========================================================================// + +#ifndef RENDERUTILS_H +#define RENDERUTILS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier2/tier2.h" +#include "Color.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class Vector; +class QAngle; +class IMaterial; +struct matrix3x4_t; + + +// Renders a wireframe sphere +void RenderWireframeSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer ); + +// Renders a sphere +void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer, bool bInsideOut = false ); +void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, IMaterial *pMaterial, bool bInsideOut = false ); + +// Renders a wireframe box relative to an origin +void RenderWireframeBox( const Vector &vOrigin, const QAngle& angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer ); + +// Renders a swept wireframe box +void RenderWireframeSweptBox( const Vector &vStart, const Vector &vEnd, const QAngle &angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer ); + +// Renders a solid box +void RenderBox( const Vector& origin, const QAngle& angles, const Vector& mins, const Vector& maxs, Color c, bool bZBuffer, bool bInsideOut = false ); +void RenderBox( const Vector& origin, const QAngle& angles, const Vector& mins, const Vector& maxs, Color c, IMaterial *pMaterial, bool bInsideOut = false ); + +// Renders axes, red->x, green->y, blue->z (axis aligned) +void RenderAxes( const Vector &vOrigin, float flScale, bool bZBuffer ); +void RenderAxes( const matrix3x4_t &transform, float flScale, bool bZBuffer ); + +// Render a line +void RenderLine( const Vector& v1, const Vector& v2, Color c, bool bZBuffer ); + +// Draws a triangle +void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, bool bZBuffer ); +void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, IMaterial *pMaterial ); + +// Draws a axis-aligned quad +void RenderQuad( IMaterial *pMaterial, float x, float y, float w, float h, float z, float s0, float t0, float s1, float t1, const Color& clr ); + +// Renders a screen space quad +void DrawScreenSpaceRectangle( IMaterial *pMaterial, + int nDestX, int nDestY, int nWidth, int nHeight, // Rect to draw into in screen space + float flSrcTextureX0, float flSrcTextureY0, // which texel you want to appear at destx/y + float flSrcTextureX1, float flSrcTextureY1, // which texel you want to appear at destx+width-1, desty+height-1 + int nSrcTextureWidth, int nSrcTextureHeight, // needed for fixup + void *pClientRenderable = NULL, // Used to pass to the bind proxies + int nXDice = 1, + int nYDice = 1, + float fDepth = 0.0 ); // what Z value to put in the verts + +// Renders a single polygon without texturing in normalized-device coordinate space (x, y in the range [-1,1], z = 0) +void DrawNDCSpaceUntexturedPolygon( IMaterial *pMaterial, int nVertexCount, Vector2D *pScreenSpaceCoordinates, void *pClientRenderable ); + +#endif // RENDERUTILS_H + diff --git a/public/tier2/resourceprecacher.h b/public/tier2/resourceprecacher.h new file mode 100644 index 0000000..3580773 --- /dev/null +++ b/public/tier2/resourceprecacher.h @@ -0,0 +1,195 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Utilities for setting vproject settings +// +//===========================================================================// + +#ifndef _RESOURCEPRECACHER_H +#define _RESOURCEPRECACHER_H + +#ifdef _WIN32 +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Resource list +//----------------------------------------------------------------------------- +FORWARD_DECLARE_HANDLE( ResourceList_t ); +#define RESOURCE_LIST_INVALID ( (ResourceList_t)-1 ) + +//----------------------------------------------------------------------------- +// Resource 'systems', which use other resources +// NOTE: If you add types here, be sure to fix s_pResourceSystemName +//----------------------------------------------------------------------------- +enum PrecacheSystem_t +{ + CLIENTGLOBAL = 0, // Always precache these + SERVERGLOBAL, + VGUI_PANEL, // What to precache when using a vgui panel + DISPATCH_EFFECT, // What to precache when using a dispatch effect + SHARED_SYSTEM, // Precache lists which are reused and can be referenced as a resource type + + PRECACHE_SYSTEM_COUNT, + +#if defined( GAME_DLL ) + GLOBAL = SERVERGLOBAL, +#elif defined( CLIENT_DLL ) || defined( GAMEUI_EXPORTS ) + GLOBAL = CLIENTGLOBAL, +#endif +}; + +//----------------------------------------------------------------------------- +// Resource types +// NOTE: If you add a type here, modify s_pResourceTypeName in resourceaccesscontrol.cpp +//----------------------------------------------------------------------------- +enum ResourceTypeOld_t // called 'Old' to disambiguate with ResourceSystem +{ + RESOURCE_VGUI_PANEL = 0, // .res file + RESOURCE_MATERIAL, // .vmt file + RESOURCE_MODEL, // .mdl file + RESOURCE_PARTICLE_SYSTEM, // particle system + RESOURCE_GAMESOUND, // game sound + + RESOURCE_TYPE_OLD_COUNT, +}; + + +//----------------------------------------------------------------------------- +// Resource types +// NOTE: If you add types here, be sure to fix s_pPrecacheResourceTypeName +// A compile-time assert will trigger if you don't. +//----------------------------------------------------------------------------- +enum PrecacheResourceType_t +{ + VGUI_RESOURCE = 0, // .res file + MATERIAL, // .vmt file + MODEL, // .mdl file + GAMESOUND, // sound + PARTICLE_SYSTEM, // particle system + ENTITY, // Other entity + DECAL, // A decal + PARTICLE_MATERIAL, // A particle system material (old-style, obsolete) + KV_DEP_FILE, // keyvalues file containing a resource dependency list + GAME_MATERIAL_DECALS, // All decals related to game materials ( resource name is ignored ) + PHYSICS_GAMESOUNDS, // Resource names are either "BulletSounds", "StepSounds", or "PhysicsImpactSounds" + SHARED, // a shared precache group (see PrecacheSystem_t SHARED) + + PRECACHE_RESOURCE_TYPE_COUNT, +}; + +//----------------------------------------------------------------------------- +// Callback interface for handler who knows how to precache particular kinds of resources +//----------------------------------------------------------------------------- +abstract_class IPrecacheHandler +{ +public: + virtual void CacheResource( PrecacheResourceType_t nType, const char *pName, + bool bPrecache, ResourceList_t hResourceList, int *pIndex = NULL ) = 0; +}; + +//----------------------------------------------------------------------------- +// Interface to automated system for precaching resources +//----------------------------------------------------------------------------- +abstract_class IResourcePrecacher +{ +public: + virtual void Cache( IPrecacheHandler *pPrecacheHandler, bool bPrecache, ResourceList_t hResourceList, bool bIgnoreConditionals ) = 0; + virtual PrecacheSystem_t GetSystem() = 0; + virtual const char *GetName() = 0; + virtual IResourcePrecacher *GetNext() = 0; + virtual void SetNext( IResourcePrecacher * pNext ) = 0; +}; + +//----------------------------------------------------------------------------- +// Actually does the precaching +//----------------------------------------------------------------------------- +class CBaseResourcePrecacher : public IResourcePrecacher +{ + // Other public methods +public: + CBaseResourcePrecacher( PrecacheSystem_t nSystem, const char *pName ) + { + m_nSystem = nSystem; + m_pName = pName; + m_pNext = sm_pFirst[nSystem]; + sm_pFirst[nSystem] = this; + } + + static void RegisterAll(); + + PrecacheSystem_t GetSystem() { return m_nSystem; } + const char *GetName() { return m_pName; } + IResourcePrecacher *GetNext() { return m_pNext; } + void SetNext( IResourcePrecacher * pNext ) { m_pNext = pNext; } + + static CBaseResourcePrecacher *sm_pFirst[PRECACHE_SYSTEM_COUNT]; + + PrecacheSystem_t m_nSystem; + const char *m_pName; + IResourcePrecacher *m_pNext; + + friend class CPrecacheRegister; +}; + + +//----------------------------------------------------------------------------- +// Automatic precache macros +//----------------------------------------------------------------------------- + +// Beginning +#define PRECACHE_REGISTER_BEGIN_CONDITIONAL( _system, _className, _condition ) \ + namespace _className ## Precache \ +{ \ +class CResourcePrecacher : public CBaseResourcePrecacher\ +{ \ +public: \ + CResourcePrecacher() : CBaseResourcePrecacher( _system, #_className ) {} \ +public: \ + virtual void Cache( IPrecacheHandler *pPrecacheHandler, bool bPrecache, ResourceList_t hResourceList, bool bIgnoreConditionals ); \ +}; \ + void CResourcePrecacher::Cache( IPrecacheHandler *pPrecacheHandler, bool bPrecache, ResourceList_t hResourceList, bool bIgnoreConditionals ) \ +{ \ + if ( !bIgnoreConditionals && !( _condition ) ) \ + return; + +#define PRECACHE_REGISTER_BEGIN( _system, _className ) \ + PRECACHE_REGISTER_BEGIN_CONDITIONAL( _system, _className, true ) + +// Resource precache definitions +// NOTE: PRECACHE_INDEX_CONDITIONAL doesn't initialize the index to 0 +// on the assumption that some other conditional will +#ifdef _WIN64 +#error "PRECACHE_INDEX and PRECACHE_INDEX_CONDITIONAL won't work in 64 bit because the old-school particle mgr is sending ptr data types into here. Hopefully the old-school particle mgr will die before this is an issue." +#endif + +#define PRECACHE( _type, _name ) pPrecacheHandler->CacheResource( _type, _name, bPrecache, hResourceList, NULL ); +#define PRECACHE_INDEX( _type, _name, _index ) pPrecacheHandler->CacheResource( _type, _name, bPrecache, hResourceList, (int*)( &(_index) ) ); +#define PRECACHE_CONDITIONAL( _type, _name, _condition ) \ + if ( !bIgnoreConditionals && ( _condition ) ) \ + pPrecacheHandler->CacheResource( _type, _name, bPrecache, hResourceList, NULL ); +#define PRECACHE_INDEX_CONDITIONAL( _type, _name, _index, _func ) \ + if ( bIgnoreConditionals || ( _condition ) ) \ +{ \ + pPrecacheHandler->CacheResource( _type, _name, bPrecache, hResourceList, (int*)( &(_index) ) ); \ +} + +//End +#define PRECACHE_REGISTER_END( ) \ +} \ + CResourcePrecacher s_ResourcePrecacher; \ +} + +// FIXME: Remove! Backward compat +#define PRECACHE_WEAPON_REGISTER( _className ) \ + PRECACHE_REGISTER_BEGIN( GLOBAL, _className ) \ + PRECACHE( ENTITY, #_className ) \ + PRECACHE_REGISTER_END() + +#define PRECACHE_REGISTER( _className ) \ + PRECACHE_REGISTER_BEGIN( GLOBAL, _className ) \ + PRECACHE( ENTITY, #_className ) \ + PRECACHE_REGISTER_END() + +#endif // _RESOURCEPRECACHER_H + + diff --git a/public/tier2/riff.h b/public/tier2/riff.h new file mode 100644 index 0000000..8729540 --- /dev/null +++ b/public/tier2/riff.h @@ -0,0 +1,202 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RIFF_H +#define RIFF_H +#pragma once + +#include "commonmacros.h" + + +//----------------------------------------------------------------------------- +// Purpose: This is a simple abstraction that the RIFF classes use to read from +// files/memory +//----------------------------------------------------------------------------- +class IFileReadBinary +{ +public: + virtual int open( const char *pFileName ) = 0; + virtual int read( void *pOutput, int size, int file ) = 0; + virtual void close( int file ) = 0; + virtual void seek( int file, int pos ) = 0; + virtual unsigned int tell( int file ) = 0; + virtual unsigned int size( int file ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Used to read/parse a RIFF format file +//----------------------------------------------------------------------------- +class InFileRIFF +{ +public: + InFileRIFF( const char *pFileName, IFileReadBinary &io ); + ~InFileRIFF( void ); + + unsigned int RIFFName( void ) { return m_riffName; } + unsigned int RIFFSize( void ) { return m_riffSize; } + + int ReadInt( void ); + int ReadData( void *pOutput, int dataSize ); + int PositionGet( void ); + void PositionSet( int position ); + bool IsValid( void ) { return m_file != 0; } + +private: + const InFileRIFF & operator=( const InFileRIFF & ); + + IFileReadBinary &m_io; + int m_file; + unsigned int m_riffName; + unsigned int m_riffSize; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Used to iterate over an InFileRIFF +//----------------------------------------------------------------------------- +class IterateRIFF +{ +public: + IterateRIFF( InFileRIFF &riff, int size ); + IterateRIFF( IterateRIFF &parent ); + + bool ChunkAvailable( void ); + bool ChunkNext( void ); + + unsigned int ChunkName( void ); + unsigned int ChunkSize( void ); + int ChunkRead( void *pOutput ); + int ChunkReadPartial( void *pOutput, int dataSize ); + int ChunkReadInt( void ); + int ChunkFilePosition( void ) { return m_chunkPosition; } + +private: + const IterateRIFF & operator=( const IterateRIFF & ); + + void ChunkSetup( void ); + void ChunkClear( void ); + + InFileRIFF &m_riff; + int m_start; + int m_size; + + unsigned int m_chunkName; + int m_chunkSize; + int m_chunkPosition; +}; + +class IFileWriteBinary +{ +public: + virtual int create( const char *pFileName ) = 0; + virtual int write( void *pData, int size, int file ) = 0; + virtual void close( int file ) = 0; + virtual void seek( int file, int pos ) = 0; + virtual unsigned int tell( int file ) = 0; +}; +//----------------------------------------------------------------------------- +// Purpose: Used to write a RIFF format file +//----------------------------------------------------------------------------- +class OutFileRIFF +{ +public: + OutFileRIFF( const char *pFileName, IFileWriteBinary &io ); + ~OutFileRIFF( void ); + + bool WriteInt( int number ); + bool WriteData( void *pOutput, int dataSize ); + int PositionGet( void ); + void PositionSet( int position ); + bool IsValid( void ) { return m_file != 0; } + + void HasLISETData( int position ); + +private: + const OutFileRIFF & operator=( const OutFileRIFF & ); + + IFileWriteBinary &m_io; + int m_file; + unsigned int m_riffName; + unsigned int m_riffSize; + unsigned int m_nNamePos; + + // hack to make liset work correctly + bool m_bUseIncorrectLISETLength; + int m_nLISETSize; + + +}; + +//----------------------------------------------------------------------------- +// Purpose: Used to iterate over an InFileRIFF +//----------------------------------------------------------------------------- +class IterateOutputRIFF +{ +public: + IterateOutputRIFF( OutFileRIFF &riff ); + IterateOutputRIFF( IterateOutputRIFF &parent ); + + void ChunkStart( unsigned int chunkname ); + void ChunkFinish( void ); + + void ChunkWrite( unsigned int chunkname, void *pOutput, int size ); + void ChunkWriteInt( int number ); + void ChunkWriteData( void *pOutput, int size ); + + int ChunkFilePosition( void ) { return m_chunkPosition; } + + unsigned int ChunkGetPosition( void ); + void ChunkSetPosition( int position ); + + void CopyChunkData( IterateRIFF& input ); + + void SetLISETData( int position ); + +private: + + const IterateOutputRIFF & operator=( const IterateOutputRIFF & ); + + OutFileRIFF &m_riff; + int m_start; + int m_size; + + unsigned int m_chunkName; + int m_chunkSize; + int m_chunkPosition; + int m_chunkStart; +}; + +#define RIFF_ID MAKEID('R','I','F','F') +#define RIFF_WAVE MAKEID('W','A','V','E') +#define WAVE_FMT MAKEID('f','m','t',' ') +#define WAVE_DATA MAKEID('d','a','t','a') +#define WAVE_FACT MAKEID('f','a','c','t') +#define WAVE_CUE MAKEID('c','u','e',' ') +#define WAVE_SAMPLER MAKEID('s','m','p','l') +#define WAVE_VALVEDATA MAKEID('V','D','A','T') +#define WAVE_PADD MAKEID('P','A','D','D') +#define WAVE_LIST MAKEID('L','I','S','T') + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 0x0001 +#endif +#ifndef WAVE_FORMAT_ADPCM +#define WAVE_FORMAT_ADPCM 0x0002 +#endif +#define WAVE_FORMAT_XBOX_ADPCM 0x0069 +#ifndef WAVE_FORMAT_XMA +#define WAVE_FORMAT_XMA 0x0165 +#endif + +#endif // RIFF_H diff --git a/public/tier2/soundutils.h b/public/tier2/soundutils.h new file mode 100644 index 0000000..c44fbbf --- /dev/null +++ b/public/tier2/soundutils.h @@ -0,0 +1,31 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Helper methods + classes for sound +// +//===========================================================================// + +#ifndef SOUNDUTILS_H +#define SOUNDUTILS_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier2/riff.h" + + +//----------------------------------------------------------------------------- +// RIFF reader/writers that use the file system +//----------------------------------------------------------------------------- +extern IFileReadBinary *g_pFSIOReadBinary; +extern IFileWriteBinary *g_pFSIOWriteBinary; + + +//----------------------------------------------------------------------------- +// Returns the duration of a wav file +//----------------------------------------------------------------------------- +float GetWavSoundDuration( const char *pWavFile ); + + +#endif // SOUNDUTILS_H + diff --git a/public/tier2/tier2.h b/public/tier2/tier2.h new file mode 100644 index 0000000..b8eb53d --- /dev/null +++ b/public/tier2/tier2.h @@ -0,0 +1,121 @@ +//===== Copyright � 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef TIER2_H +#define TIER2_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier1/tier1.h" + + +//----------------------------------------------------------------------------- +// Call this to connect to/disconnect from all tier 2 libraries. +// It's up to the caller to check the globals it cares about to see if ones are missing +//----------------------------------------------------------------------------- +void ConnectTier2Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount ); +void DisconnectTier2Libraries(); + + +//----------------------------------------------------------------------------- +// Call this to get the file system set up to stdio for utilities, etc: +//----------------------------------------------------------------------------- +void InitDefaultFileSystem(void); +void ShutdownDefaultFileSystem(void); + + +//----------------------------------------------------------------------------- +// for simple utilities using valve libraries, call the entry point below in main(). It will +// init a filesystem for you, init mathlib, and create the command line. Note that this function +// may modify argc/argv because it filters out arguments (like -allowdebug). +//----------------------------------------------------------------------------- +void InitCommandLineProgram( int &argc, char ** &argv ); + + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier2 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier2AppSystem : public CTier1AppSystem< IInterface, ConVarFlag > +{ + typedef CTier1AppSystem< IInterface, ConVarFlag > BaseClass; + +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + ConnectTier2Libraries( &factory, 1 ); + return true; + } + + virtual InitReturnVal_t Init() + { + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + return INIT_OK; + } + + virtual AppSystemTier_t GetTier() + { + return APP_SYSTEM_TIER2; + } + + virtual void Shutdown() + { + BaseClass::Shutdown(); + } + + virtual void Disconnect() + { + DisconnectTier2Libraries(); + BaseClass::Disconnect(); + } +}; + + +//----------------------------------------------------------------------------- +// Distance fade information +//----------------------------------------------------------------------------- +enum FadeMode_t +{ + FADE_MODE_NONE = 0, + FADE_MODE_LOW, + FADE_MODE_MED, + FADE_MODE_HIGH, + FADE_MODE_360, + FADE_MODE_LEVEL, + + FADE_MODE_COUNT, +}; + +struct FadeData_t +{ + float m_flPercentMin; + float m_flPercentMax; + float m_flPixelMin; + float m_flPixelMax; + float m_flWidth; +}; + +// see tier2.cpp for data! +extern FadeData_t g_aFadeData[FADE_MODE_COUNT]; + + +//----------------------------------------------------------------------------- +// Used by the resource system for fast resource frame counter +//----------------------------------------------------------------------------- +extern uint32 g_nResourceFrameCount; + + +#endif // TIER2_H + diff --git a/public/tier2/tier2_logging.h b/public/tier2/tier2_logging.h new file mode 100644 index 0000000..f5cee5d --- /dev/null +++ b/public/tier2/tier2_logging.h @@ -0,0 +1,72 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +// +// Tier2 logging helpers. Adds support for file I/O +// +//=============================================================================== + +#ifndef TIER2_LOGGING_H +#define TIER2_LOGGING_H + +#if defined( COMPILER_MSVC ) +#pragma once +#endif + +#include "logging.h" + + +const int MAX_SIMULTANEOUS_LOGGING_FILE_COUNT = 16; +const int INVALID_LOGGING_FILE_HANDLE = -1; + +typedef int LoggingFileHandle_t; +typedef void * FileHandle_t; + +#define FILELOGGINGLISTENER_INTERFACE_VERSION "FileLoggingListener001" + +abstract_class IFileLoggingListener : public ILoggingListener +{ +public: + virtual void Log( const LoggingContext_t *pContext, const char *pMessage ) = 0; + + virtual LoggingFileHandle_t BeginLoggingToFile( const char *pFilename, const char *pOptions, const char *pPathID = NULL ) = 0; + virtual void EndLoggingToFile( LoggingFileHandle_t fileHandle ) = 0; + + virtual void AssignLogChannel( LoggingChannelID_t channelID, LoggingFileHandle_t loggingFileHandle ) = 0; + virtual void UnassignLogChannel( LoggingChannelID_t channelID ) = 0; + virtual void AssignAllLogChannels( LoggingFileHandle_t loggingFileHandle ) = 0; + virtual void UnassignAllLogChannels() = 0; +}; + +class CFileLoggingListener : public IFileLoggingListener +{ +public: + CFileLoggingListener(); + ~CFileLoggingListener(); + + virtual void Log( const LoggingContext_t *pContext, const char *pMessage ); + + virtual LoggingFileHandle_t BeginLoggingToFile( const char *pFilename, const char *pOptions, const char *pPathID = NULL ); + virtual void EndLoggingToFile( LoggingFileHandle_t fileHandle ); + + virtual void AssignLogChannel( LoggingChannelID_t channelID, LoggingFileHandle_t loggingFileHandle ); + virtual void UnassignLogChannel( LoggingChannelID_t channelID ); + virtual void AssignAllLogChannels( LoggingFileHandle_t loggingFileHandle ); + virtual void UnassignAllLogChannels(); + +private: + int GetUnusedFileInfo() const; + + struct FileInfo_t + { + FileHandle_t m_FileHandle; + + bool IsOpen() const { return m_FileHandle != 0; } + void Reset() { m_FileHandle = 0; } + }; + + FileInfo_t m_OpenFiles[MAX_SIMULTANEOUS_LOGGING_FILE_COUNT]; + + // Table which maps logging channel IDs to open files + int m_FileIndices[MAX_LOGGING_CHANNEL_COUNT]; +}; + +#endif // TIER2_LOGGING_H \ No newline at end of file diff --git a/public/tier2/tier2dm.h b/public/tier2/tier2dm.h new file mode 100644 index 0000000..9d481d5 --- /dev/null +++ b/public/tier2/tier2dm.h @@ -0,0 +1,72 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef TIER2DM_H +#define TIER2DM_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier2/tier2.h" + +//----------------------------------------------------------------------------- +// Set up methods related to datamodel interfaces +//----------------------------------------------------------------------------- +bool ConnectDataModel( CreateInterfaceFn factory ); +InitReturnVal_t InitDataModel(); +void ShutdownDataModel(); +void DisconnectDataModel(); + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier2 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier2DmAppSystem : public CTier2AppSystem< IInterface, ConVarFlag > +{ + typedef CTier2AppSystem< IInterface, ConVarFlag > BaseClass; + +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + ConnectDataModel( factory ); + + return true; + } + + virtual InitReturnVal_t Init() + { + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + nRetVal = InitDataModel(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + return INIT_OK; + } + + virtual void Shutdown() + { + ShutdownDataModel(); + BaseClass::Shutdown(); + } + + virtual void Disconnect() + { + DisconnectDataModel(); + BaseClass::Disconnect(); + } +}; + + +#endif // TIER2DM_H + diff --git a/public/tier2/tokenreader.h b/public/tier2/tokenreader.h new file mode 100644 index 0000000..f6af52a --- /dev/null +++ b/public/tier2/tokenreader.h @@ -0,0 +1,83 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TOKENREADER_H +#define TOKENREADER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/basetypes.h" +#include "tier2/utlstreambuffer.h" +#include + + +typedef enum +{ + TOKENSTRINGTOOLONG = -4, + TOKENERROR = -3, + TOKENNONE = -2, + TOKENEOF = -1, + OPERATOR, + INTEGER, + STRING, + IDENT +} trtoken_t; + + +#define IsToken(s1, s2) !strcmpi(s1, s2) + +#define MAX_TOKEN 128 + 1 +#define MAX_IDENT 64 + 1 +#define MAX_STRING 128 + 1 + + +class TokenReader +{ +public: + + TokenReader(); + + bool Open(const char *pszFilename); + trtoken_t NextToken(char *pszStore, int nSize); + trtoken_t NextTokenDynamic(char **ppszStore); + void Close(); + + void IgnoreTill(trtoken_t ttype, const char *pszToken); + void Stuff(trtoken_t ttype, const char *pszToken); + bool Expecting(trtoken_t ttype, const char *pszToken); + const char *Error(char *error, ...); + trtoken_t PeekTokenType(char* = NULL, int maxlen = 0); + + inline int GetErrorCount(void); + +private: + + trtoken_t GetString(char *pszStore, int nSize); + bool SkipWhiteSpace(void); + + CUtlStreamBuffer m_file; + int m_nLine; + int m_nErrorCount; + + char m_szFilename[128]; + char m_szStuffed[128]; + bool m_bStuffed; + trtoken_t m_eStuffed; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Returns the total number of parsing errors since this file was opened. +//----------------------------------------------------------------------------- +int TokenReader::GetErrorCount(void) +{ + return(m_nErrorCount); +} + + +#endif // TOKENREADER_H diff --git a/public/tier2/utlstreambuffer.h b/public/tier2/utlstreambuffer.h new file mode 100644 index 0000000..a425a25 --- /dev/null +++ b/public/tier2/utlstreambuffer.h @@ -0,0 +1,72 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLSTREAMBUFFER_H +#define UTLSTREAMBUFFER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlbuffer.h" +#include "filesystem.h" + + +//----------------------------------------------------------------------------- +// Command parsing.. +//----------------------------------------------------------------------------- +class CUtlStreamBuffer : public CUtlBuffer +{ + typedef CUtlBuffer BaseClass; + +public: + // See CUtlBuffer::BufferFlags_t for flags + CUtlStreamBuffer( ); + CUtlStreamBuffer( const char *pFileName, const char *pPath, int nFlags = 0, bool bDelayOpen = false, int nOpenFileFlags = 0 ); + ~CUtlStreamBuffer(); + + // Open the file. normally done in constructor + void Open( const char *pFileName, const char *pPath, int nFlags, int nOpenFileFlags = 0 ); + + // close the file. normally done in destructor + void Close(); + + // Is the file open? + bool IsOpen() const; + +private: + // error flags + enum + { + FILE_OPEN_ERROR = MAX_ERROR_FLAG << 1, + }; + + // Overflow functions + bool StreamPutOverflow( int nSize ); + bool StreamGetOverflow( int nSize ); + + // Grow allocation size to fit requested size + void GrowAllocatedSize( int nSize ); + + // Reads bytes from the file; fixes up maxput if necessary and null terminates + int ReadBytesFromFile( int nBytesToRead, int nReadOffset ); + + FileHandle_t OpenFile( const char *pFileName, const char *pPath, int nOpenFileFlags ); + + FileHandle_t m_hFileHandle; + + // cached for delayed open + char *m_pFileName; + char *m_pPath; + int m_nOpenFileFlags; +}; + + +#endif // UTLSTREAMBUFFER_H + diff --git a/public/tier2/vconfig.h b/public/tier2/vconfig.h new file mode 100644 index 0000000..7492bdd --- /dev/null +++ b/public/tier2/vconfig.h @@ -0,0 +1,27 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Utilities for setting vproject settings +// +//===========================================================================// + +#ifndef _VCONFIG_H +#define _VCONFIG_H + +#ifdef _WIN32 +#pragma once +#endif + + +// The registry keys that vconfig uses to store the current vproject directory. +#define VPROJECT_REG_KEY "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" + +// For accessing the environment variables we store the current vproject in. +void SetVConfigRegistrySetting( const char *pName, const char *pValue, bool bNotify = true ); +bool GetVConfigRegistrySetting( const char *pName, char *pReturn, int size ); +#ifdef _WIN32 +bool RemoveObsoleteVConfigRegistrySetting( const char *pValueName, char *pOldValue = NULL , int size = 0 ); +#endif +bool ConvertObsoleteVConfigRegistrySetting( const char *pValueName ); + + +#endif // _VCONFIG_H diff --git a/public/tier3/choreoutils.h b/public/tier3/choreoutils.h new file mode 100644 index 0000000..55d35af --- /dev/null +++ b/public/tier3/choreoutils.h @@ -0,0 +1,39 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Helper methods + classes for choreo +// +//===========================================================================// + +#ifndef CHOREOUTILS_H +#define CHOREOUTILS_H + +#if defined( _WIN32 ) +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CChoreoScene; +class CChoreoEvent; +class CStudioHdr; + + +//----------------------------------------------------------------------------- +// Finds sound files associated with events +//----------------------------------------------------------------------------- +const char *GetSoundForEvent( CChoreoEvent *pEvent, CStudioHdr *pStudioHdr ); + + +//----------------------------------------------------------------------------- +// Fixes up the duration of a choreo scene based on wav files + animations +// Returns true if a change needed to be made +//----------------------------------------------------------------------------- +bool AutoAddGestureKeys( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly ); +bool UpdateGestureLength( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly ); +bool UpdateSequenceLength( CChoreoEvent *e, CStudioHdr *pStudioHdr, float *pPoseParameters, bool bCheckOnly, bool bVerbose ); + + +#endif // CHOREOUTILS_H + diff --git a/public/tier3/mdlutils.h b/public/tier3/mdlutils.h new file mode 100644 index 0000000..641adda --- /dev/null +++ b/public/tier3/mdlutils.h @@ -0,0 +1,114 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef MDLUTILS_H +#define MDLUTILS_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "datacache/imdlcache.h" +#include "mathlib/vector.h" +#include "Color.h" +#include "studio.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct matrix3x4_t; + +class CMDLAttachmentData +{ +public: + matrix3x4_t m_AttachmentToWorld; + bool m_bValid; +}; + +struct MDLSquenceLayer_t +{ + int m_nSequenceIndex; + float m_flWeight; +}; + +//----------------------------------------------------------------------------- +// Class containing simplistic MDL state for use in rendering +//----------------------------------------------------------------------------- +class CMDL +{ +public: + CMDL(); + ~CMDL(); + + void SetMDL( MDLHandle_t h ); + MDLHandle_t GetMDL() const; + + // Simple version of drawing; sets up bones for you + void Draw( const matrix3x4_t& rootToWorld ); + + /// NOTE: This version of draw assumes you've filled in the bone to world + /// matrix yourself by calling IStudioRender::LockBoneMatrices. The pointer + /// returned by that method needs to be passed into here + /// @param flags allows you to specify additional STUDIORENDER_ flags -- usually never necessary + /// unless you need to (eg) forcibly disable shadows for some reason. + void Draw( const matrix3x4_t& rootToWorld, const matrix3x4_t *pBoneToWorld, int flags = 0 ); + + + void SetUpBones( const matrix3x4_t& shapeToWorld, int nMaxBoneCount, matrix3x4_t *pOutputMatrices, const float *pPoseParameters = NULL, MDLSquenceLayer_t *pSequenceLayers = NULL, int nNumSequenceLayers = 0 ); + void SetupBonesWithBoneMerge( const CStudioHdr *pMergeHdr, matrix3x4_t *pMergeBoneToWorld, + const CStudioHdr *pFollow, const matrix3x4_t *pFollowBoneToWorld, const matrix3x4_t &matModelToWorld ); + + studiohdr_t *GetStudioHdr(); + + virtual bool GetAttachment( int number, matrix3x4_t &matrix ); + +private: + void UnreferenceMDL(); + + void SetupBones_AttachmentHelper( CStudioHdr *hdr, Vector pos[], Quaternion q[] ); + bool PutAttachment( int number, const matrix3x4_t &attachmentToWorld ); + CUtlVector m_Attachments; + +public: + MDLHandle_t m_MDLHandle; + Color m_Color; + int m_nSkin; + int m_nBody; + int m_nSequence; + int m_nLOD; + float m_flPlaybackRate; + float m_flTime; + float m_pFlexControls[ MAXSTUDIOFLEXCTRL * 4 ]; + Vector m_vecViewTarget; + bool m_bWorldSpaceViewTarget; +}; + + +//----------------------------------------------------------------------------- +// Returns the bounding box for the model +//----------------------------------------------------------------------------- +void GetMDLBoundingBox( Vector *pMins, Vector *pMaxs, MDLHandle_t h, int nSequence ); + +//----------------------------------------------------------------------------- +// Returns the radius of the model as measured from the origin +//----------------------------------------------------------------------------- +float GetMDLRadius( MDLHandle_t h, int nSequence ); + +//----------------------------------------------------------------------------- +// Returns a more accurate bounding sphere +//----------------------------------------------------------------------------- +void GetMDLBoundingSphere( Vector *pVecCenter, float *pRadius, MDLHandle_t h, int nSequence ); + +//----------------------------------------------------------------------------- +// Determines which pose parameters are used by the specified sequence +//----------------------------------------------------------------------------- +void FindSequencePoseParameters( CStudioHdr &hdr, int nSequence, bool *pPoseParameters, int nCount ); + + +#endif // MDLUTILS_H + diff --git a/public/tier3/scenetokenprocessor.h b/public/tier3/scenetokenprocessor.h new file mode 100644 index 0000000..55c7f7f --- /dev/null +++ b/public/tier3/scenetokenprocessor.h @@ -0,0 +1,18 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef SCENETOKENPROCESSOR_H +#define SCENETOKENPROCESSOR_H +#ifdef _WIN32 +#pragma once +#endif + +class ISceneTokenProcessor; + +ISceneTokenProcessor *GetTokenProcessor(); +void SetTokenProcessorBuffer( const char *buf ); + +#endif // SCENETOKENPROCESSOR_H diff --git a/public/tier3/tier3.h b/public/tier3/tier3.h new file mode 100644 index 0000000..a957d70 --- /dev/null +++ b/public/tier3/tier3.h @@ -0,0 +1,58 @@ +//===== Copyright � 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef TIER3_H +#define TIER3_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier2/tier2.h" + + +//----------------------------------------------------------------------------- +// Call this to connect to/disconnect from all tier 3 libraries. +// It's up to the caller to check the globals it cares about to see if ones are missing +//----------------------------------------------------------------------------- +void ConnectTier3Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount ); +void DisconnectTier3Libraries(); + + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier3 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier3AppSystem : public CTier2AppSystem< IInterface, ConVarFlag > +{ + typedef CTier2AppSystem< IInterface, ConVarFlag > BaseClass; + +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + ConnectTier3Libraries( &factory, 1 ); + return true; + } + + virtual void Disconnect() + { + DisconnectTier3Libraries(); + BaseClass::Disconnect(); + } + + virtual AppSystemTier_t GetTier() + { + return APP_SYSTEM_TIER3; + } +}; + + +#endif // TIER3_H + diff --git a/public/tier3/tier3dm.h b/public/tier3/tier3dm.h new file mode 100644 index 0000000..6110292 --- /dev/null +++ b/public/tier3/tier3dm.h @@ -0,0 +1,45 @@ +//===== Copyright © 2005-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// + + +#ifndef TIER3DM_H +#define TIER3DM_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier3/tier3.h" +#include "tier2/tier2dm.h" + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier2 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier3DmAppSystem : public CTier2DmAppSystem< IInterface, ConVarFlag > +{ + typedef CTier2DmAppSystem< IInterface, ConVarFlag > BaseClass; + +public: + virtual bool Connect( CreateInterfaceFn factory ) + { + if ( !BaseClass::Connect( factory ) ) + return false; + + ConnectTier3Libraries( &factory, 1 ); + return true; + } + + virtual void Disconnect() + { + DisconnectTier3Libraries(); + BaseClass::Disconnect(); + } +}; + + +#endif // TIER3DM_H + diff --git a/public/toolframework/iclientenginetools.h b/public/toolframework/iclientenginetools.h new file mode 100644 index 0000000..4d4f6cf --- /dev/null +++ b/public/toolframework/iclientenginetools.h @@ -0,0 +1,59 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ICLIENTENGINETOOLS_H +#define ICLIENTENGINETOOLS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "toolframework/itoolentity.h" // HTOOLHANDLE + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class KeyValues; +struct AudioState_t; + + +//----------------------------------------------------------------------------- +// Purpose: Exported from engine to client .dll to marshall tool framework calls +// into IToolSystems +//----------------------------------------------------------------------------- +class IClientEngineTools : public IBaseInterface +{ +public: + // Level init, shutdown + virtual void LevelInitPreEntityAllTools() = 0; + // entities are created / spawned / precached here + virtual void LevelInitPostEntityAllTools() = 0; + + virtual void LevelShutdownPreEntityAllTools() = 0; + // Entities are deleted / released here... + virtual void LevelShutdownPostEntityAllTools() = 0; + + virtual void PreRenderAllTools() = 0; + virtual void PostRenderAllTools() = 0; + + virtual void PostToolMessage( HTOOLHANDLE hEntity, KeyValues *msg ) = 0; + + virtual void AdjustEngineViewport( int& x, int& y, int& width, int& height ) = 0; + virtual bool SetupEngineView( Vector &origin, QAngle &angles, float &fov ) = 0; + virtual bool SetupAudioState( AudioState_t &audioState ) = 0; + + // Paintmode is an enum declared in ienginevgui.h + virtual void VGui_PreRenderAllTools( int paintMode ) = 0; + virtual void VGui_PostRenderAllTools( int paintMode ) = 0; + + virtual bool IsThirdPersonCamera( ) = 0; + + virtual bool InToolMode() = 0; +}; + +#define VCLIENTENGINETOOLS_INTERFACE_VERSION "VCLIENTENGINETOOLS001" + +#endif // ICLIENTENGINETOOLS_H diff --git a/public/toolframework/ienginetool.h b/public/toolframework/ienginetool.h new file mode 100644 index 0000000..11f9c53 --- /dev/null +++ b/public/toolframework/ienginetool.h @@ -0,0 +1,246 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef IENGINETOOL_H +#define IENGINETOOL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" +#include "soundflags.h" +#include "avi/iavi.h" +#include "ispatialpartition.h" + +class CViewSetup; +class IToolSystem; +class KeyValues; +class ITraceFilter; +class CBaseTrace; +struct dlight_t; +struct Ray_t; +struct AudioState_t; + + +typedef bool (*FnQuitHandler)( void *pvUserData ); + +#ifndef MAX_DLIGHTS +#define MAX_DLIGHTS 32 +#endif + +// Exposed from engine to all tools, simplest interface +class IEngineToolFramework : public IBaseInterface +{ +public: + // Input system overrides TBD + // Something like this + //virtual void AddMessageHandler( int wm_message, bool (*pfnCallback)( int wm_message, int wParam, int lParam ) ) = 0; + //virtual void RemoveMessageHanlder( int wm_message, bool (*pfnCallbackToRemove)( int wm_message, int wParam, int lParam ) ) = 0; + + // Helpers for implementing a tool switching UI + virtual int GetToolCount() const = 0; + virtual char const *GetToolName( int index ) const = 0; + virtual void SwitchToTool( int index ) = 0; + + virtual bool IsTopmostTool( const IToolSystem *sys ) const = 0; + + virtual const IToolSystem *GetToolSystem( int index ) const = 0; + virtual IToolSystem *GetTopmostTool() = 0; + + // Take over input + virtual void ShowCursor( bool show ) = 0; + virtual bool IsCursorVisible() const = 0; + + // If module not already loaded, loads it and optionally switches to first tool in module. Returns false if load failed or tool already loaded + virtual bool LoadToolModule( char const *pToolModule, bool bSwitchToFirst ) = 0; +}; + +#define VENGINETOOLFRAMEWORK_INTERFACE_VERSION "VENGINETOOLFRAMEWORK003" + +struct model_t; +struct studiohdr_t; + +#include "toolframework/itoolentity.h" + +// Exposed from engine to tools via, more involved version of above +class IEngineTool : public IEngineToolFramework +{ +public: + virtual void GetServerFactory( CreateInterfaceFn& factory ) = 0; + virtual void GetClientFactory( CreateInterfaceFn& factory ) = 0; + + virtual float GetSoundDuration( const char *pszName ) = 0; + virtual bool IsSoundStillPlaying( int guid ) = 0; + // Returns the guid of the sound + virtual int StartSound( + int iUserData, + bool staticsound, + int iEntIndex, + int iChannel, + const char *pSample, + float flVolume, + soundlevel_t iSoundlevel, + const Vector& origin, + const Vector& direction, + int iFlags = 0, + int iPitch = PITCH_NORM, + bool bUpdatePositions = true, + float delay = 0.0f, + int speakerentity = -1 ) = 0; + + virtual void StopSoundByGuid( int guid ) = 0; + + virtual void SetVolumeByGuid( int guid, float flVolume ) = 0; + + // Returns how long the sound is + virtual float GetSoundDuration( int guid ) = 0; + + // Returns if the sound is looping + virtual bool IsLoopingSound( int guid ) = 0; + virtual void ReloadSound( const char *pSample ) = 0; + virtual void StopAllSounds( ) = 0; + virtual float GetMono16Samples( const char *pszName, CUtlVector< short >& sampleList ) = 0; + virtual void SetAudioState( const AudioState_t &audioState ) = 0; + + // Issue a console command + virtual void Command( char const *cmd ) = 0; + // Flush console command buffer right away + virtual void Execute() = 0; + + virtual char const *GetCurrentMap() = 0; + virtual void ChangeToMap( char const *mapname ) = 0; + virtual bool IsMapValid( char const *mapname ) = 0; + + // Method for causing engine to call client to render scene with no view model or overlays + // See cdll_int.h for enum RenderViewInfo_t for specifying whatToRender + virtual void RenderView( CViewSetup &view, int nFlags, int whatToRender ) = 0; + + // Returns true if the player is fully connected and active in game (i.e, not still loading) + virtual bool IsInGame() = 0; + // Returns true if the player is connected, but not necessarily active in game (could still be loading) + virtual bool IsConnected() = 0; + + virtual int GetMaxClients() = 0; // Tools might want to ensure single player, e.g. + + virtual bool IsGamePaused() = 0; + virtual void SetGamePaused( bool paused ) = 0; + + virtual float GetTimescale() = 0; // Could do this via ConVar system, too + virtual void SetTimescale( float scale ) = 0; + + // Real time is unscaled, but is updated once per frame + virtual float GetRealTime() = 0; + virtual float GetRealFrameTime() = 0; // unscaled + + // Get high precision timer (for profiling?) + virtual float Time() = 0; + + // Host time is scaled + virtual float HostFrameTime() = 0; // host_frametime + virtual float HostTime() = 0; // host_time + virtual int HostTick() = 0; // host_tickcount + virtual int HostFrameCount() = 0; // total famecount + + virtual float ServerTime() = 0; // gpGlobals->curtime on server + virtual float ServerFrameTime() = 0; // gpGlobals->frametime on server + virtual int ServerTick() = 0; // gpGlobals->tickcount on server + virtual float ServerTickInterval() = 0; // tick interval on server + + virtual float ClientTime() = 0; // gpGlobals->curtime on client + virtual float ClientFrameTime() = 0; // gpGlobals->frametime on client + virtual int ClientTick() = 0; // gpGlobals->tickcount on client + + virtual void SetClientFrameTime( float frametime ) = 0; // gpGlobals->frametime on client + + // Currently the engine doesn't like to do networking when it's paused, but if a tool changes entity state, it can be useful to force + // a network update to get that state over to the client + virtual void ForceUpdateDuringPause() = 0; + + // Maybe through modelcache??? + virtual model_t *GetModel( HTOOLHANDLE hEntity ) = 0; + // Get the .mdl file used by entity (if it's a cbaseanimating) + virtual studiohdr_t *GetStudioModel( HTOOLHANDLE hEntity ) = 0; + + // SINGLE PLAYER/LISTEN SERVER ONLY (just matching the client .dll api for this) + // Prints the formatted string to the notification area of the screen ( down the right hand edge + // numbered lines starting at position 0 + virtual void Con_NPrintf( int pos, const char *fmt, ... ) = 0; + // SINGLE PLAYER/LISTEN SERVER ONLY(just matching the client .dll api for this) + // Similar to Con_NPrintf, but allows specifying custom text color and duration information + virtual void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... ) = 0; + + // Get the current game directory (hl2, tf2, hl1, cstrike, etc.) + virtual void GetGameDir( char *szGetGameDir, int maxlength ) = 0; + + // Do we need separate rects for the 3d "viewport" vs. the tools surface??? and can we control viewports from + virtual void GetScreenSize( int& width, int &height ) = 0; + + // GetRootPanel(VPANEL) + + // Sets the location of the main view + virtual void SetMainView( const Vector &vecOrigin, const QAngle &angles ) = 0; + + // Gets the player view + virtual bool GetPlayerView( CViewSetup &playerView, int x, int y, int w, int h ) = 0; + + // From a location on the screen, figure out the vector into the world + virtual void CreatePickingRay( const CViewSetup &viewSetup, int x, int y, Vector& org, Vector& forward ) = 0; + + // precache methods + virtual bool PrecacheSound( const char *pName, bool bPreload = false ) = 0; + virtual bool PrecacheModel( const char *pName, bool bPreload = false ) = 0; + + virtual void InstallQuitHandler( void *pvUserData, FnQuitHandler func ) = 0; + virtual void TakeTGAScreenShot( char const *filename, int width, int height ) = 0; + // Even if game is paused, force networking to update to get new server state down to client + virtual void ForceSend() = 0; + + virtual bool IsRecordingMovie() = 0; + + // NOTE: Params can contain file name, frame rate, output avi, output raw, and duration + virtual void StartMovieRecording( KeyValues *pMovieParams ) = 0; + virtual void EndMovieRecording() = 0; + virtual void CancelMovieRecording() = 0; + virtual AVIHandle_t GetRecordingAVIHandle() = 0; + + virtual void StartRecordingVoiceToFile( char const *filename, char const *pPathID = 0 ) = 0; + virtual void StopRecordingVoiceToFile() = 0; + virtual bool IsVoiceRecording() = 0; + + // A version that simply accepts a ray (can work as a traceline or tracehull) + virtual void TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, CBaseTrace *pTrace ) = 0; // client version + virtual void TraceRayServer( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, CBaseTrace *pTrace ) = 0; + + virtual bool IsConsoleVisible() = 0; + + virtual int GetPointContents( const Vector &vecPosition ) = 0; + + virtual int GetActiveDLights( dlight_t *pList[MAX_DLIGHTS] ) = 0; + virtual int GetLightingConditions( const Vector &vecPosition, Vector *pColors, int nMaxLocalLights, LightDesc_t *pLocalLights ) = 0; + + virtual void GetWorldToScreenMatrixForView( const CViewSetup &view, VMatrix *pVMatrix ) = 0; + + // Collision support + virtual SpatialPartitionHandle_t CreatePartitionHandle( IHandleEntity *pEntity, + SpatialPartitionListMask_t listMask, const Vector& mins, const Vector& maxs ) = 0; + virtual void DestroyPartitionHandle( SpatialPartitionHandle_t hPartition ) = 0; + virtual void InstallPartitionQueryCallback( IPartitionQueryCallback *pQuery ) = 0; + virtual void RemovePartitionQueryCallback( IPartitionQueryCallback *pQuery ) = 0; + virtual void ElementMoved( SpatialPartitionHandle_t handle, + const Vector& mins, const Vector& maxs ) = 0; + virtual void OnModeChanged( bool bGameMode ) = 0; + // Get the engine's window. + virtual void* GetEngineHwnd() = 0; + + // Returns the actual elapsed time of the samples + virtual float GetSoundElapsedTime( int guid ) = 0; + virtual void ValidateSoundCache( char const *pchSoundName ) = 0; + virtual void PrefetchSound( char const *pchSoundName ) = 0; +}; + +#define VENGINETOOL_INTERFACE_VERSION "VENGINETOOL003" + +#endif // IENGINETOOL_H diff --git a/public/toolframework/iserverenginetools.h b/public/toolframework/iserverenginetools.h new file mode 100644 index 0000000..6b67ab9 --- /dev/null +++ b/public/toolframework/iserverenginetools.h @@ -0,0 +1,56 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ISERVERENGINETOOLS_H +#define ISERVERENGINETOOLS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CServerDemo; + +//----------------------------------------------------------------------------- +// Purpose: exposed from engine to game .dll +//----------------------------------------------------------------------------- +class IServerEngineTools : public IBaseInterface +{ +public: + // Level init, shutdown + virtual void LevelInitPreEntityAllTools() = 0; + // entities are created / spawned / precached here + virtual void LevelInitPostEntityAllTools() = 0; + + virtual void LevelShutdownPreEntityAllTools() = 0; + // Entities are deleted / released here... + virtual void LevelShutdownPostEntityAllTools() = 0; + // end of level shutdown + + // Called each frame before entities think + virtual void FrameUpdatePreEntityThinkAllTools() = 0; + // called after entities think + virtual void FrameUpdatePostEntityThinkAllTools() = 0; + + virtual void PreClientUpdateAllTools() = 0; + // FIXME: PostClientUpdateAllTools()??? + + // The server uses this to call into the tools to get the actual + // entities to spawn on startup + virtual const char* GetEntityData( const char *pActualEntityData ) = 0; + virtual void* QueryInterface( const char *pInterfaceName ) = 0; + + virtual void PreSetupVisibilityAllTools() = 0; + + virtual bool InToolMode() = 0; +}; + +#define VSERVERENGINETOOLS_INTERFACE_VERSION "VSERVERENGINETOOLS001" + +#endif // ISERVERENGINETOOLS_H diff --git a/public/toolframework/itooldictionary.h b/public/toolframework/itooldictionary.h new file mode 100644 index 0000000..97056b5 --- /dev/null +++ b/public/toolframework/itooldictionary.h @@ -0,0 +1,39 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ITOOLDICTIONARY_H +#define ITOOLDICTIONARY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +class IToolSystem; + + +//----------------------------------------------------------------------------- +// Purpose: Every tool dll sitting in bin\tools must expose this interface +// The engine will load the .dll, get this interface, and then ask for all +// tools in the .dll +// The engine will call CreateTools just before querying for the tools, so you +// can instance any dynamically instanced tools during that call +//----------------------------------------------------------------------------- +class IToolDictionary : public IAppSystem +{ +public: + virtual void CreateTools() = 0; + virtual int GetToolCount() const = 0; + virtual IToolSystem *GetTool( int index ) = 0; +}; + +#define VTOOLDICTIONARY_INTERFACE_VERSION "VTOOLDICTIONARY003" + +#endif // ITOOLDICTIONARY_H diff --git a/public/toolframework/itoolentity.h b/public/toolframework/itoolentity.h new file mode 100644 index 0000000..81ec194 --- /dev/null +++ b/public/toolframework/itoolentity.h @@ -0,0 +1,236 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ITOOLENTITY_H +#define ITOOLENTITY_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "tier1/utlvector.h" +#include "Color.h" +#include "basehandle.h" +#include "iclientrenderable.h" +#include "engine/ishadowmgr.h" +#include "engine/ivmodelinfo.h" +#include "engine/IClientLeafSystem.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IServerEntity; +class IClientEntity; +class IToolSystem; +class IClientRenderable; +class Vector; +class QAngle; + + +//----------------------------------------------------------------------------- +// Safe accessor to an entity +//----------------------------------------------------------------------------- +typedef unsigned int HTOOLHANDLE; +enum +{ + HTOOLHANDLE_INVALID = 0 +}; + + +//----------------------------------------------------------------------------- +// If you change this, change the flags in IClientShadowMgr.h also +//----------------------------------------------------------------------------- +enum ClientShadowFlags_t +{ + SHADOW_FLAGS_USE_RENDER_TO_TEXTURE = (SHADOW_FLAGS_LAST_FLAG<<1), + SHADOW_FLAGS_ANIMATING_SOURCE = (SHADOW_FLAGS_LAST_FLAG<<2), + SHADOW_FLAGS_USE_DEPTH_TEXTURE = (SHADOW_FLAGS_LAST_FLAG<<3), + SHADOW_FLAGS_CUSTOM_DRAW = (SHADOW_FLAGS_LAST_FLAG<<4), + // Update this if you add flags + CLIENT_SHADOW_FLAGS_LAST_FLAG = SHADOW_FLAGS_CUSTOM_DRAW +}; + + +//----------------------------------------------------------------------------- +// Opaque pointer returned from Find* methods, don't store this, you need to +// Attach it to a tool entity or discard after searching +//----------------------------------------------------------------------------- +typedef void *EntitySearchResult; +typedef void *ParticleSystemSearchResult; + + +//----------------------------------------------------------------------------- +// Purpose: Client side tool interace (right now just handles IClientRenderables). +// In theory could support hooking into client side entities directly +//----------------------------------------------------------------------------- +class IClientTools : public IBaseInterface +{ +public: + // Allocates or returns the handle to an entity previously found using the Find* APIs below + virtual HTOOLHANDLE AttachToEntity( EntitySearchResult entityToAttach ) = 0; + virtual void DetachFromEntity( EntitySearchResult entityToDetach ) = 0; + + virtual EntitySearchResult GetEntity( HTOOLHANDLE handle ) = 0; + + // Checks whether a handle is still valid. + virtual bool IsValidHandle( HTOOLHANDLE handle ) = 0; + + // Iterates the list of entities which have been associated with tools + virtual int GetNumRecordables() = 0; + virtual HTOOLHANDLE GetRecordable( int index ) = 0; + + // Iterates through ALL entities (separate list for client vs. server) + virtual EntitySearchResult NextEntity( EntitySearchResult currentEnt ) = 0; + EntitySearchResult FirstEntity() { return NextEntity( NULL ); } + + // Use this to turn on/off the presence of an underlying game entity + virtual void SetEnabled( HTOOLHANDLE handle, bool enabled ) = 0; + // Use this to tell an entity to post "state" to all listening tools + virtual void SetRecording( HTOOLHANDLE handle, bool recording ) = 0; + // Some entities are marked with ShouldRecordInTools false, such as ui entities, etc. + virtual bool ShouldRecord( HTOOLHANDLE handle ) = 0; + + virtual HTOOLHANDLE GetToolHandleForEntityByIndex( int entindex ) = 0; + + virtual int GetModelIndex( HTOOLHANDLE handle ) = 0; + virtual const char* GetModelName ( HTOOLHANDLE handle ) = 0; + virtual const char* GetClassname ( HTOOLHANDLE handle ) = 0; + + virtual void AddClientRenderable( IClientRenderable *pRenderable, bool bDrawWithViewModels, RenderableTranslucencyType_t nType, RenderableModelType_t nModelType = RENDERABLE_MODEL_UNKNOWN_TYPE ) = 0; + virtual void RemoveClientRenderable( IClientRenderable *pRenderable ) = 0; + virtual void SetTranslucencyType( IClientRenderable *pRenderable, RenderableTranslucencyType_t nType ) = 0; + virtual void MarkClientRenderableDirty( IClientRenderable *pRenderable ) = 0; + virtual void UpdateProjectedTexture( ClientShadowHandle_t h, bool bForce ) = 0; + + virtual bool DrawSprite( IClientRenderable *pRenderable, float scale, float frame, int rendermode, int renderfx, const Color &color, float flProxyRadius, int *pVisHandle ) = 0; + virtual void DrawSprite( const Vector &vecOrigin, float flWidth, float flHeight, color32 color ) = 0; + + virtual EntitySearchResult GetLocalPlayer() = 0; + virtual bool GetLocalPlayerEyePosition( Vector& org, QAngle& ang, float &fov ) = 0; + + // See ClientShadowFlags_t above + virtual ClientShadowHandle_t CreateShadow( CBaseHandle handle, int nFlags ) = 0; + virtual void DestroyShadow( ClientShadowHandle_t h ) = 0; + + virtual ClientShadowHandle_t CreateFlashlight( const FlashlightState_t &lightState ) = 0; + virtual void DestroyFlashlight( ClientShadowHandle_t h ) = 0; + virtual void UpdateFlashlightState( ClientShadowHandle_t h, const FlashlightState_t &lightState ) = 0; + + virtual void AddToDirtyShadowList( ClientShadowHandle_t h, bool force = false ) = 0; + virtual void MarkRenderToTextureShadowDirty( ClientShadowHandle_t h ) = 0; + + // Global toggle for recording + virtual void EnableRecordingMode( bool bEnable ) = 0; + virtual bool IsInRecordingMode() const = 0; + + // Trigger a temp entity + virtual void TriggerTempEntity( KeyValues *pKeyValues ) = 0; + + // get owning weapon (for viewmodels) + virtual int GetOwningWeaponEntIndex( int entindex ) = 0; + virtual int GetEntIndex( EntitySearchResult entityToAttach ) = 0; + + virtual int FindGlobalFlexcontroller( char const *name ) = 0; + virtual char const *GetGlobalFlexControllerName( int idx ) = 0; + + // helper for traversing ownership hierarchy + virtual EntitySearchResult GetOwnerEntity( EntitySearchResult currentEnt ) = 0; + + // common and useful types to query for hierarchically + virtual bool IsPlayer ( EntitySearchResult currentEnt ) = 0; + virtual bool IsCombatCharacter ( EntitySearchResult currentEnt ) = 0; + virtual bool IsNPC ( EntitySearchResult currentEnt ) = 0; + virtual bool IsRagdoll ( EntitySearchResult currentEnt ) = 0; + virtual bool IsViewModel ( EntitySearchResult currentEnt ) = 0; + virtual bool IsViewModelOrAttachment( EntitySearchResult currentEnt ) = 0; + virtual bool IsWeapon ( EntitySearchResult currentEnt ) = 0; + virtual bool IsSprite ( EntitySearchResult currentEnt ) = 0; + virtual bool IsProp ( EntitySearchResult currentEnt ) = 0; + virtual bool IsBrush ( EntitySearchResult currentEnt ) = 0; + + virtual Vector GetAbsOrigin( HTOOLHANDLE handle ) = 0; + virtual QAngle GetAbsAngles( HTOOLHANDLE handle ) = 0; + + // This reloads a portion or all of a particle definition file. + // It's up to the client to decide if it cares about this file + // Use a UtlBuffer to crack the data + virtual void ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen ) = 0; + + // ParticleSystem iteration, query, modification + virtual ParticleSystemSearchResult FirstParticleSystem() { return NextParticleSystem( NULL ); } + virtual ParticleSystemSearchResult NextParticleSystem( ParticleSystemSearchResult sr ) = 0; + virtual void SetRecording( ParticleSystemSearchResult sr, bool bRecord ) = 0; + + // Sends a mesage from the tool to the client + virtual void PostToolMessage( KeyValues *pKeyValues ) = 0; + + // Indicates whether the client should render particle systems + virtual void EnableParticleSystems( bool bEnable ) = 0; + + // Is the game rendering in 3rd person mode? + virtual bool IsRenderingThirdPerson() const = 0; +}; + +#define VCLIENTTOOLS_INTERFACE_VERSION "VCLIENTTOOLS001" + + +class CEntityRespawnInfo +{ +public: + int m_nHammerID; + const char *m_pEntText; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Interface from engine to tools for manipulating entities +//----------------------------------------------------------------------------- +class IServerTools : public IBaseInterface +{ +public: + virtual IServerEntity *GetIServerEntity( IClientEntity *pClientEntity ) = 0; + virtual bool SnapPlayerToPosition( const Vector &org, const QAngle &ang, IClientEntity *pClientPlayer = NULL ) = 0; + virtual bool GetPlayerPosition( Vector &org, QAngle &ang, IClientEntity *pClientPlayer = NULL ) = 0; + virtual bool SetPlayerFOV( int fov, IClientEntity *pClientPlayer = NULL ) = 0; + virtual int GetPlayerFOV( IClientEntity *pClientPlayer = NULL ) = 0; + virtual bool IsInNoClipMode( IClientEntity *pClientPlayer = NULL ) = 0; + + // entity searching + virtual void *FirstEntity( void ) = 0; + virtual void *NextEntity( void *pEntity ) = 0; + virtual void *FindEntityByHammerID( int iHammerID ) = 0; + + // entity query + virtual bool GetKeyValue( void *pEntity, const char *szField, char *szValue, int iMaxLen ) = 0; + virtual bool SetKeyValue( void *pEntity, const char *szField, const char *szValue ) = 0; + virtual bool SetKeyValue( void *pEntity, const char *szField, float flValue ) = 0; + virtual bool SetKeyValue( void *pEntity, const char *szField, const Vector &vecValue ) = 0; + + // entity spawning + virtual void *CreateEntityByName( const char *szClassName ) = 0; + virtual void DispatchSpawn( void *pEntity ) = 0; + virtual bool DestroyEntityByHammerId( int iHammerID ) = 0; + + // This function respawns the entity into the same entindex slot AND tricks the EHANDLE system into thinking it's the same + // entity version so anyone holding an EHANDLE to the entity points at the newly-respawned entity. + virtual bool RespawnEntitiesWithEdits( CEntityRespawnInfo *pInfos, int nInfos ) = 0; + + // This reloads a portion or all of a particle definition file. + // It's up to the server to decide if it cares about this file + // Use a UtlBuffer to crack the data + virtual void ReloadParticleDefintions( const char *pFileName, const void *pBufData, int nLen ) = 0; + + virtual void AddOriginToPVS( const Vector &org ) = 0; + virtual void MoveEngineViewTo( const Vector &vPos, const QAngle &vAngles ) = 0; + + // Call UTIL_Remove on the entity. + virtual void RemoveEntity( int nHammerID ) = 0; +}; + +#define VSERVERTOOLS_INTERFACE_VERSION "VSERVERTOOLS001" + +#endif // ITOOLENTITY_H diff --git a/public/toolframework/itoolframework.h b/public/toolframework/itoolframework.h new file mode 100644 index 0000000..f711f27 --- /dev/null +++ b/public/toolframework/itoolframework.h @@ -0,0 +1,321 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ITOOLFRAMEWORK_H +#define ITOOLFRAMEWORK_H +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/IAppSystem.h" +#include "materialsystem/imaterialproxy.h" +#include "toolframework/itoolentity.h" +#include "mathlib/vector.h" +#include "Color.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IToolSystem; +struct SpatializationInfo_t; +class KeyValues; +class CBoneList; + + +//----------------------------------------------------------------------------- +// Standard messages +//----------------------------------------------------------------------------- +struct EffectRecordingState_t +{ + bool m_bVisible : 1; + bool m_bThirdPerson : 1; + Color m_Color; + float m_Scale; + const char *m_pMaterialName; + int m_nAttachment; + Vector m_vecAttachment; // only used if m_nAttachment is -1 +}; + +struct BaseEntityRecordingState_t +{ + BaseEntityRecordingState_t() : + m_flTime( 0.0f ), + m_pModelName( 0 ), + m_nOwner( -1 ), + m_fEffects( 0 ), + m_bVisible( false ), + m_bRecordFinalVisibleSample( false ), + m_numEffects( 0 ), + m_pEffects( NULL ), + m_nFollowEntity( -1 ) + { + m_vecRenderOrigin.Init(); + m_vecRenderAngles.Init(); + } + + float m_flTime; + const char *m_pModelName; + int m_nOwner; + int m_fEffects; + bool m_bVisible : 1; + bool m_bRecordFinalVisibleSample : 1; + Vector m_vecRenderOrigin; + QAngle m_vecRenderAngles; + int m_nFollowEntity; + + int m_numEffects; + EffectRecordingState_t *m_pEffects; +}; + +struct SpriteRecordingState_t +{ + float m_flRenderScale; + float m_flFrame; + int m_nRenderMode; + bool m_nRenderFX; + Color m_Color; + float m_flProxyRadius; +}; + +struct BaseAnimatingHighLevelRecordingState_t +{ + BaseAnimatingHighLevelRecordingState_t() + : m_bClearIkTargets( false ), + m_bIsRagdoll( false ), + m_bShouldCreateIkContext( false ), + m_nNumPoseParams( 0 ), + m_flCycle( 0.0f ), + m_flPlaybackRate( 1.0f ), + m_flCycleRate( 0.0f ), + m_nFrameCount( 0 ), + m_bInterpEffectActive( false ) + { + } + + bool m_bClearIkTargets; + bool m_bIsRagdoll; + bool m_bShouldCreateIkContext; + int m_nNumPoseParams; + + float m_flCycle; + float m_flPlaybackRate; + float m_flCycleRate; + int m_nFrameCount; + + float m_flPoseParameter[MAXSTUDIOPOSEPARAM]; + + bool m_bInterpEffectActive; +}; + +struct BaseAnimatingRecordingState_t +{ + BaseAnimatingHighLevelRecordingState_t m_highLevelState; + + int m_nSkin; + int m_nBody; + int m_nSequence; + CBoneList *m_pBoneList; +}; + +struct BaseFlexRecordingState_t +{ + int m_nFlexCount; + float *m_pDestWeight; + Vector m_vecViewTarget; +}; + +struct CameraRecordingState_t +{ + bool m_bThirdPerson; + float m_flFOV; + Vector m_vecEyePosition; + QAngle m_vecEyeAngles; +}; + +struct MonitorRecordingState_t +{ + bool m_bActive; + float m_flFOV; + bool m_bFogEnabled; + float m_flFogStart; + float m_flFogEnd; + Color m_FogColor; +}; + +struct EntityTeleportedRecordingState_t +{ + Vector m_vecTo; + QAngle m_qaTo; + bool m_bTeleported; + bool m_bViewOverride; + matrix3x4_t m_teleportMatrix; +}; + +struct PortalRecordingState_t +{ + int m_nPortalId; + int m_nLinkedPortalId; + float m_fStaticAmount; + float m_fSecondaryStaticAmount; + float m_fOpenAmount; + bool m_bIsPortal2; //for any set of portals, one must be portal 1, and the other portal 2. Uses different render targets +}; + +struct ParticleSystemCreatedState_t +{ + int m_nParticleSystemId; + const char * m_pName; + float m_flTime; + int m_nOwner; +}; + +struct ParticleSystemDestroyedState_t +{ + int m_nParticleSystemId; + float m_flTime; +}; + +struct ParticleSystemStopEmissionState_t +{ + int m_nParticleSystemId; + float m_flTime; + bool m_bInfiniteOnly; +}; + +struct ParticleSystemSetControlPointObjectState_t +{ + int m_nParticleSystemId; + float m_flTime; + int m_nControlPoint; + int m_nObject; +}; + +struct ParticleSystemSetControlPointPositionState_t +{ + int m_nParticleSystemId; + float m_flTime; + int m_nControlPoint; + Vector m_vecPosition; +}; + +struct ParticleSystemSetControlPointOrientationState_t +{ + int m_nParticleSystemId; + float m_flTime; + int m_nControlPoint; + Quaternion m_qOrientation; +}; + + +//----------------------------------------------------------------------------- +// Purpose: This interface lives in the engine and handles loading up/unloading all +// available tools +//----------------------------------------------------------------------------- +class IToolFrameworkInternal : public IAppSystem +{ +public: // Client Hooks + virtual bool ClientInit( CreateInterfaceFn clientFactory ) = 0; + virtual void ClientShutdown() = 0; + + // Level init, shutdown + virtual void ClientLevelInitPreEntityAllTools() = 0; + // entities are created / spawned / precached here + virtual void ClientLevelInitPostEntityAllTools() = 0; + + virtual void ClientLevelShutdownPreEntityAllTools() = 0; + // Entities are deleted / released here... + virtual void ClientLevelShutdownPostEntityAllTools() = 0; + + virtual void ClientPreRenderAllTools() = 0; + virtual void ClientPostRenderAllTools() = 0; + + // Should we render with a thirdperson camera? + virtual bool IsThirdPersonCamera() = 0; + + // is the current tool recording? + virtual bool IsToolRecording() = 0; + +public: // Server Hooks + // Level init, shutdown + virtual bool ServerInit( CreateInterfaceFn serverFactory ) = 0; + virtual void ServerShutdown() = 0; + + virtual void ServerLevelInitPreEntityAllTools() = 0; + // entities are created / spawned / precached here + virtual void ServerLevelInitPostEntityAllTools() = 0; + + virtual void ServerLevelShutdownPreEntityAllTools() = 0; + // Entities are deleted / released here... + virtual void ServerLevelShutdownPostEntityAllTools() = 0; + // end of level shutdown + + // Called each frame before entities think + virtual void ServerFrameUpdatePreEntityThinkAllTools() = 0; + // called after entities think + virtual void ServerFrameUpdatePostEntityThinkAllTools() = 0; + virtual void ServerPreClientUpdateAllTools() = 0; + + virtual void ServerPreSetupVisibilityAllTools() = 0; + +public: // Other Hooks + // If any tool returns false, the engine will not actually quit + // FIXME: Not implemented yet + virtual bool CanQuit() = 0; + + // Called at end of Host_Init + virtual bool PostInit() = 0; + + virtual void Think( bool finalTick ) = 0; + + virtual void PostMessage( KeyValues *msg ) = 0; + + virtual bool GetSoundSpatialization( int iUserData, int guid, SpatializationInfo_t& info ) = 0; + + virtual void HostRunFrameBegin() = 0; + virtual void HostRunFrameEnd() = 0; + + virtual void RenderFrameBegin() = 0; + virtual void RenderFrameEnd() = 0; + + // Paintmode is an enum declared in enginevgui.h + virtual void VGui_PreRenderAllTools( int paintMode ) = 0; + virtual void VGui_PostRenderAllTools( int paintMode ) = 0; + + virtual void VGui_PreSimulateAllTools() = 0; + virtual void VGui_PostSimulateAllTools() = 0; + + // Are we using tools? + virtual bool InToolMode() = 0; + + // Should the game be allowed to render the world? + virtual bool ShouldGameRenderView() = 0; + + // Should sounds from the game be played? + virtual bool ShouldGamePlaySounds() = 0; + + virtual IMaterialProxy *LookupProxy( const char *proxyName ) = 0; + +public: // general framework hooks + virtual bool LoadFilmmaker() = 0; + virtual void UnloadFilmmaker() = 0; + virtual int GetToolCount() = 0; + virtual char const *GetToolName( int index ) = 0; + virtual void SwitchToTool( int index ) = 0; + virtual IToolSystem *SwitchToTool( const char *pToolName ) = 0; + virtual bool IsTopmostTool( const IToolSystem *sys ) = 0; + virtual const IToolSystem *GetToolSystem( int index ) const = 0; + virtual IToolSystem *GetTopmostTool() = 0; + // If module not already loaded, loads it and optionally switches to first tool in module. Returns false if load failed or tool already loaded + virtual bool LoadToolModule( char const *pToolModule, bool bSwitchToFirst ) = 0; +}; + +// Expose to rest of engine as a singleton +extern IToolFrameworkInternal *toolframework; + +// Exposed to launcher to automatically add AppSystemGroup hooks +#define VTOOLFRAMEWORK_INTERFACE_VERSION "VTOOLFRAMEWORKVERSION002" + +#endif // ITOOLFRAMEWORK_H diff --git a/public/toolframework/itoolsystem.h b/public/toolframework/itoolsystem.h new file mode 100644 index 0000000..138108f --- /dev/null +++ b/public/toolframework/itoolsystem.h @@ -0,0 +1,159 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef ITOOLSYSTEM_H +#define ITOOLSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "itoolentity.h" +#include "interface.h" +#include "materialsystem/imaterialproxy.h" +#include "inputsystem/iinputsystem.h" + +class KeyValues; +struct SpatializationInfo_t; +struct AudioState_t; + +namespace vgui +{ + typedef unsigned int VPANEL; +} + +class CServerDemo; + +//----------------------------------------------------------------------------- +// Purpose: All tools expose this interface, which includes both client and server +// related hooks +//----------------------------------------------------------------------------- +class IToolSystem +{ +public: + // Name describing the tool + virtual char const *GetToolName() = 0; + + // Called at the end of engine startup (after client .dll and server .dll have been loaded) + virtual bool Init() = 0; + + // Called during RemoveTool or when engine is shutting down + virtual void Shutdown() = 0; + + // Called after server.dll is loaded + virtual bool ServerInit( CreateInterfaceFn serverFactory ) = 0; + // Called after client.dll is loaded + virtual bool ClientInit( CreateInterfaceFn clientFactory ) = 0; + + virtual void ServerShutdown() = 0; + virtual void ClientShutdown() = 0; + + // Allow tool to override quitting, called before Shutdown(), return no to abort quitting + virtual bool CanQuit( const char *pExitMsg ) = 0; + + // Called when another system wiches to post a message to the tool and/or a specific entity + // FIXME: Are KeyValues too inefficient here? + virtual void PostToolMessage( HTOOLHANDLE hEntity, KeyValues *message ) = 0; + + // Called oncer per frame even when no level is loaded... (call ProcessMessages()) + virtual void Think( bool finalTick ) = 0; + +// Server calls: + + // Level init, shutdown + virtual void ServerLevelInitPreEntity() = 0; + // entities are created / spawned / precached here + virtual void ServerLevelInitPostEntity() = 0; + + virtual void ServerLevelShutdownPreEntity() = 0; + // Entities are deleted / released here... + virtual void ServerLevelShutdownPostEntity() = 0; + // end of level shutdown + + // Called each frame before entities think + virtual void ServerFrameUpdatePreEntityThink() = 0; + // called after entities think + virtual void ServerFrameUpdatePostEntityThink() = 0; + virtual void ServerPreClientUpdate() = 0; + virtual void ServerPreSetupVisibility() = 0; + + // Used to allow the tool to spawn different entities when it's active + virtual const char* GetEntityData( const char *pActualEntityData ) = 0; + virtual void* QueryInterface( const char *pInterfaceName ) = 0; + +// Client calls: + // Level init, shutdown + virtual void ClientLevelInitPreEntity() = 0; + // entities are created / spawned / precached here + virtual void ClientLevelInitPostEntity() = 0; + + virtual void ClientLevelShutdownPreEntity() = 0; + // Entities are deleted / released here... + virtual void ClientLevelShutdownPostEntity() = 0; + // end of level shutdown + // Called before rendering + virtual void ClientPreRender() = 0; + virtual void ClientPostRender() = 0; + + // Let tool override viewport for engine + virtual void AdjustEngineViewport( int& x, int& y, int& width, int& height ) = 0; + + // let tool override view/camera + virtual bool SetupEngineView( Vector &origin, QAngle &angles, float &fov ) = 0; + + // let tool override microphone + virtual bool SetupAudioState( AudioState_t &audioState ) = 0; + + // Should the client be allowed to render the view normally? + virtual bool ShouldGameRenderView() = 0; + virtual bool IsThirdPersonCamera() = 0; + + // Should sounds from the game be played? + virtual bool ShouldGamePlaySounds() = 0; + + // is the current tool recording? + virtual bool IsToolRecording() = 0; + + virtual IMaterialProxy *LookupProxy( const char *proxyName ) = 0; + + // Possible hooks for rendering + // virtual void Think( float curtime, float frametime ) = 0; + // virtual void Prerender() = 0; + // virtual void Render3D() = 0; + // virtual void Render2D() = 0; +// Tool activation/deactivation + + // This tool is being activated + virtual void OnToolActivate() = 0; + // Another tool is being activated + virtual void OnToolDeactivate() = 0; + + virtual bool TrapKey( ButtonCode_t key, bool down ) = 0; + + virtual bool GetSoundSpatialization( int iUserData, int guid, SpatializationInfo_t& info ) = 0; + + // Unlike the client .dll pre/post render stuff, these get called no matter whether a map is loaded and they only get called once per frame!!! + virtual void RenderFrameBegin() = 0; + virtual void RenderFrameEnd() = 0; + + // wraps the entire frame - surrounding all other begin/end and pre/post calls + virtual void HostRunFrameBegin() = 0; + virtual void HostRunFrameEnd() = 0; + + // See enginevgui.h for paintmode_t enum definitions + virtual void VGui_PreRender( int paintMode ) = 0; + virtual void VGui_PostRender( int paintMode ) = 0; + + virtual void VGui_PreSimulate() = 0; + virtual void VGui_PostSimulate() = 0; + + virtual vgui::VPANEL GetClientWorkspaceArea() = 0; +}; + +// Pointer to a member method of IGameSystem +typedef void (IToolSystem::*ToolSystemFunc_t)(); +typedef void (IToolSystem::*ToolSystemFunc_Int_t)( int arg ); + +#endif // ITOOLSYSTEM_H diff --git a/public/toolframework/toolframework.cpp b/public/toolframework/toolframework.cpp new file mode 100644 index 0000000..263a8ce --- /dev/null +++ b/public/toolframework/toolframework.cpp @@ -0,0 +1,40 @@ +#include "toolframework/itooldictionary.h" +#include "utlvector.h" + +class CToolDictionary : public IToolDictionary +{ +public: + virtual int GetToolCount() const + { + return m_Tools.Count(); + } + + virtual IToolSystem *GetTool( int index ) + { + if ( index < 0 || index >= m_Tools.Count() ) + { + return NULL; + } + return m_Tools[ index ]; + } + +public: + + void RegisterTool( IToolSystem *tool ) + { + m_Tools.AddToTail( tool ); + } +private: + + CUtlVector< IToolSystem * > m_Tools; +}; + +static CToolDictionary g_ToolDictionary; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( IToolDictionary, CToolDictionary, VTOOLDICTIONARY_INTERFACE_VERSION, g_ToolDictionary ); + +void RegisterTool( IToolSystem *tool ) +{ + g_ToolDictionary.RegisterTool( tool ); +} + diff --git a/public/tools/bonelist.cpp b/public/tools/bonelist.cpp new file mode 100644 index 0000000..6b4e5c4 --- /dev/null +++ b/public/tools/bonelist.cpp @@ -0,0 +1,72 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#include "cbase.h" +#include "bonelist.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CBoneList::CBoneList() +{ + m_bShouldDelete = false; + m_nBones = 0; + Q_memset( m_vecPos, 0, sizeof( m_vecPos ) ); + Q_memset( m_quatRot, 0, sizeof( m_quatRot ) ); +} + +void CBoneList::Release() +{ + if (m_bShouldDelete ) + { + delete this; + } + else + { + Warning( "Called Release() on CBoneList not allocated via Alloc() method\n" ); + } +} + +CBoneList *CBoneList::Alloc() +{ + CBoneList *newList = new CBoneList; + Assert( newList ); + if ( newList ) + { + newList->m_bShouldDelete = true; + } + return newList; +} + +CFlexList::CFlexList() +{ + m_bShouldDelete = false; + m_nNumFlexes = 0; + Q_memset( m_flexWeights, 0, sizeof( m_flexWeights ) ); +} + +void CFlexList::Release() +{ + if (m_bShouldDelete ) + { + delete this; + } + else + { + Warning( "Called Release() on CFlexList not allocated via Alloc() method\n" ); + } +} + +CFlexList *CFlexList::Alloc() +{ + CFlexList *newList = new CFlexList; + Assert( newList ); + if ( newList ) + { + newList->m_bShouldDelete = true; + } + return newList; +} \ No newline at end of file diff --git a/public/tools/bonelist.h b/public/tools/bonelist.h new file mode 100644 index 0000000..ff16803 --- /dev/null +++ b/public/tools/bonelist.h @@ -0,0 +1,61 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef BONELIST_H +#define BONELIST_H +#ifdef _WIN32 +#pragma once +#endif + +#include "studio.h" + +class CBoneList +{ +public: + + CBoneList(); + + void Release(); + + static CBoneList *Alloc(); + + unsigned int GetWriteSize() const + { + return 2 + m_nBones * ( sizeof( Vector ) + sizeof( Quaternion ) ); + } + + // The order of these data members must be maintained in order for the server + // demo system. ServerDemoPacket_BaseAnimating::GetSize() depends on this. + +private: + bool m_bShouldDelete : 1; + +public: + uint16 m_nBones : 15; + Vector m_vecPos[ MAXSTUDIOBONES ]; + Quaternion m_quatRot[ MAXSTUDIOBONES ]; +}; + +class CFlexList +{ +public: + + CFlexList(); + + void Release(); + + static CFlexList *Alloc(); + +public: + + int m_nNumFlexes; + float m_flexWeights[ MAXSTUDIOFLEXCTRL ]; + +private: + bool m_bShouldDelete; +}; + +#endif // BONELIST_H diff --git a/public/trace.h b/public/trace.h new file mode 100644 index 0000000..92429f6 --- /dev/null +++ b/public/trace.h @@ -0,0 +1,69 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TRACE_H +#define TRACE_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "mathlib/mathlib.h" + +// Note: These flags need to match the bspfile.h DISPTRI_TAG_* flags. +#define DISPSURF_FLAG_SURFACE (1<<0) +#define DISPSURF_FLAG_WALKABLE (1<<1) +#define DISPSURF_FLAG_BUILDABLE (1<<2) +#define DISPSURF_FLAG_SURFPROP1 (1<<3) +#define DISPSURF_FLAG_SURFPROP2 (1<<4) + +//============================================================================= +// Base Trace Structure +// - shared between engine/game dlls and tools (vrad) +//============================================================================= + +class CBaseTrace +{ +public: + + // Displacement flags tests. + bool IsDispSurface( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFACE ) != 0 ); } + bool IsDispSurfaceWalkable( void ) { return ( ( dispFlags & DISPSURF_FLAG_WALKABLE ) != 0 ); } + bool IsDispSurfaceBuildable( void ) { return ( ( dispFlags & DISPSURF_FLAG_BUILDABLE ) != 0 ); } + bool IsDispSurfaceProp1( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP1 ) != 0 ); } + bool IsDispSurfaceProp2( void ) { return ( ( dispFlags & DISPSURF_FLAG_SURFPROP2 ) != 0 ); } + +public: + + // these members are aligned!! + Vector startpos; // start position + Vector endpos; // final position + cplane_t plane; // surface normal at impact + + float fraction; // time completed, 1.0 = didn't hit anything + + int contents; // contents on other side of surface hit + unsigned short dispFlags; // displacement flags for marking surfaces with data + + bool allsolid; // if true, plane is not valid + bool startsolid; // if true, the initial point was in a solid area + + CBaseTrace() {} + +private: + // No copy constructors allowed + CBaseTrace(const CBaseTrace& vOther); +}; + +#endif // TRACE_H diff --git a/public/unitlib/unitlib.h b/public/unitlib/unitlib.h new file mode 100644 index 0000000..9b9b4ea --- /dev/null +++ b/public/unitlib/unitlib.h @@ -0,0 +1,269 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef UNITLIB_H +#define UNITLIB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier1/interface.h" +#include "appframework/IAppSystem.h" + + +//----------------------------------------------------------------------------- +// Usage model for the UnitTest library +// +// The general methodology here is that clients are expected to create unit +// test DLLs that statically link to the unit test DLL and which implement +// tests of various libraries. The unit test application will dynamically +// load all DLLs in a directory, which causes them to install their test cases +// into the unit test system. The application then runs through all tests and +// executes them all, displaying the results. +// +// *** NOTE: The test suites are compiled in both debug and release builds, +// even though it's expected to only be useful in debug builds. This is because +// I couldn't come up with a good way of disabling the code in release builds. +// (The only options I could come up with would still compile in the functions, +// just not install them into the unit test library, or would make it so that +// you couldn't step through the unit test code). +// +// Even though this is the case, there's no reason not to add test cases +// directly into your shipping DLLs, as long as you surround the code with +// #ifdef _DEBUG. To error check a project to make sure it's not compiling +// in unit tests in Release build, just don't link in unitlib.lib in Release. +// You can of course also put your test suites into separate DLLs. +// +// All tests inherit from the ITestCase interface. There are two major kinds +// of tests; the first is a single test case meant to run a piece of +// code and check its results match expected values using the Assert macros. +// The second kind is a test suite which is simply a list of other tests. +// +// The following classes and macros are used to easily create unit test cases +// and suites: +// +// Use DEFINE_TESTSUITE to define a particular test suite, and DEFINE_TESTCASE +// to add as many test cases as you like to that test suite, as follows: +// +// DEFINE_TESTSUITE( VectorTestSuite ) +// +// DEFINE_TESTCASE( VectorAdditionTest, VectorTestSuite ) +// { +// .. test code here .. +// } +// +// Note that the definition of the test suite can occur in a different file +// as the test case. A link error will occur if the test suite to which a +// test case is added has not been defined. +// +// To create a test case that is not part of a suite, use... +// +// DEFINE_TESTCASE_NOSUITE( VectorAdditionTest ) +// { +// .. test code here .. +// } +// +// You can also create a suite which is a child of another suite using +// +// DEFINE_SUBSUITE( VectorTestSuite, MathTestSuite ) +// +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// dll export stuff +//----------------------------------------------------------------------------- + +#ifdef UNITLIB_DLL_EXPORT +#define UNITLIB_INTERFACE DLL_EXPORT +#define UNITLIB_CLASS_INTERFACE DLL_CLASS_EXPORT +#define UNITLIB_GLOBAL_INTERFACE DLL_GLOBAL_EXPORT +#else +#define UNITLIB_INTERFACE DLL_IMPORT +#define UNITLIB_CLASS_INTERFACE DLL_CLASS_IMPORT +#define UNITLIB_GLOBAL_INTERFACE DLL_GLOBAL_IMPORT +#endif + + +//----------------------------------------------------------------------------- +// All unit test libraries can be asked for a unit test +// AppSystem to perform connection +//----------------------------------------------------------------------------- +#define UNITTEST_INTERFACE_VERSION "UnitTestV001" + + +//----------------------------------------------------------------------------- +// +// NOTE: All classes and interfaces below you shouldn't use directly. +// Use the DEFINE_TESTSUITE and DEFINE_TESTCASE macros instead. +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Test case + suite interface +//----------------------------------------------------------------------------- +class ITestCase +{ +public: + // This returns the test name + virtual char const* GetName() = 0; + + // This runs the test + virtual void RunTest() = 0; +}; + +class ITestSuite : public ITestCase +{ +public: + // Add a test to the suite... + virtual void AddTest( ITestCase* pTest ) = 0; +}; + + + +//----------------------------------------------------------------------------- +// This is the main function exported by the unit test library used by +// unit test DLLs to install their test cases into a list to be run +//----------------------------------------------------------------------------- +UNITLIB_INTERFACE void UnitTestInstallTestCase( ITestCase* pTest ); + + +//----------------------------------------------------------------------------- +// These are the methods used by the unit test running program to run all tests +//----------------------------------------------------------------------------- +UNITLIB_INTERFACE int UnitTestCount(); +UNITLIB_INTERFACE ITestCase* GetUnitTest( int i ); + + +//----------------------------------------------------------------------------- +// Helper for unit test DLLs to expose IAppSystems +//----------------------------------------------------------------------------- +#define USE_UNITTEST_APPSYSTEM( _className ) \ + static _className s_UnitTest ## _className; \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR( _className, IAppSystem, UNITTEST_INTERFACE_VERSION, s_UnitTest ## _className ); + + +//----------------------------------------------------------------------------- +// Base class for test cases +//----------------------------------------------------------------------------- +class UNITLIB_CLASS_INTERFACE CTestCase : public ITestCase +{ +public: + CTestCase( char const* pName, ITestSuite* pParent = 0 ); + ~CTestCase(); + + // Returns the test name + char const* GetName(); + +private: + char* m_pName; +}; + + +//----------------------------------------------------------------------------- +// Test suite class +//----------------------------------------------------------------------------- +class UNITLIB_CLASS_INTERFACE CTestSuite : public ITestSuite +{ +public: + CTestSuite( char const* pName, ITestSuite* pParent = 0 ); + ~CTestSuite(); + + // This runs the test + void RunTest(); + + // Add a test to the suite... + void AddTest( ITestCase* pTest ); + + // Returns the test name + char const* GetName(); + +protected: + int m_TestCount; + ITestCase** m_ppTestCases; + char* m_pName; +}; + +#define TESTSUITE_CLASS( _suite ) \ + class CTS ## _suite : public CTestSuite \ + { \ + public: \ + CTS ## _suite(); \ + }; + +#define TESTSUITE_ACCESSOR( _suite ) \ + CTS ## _suite* GetTS ## _suite() \ + { \ + static CTS ## _suite s_TS ## _suite; \ + return &s_TS ## _suite; \ + } + +#define FWD_DECLARE_TESTSUITE( _suite ) \ + class CTS ## _suite; \ + CTS ## _suite* GetTS ## _suite(); + +#define DEFINE_TESTSUITE( _suite ) \ + TESTSUITE_CLASS( _suite ) \ + TESTSUITE_ACCESSOR( _suite ) \ + CTS ## _suite::CTS ## _suite() : CTestSuite( #_suite ) {} + +#define DEFINE_SUBSUITE( _suite, _parent ) \ + TESTSUITE_CLASS( _suite ) \ + TESTSUITE_ACCESSOR( _suite ) \ + FWD_DECLARE_TESTSUITE( _parent ) \ + CTS ## _suite::CTS ## _suite() : CTestSuite( #_suite, GetTS ## _parent() ) {} + +#define TESTCASE_CLASS( _case ) \ + class CTC ## _case : public CTestCase \ + { \ + public: \ + CTC ## _case (); \ + void RunTest(); \ + }; + +#define DEFINE_TESTCASE_NOSUITE( _case ) \ + TESTCASE_CLASS( _case ) \ + CTC ## _case::CTC ## _case () : CTestCase( #_case ) {} \ + \ + CTC ## _case s_TC ## _case; \ + \ + void CTC ## _case ::RunTest() + +#define DEFINE_TESTCASE( _case, _suite ) \ + TESTCASE_CLASS( _case ) \ + FWD_DECLARE_TESTSUITE( _suite ) \ + CTC ## _case::CTC ## _case () : CTestCase( #_case, GetTS ## _suite() ) {} \ + \ + CTC ## _case s_TC ## _case; \ + \ + void CTC ## _case ::RunTest() + + +#define _Shipping_AssertMsg( _exp, _msg, _executeExp, _bFatal ) \ + do { \ + if (!(_exp)) \ + { \ + LoggingResponse_t ret = Log_Assert( "%s (%d) : %s\n", __TFILE__, __LINE__, _msg ); \ + _executeExp; \ + if ( ret == LR_DEBUGGER ) \ + { \ + if ( !ShouldUseNewAssertDialog() || DoNewAssertDialog( __TFILE__, __LINE__, _msg ) ) \ + DebuggerBreak(); \ + if ( _bFatal ) \ + _ExitOnFatalAssert( __TFILE__, __LINE__ ); \ + } \ + } \ + } while (0) + +#define Shipping_Assert( _exp ) _Shipping_AssertMsg( _exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false ) + + +#endif // UNITLIB_H diff --git a/public/vallocator.cpp b/public/vallocator.cpp new file mode 100644 index 0000000..269408d --- /dev/null +++ b/public/vallocator.cpp @@ -0,0 +1,36 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#include +#include "vallocator.h" +#include "basetypes.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +VStdAllocator g_StdAllocator; + +void* VStdAllocator::Alloc(unsigned long size) +{ + if(size) + { + void *ret = malloc(size); + return ret; + } + else + return 0; +} + +void VStdAllocator::Free(void *ptr) +{ + free(ptr); +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/public/vallocator.h b/public/vallocator.h new file mode 100644 index 0000000..980bb8b --- /dev/null +++ b/public/vallocator.h @@ -0,0 +1,84 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// These classes let you write your own allocators to be used with new and delete. +// If you have an allocator: VAllocator *pAlloc, you can call new and delete like this: +// +// ptr = VNew(pAlloc) ClassName; +// VDelete(pAlloc, ptr); +// +// Note: allocating and freeing arrays of objects will not work using VAllocators. + + + +#ifndef VALLOCATOR_H +#define VALLOCATOR_H + + +class VAllocator +{ +public: + virtual void* Alloc(unsigned long size)=0; + virtual void Free(void *ptr)=0; +}; + + +// This allocator just uses malloc and free. +class VStdAllocator : public VAllocator +{ +public: + virtual void* Alloc(unsigned long size); + virtual void Free(void *ptr); +}; +extern VStdAllocator g_StdAllocator; + + + +// Use these to allocate classes through VAllocator. +// Allocating arrays of classes is not supported. +#define VNew(pAlloc) new +#define VDelete(pAlloc, ptr) delete ptr + +// Used internally.. just makes sure we call the right operator new. +class DummyAllocatorHelper +{ +public: + int x; +}; + +inline void* operator new(size_t size, void *ptr, DummyAllocatorHelper *asdf) +{ + asdf=asdf; // compiler warning. + size=size; + return ptr; +} + +inline void operator delete(void *ptrToDelete, void *ptr, DummyAllocatorHelper *asdf) +{ + asdf=asdf; // compiler warning. + ptr=ptr; + ptrToDelete=ptrToDelete; +} + +// Use these to manually construct and destruct lists of objects. +template +inline void VAllocator_CallConstructors(T *pObjects, int count=1) +{ + for(int i=0; i < count; i++) + new(&pObjects[i], (DummyAllocatorHelper*)0) T; +} + +template +inline void VAllocator_CallDestructors(T *pObjects, int count) +{ + for(int i=0; i < count; i++) + pObjects[i].~T(); +} + +#endif + diff --git a/public/vaudio/ivaudio.h b/public/vaudio/ivaudio.h new file mode 100644 index 0000000..16bd782 --- /dev/null +++ b/public/vaudio/ivaudio.h @@ -0,0 +1,63 @@ +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVAUDIO_H +#define IVAUDIO_H +#ifdef _WIN32 +#pragma once +#endif + +class IAudioStreamEvent +{ +public: + // called by the stream to request more data + // seek the source to position "offset" + // -1 indicates previous position + // copy the data to pBuffer and return the number of bytes copied + // you may return less than bytesRequested if the end of the stream + // is encountered. + virtual int StreamRequestData( void *pBuffer, int bytesRequested, int offset ) = 0; +}; + + +class IAudioStream +{ +public: + virtual ~IAudioStream() {} + // Decode another bufferSize output bytes from the stream + // returns number of bytes decoded + virtual int Decode( void *pBuffer, unsigned int bufferSize ) = 0; + + // output sampling bits (8/16) + virtual int GetOutputBits() = 0; + // output sampling rate in Hz + virtual int GetOutputRate() = 0; + // output channels (1=mono,2=stereo) + virtual int GetOutputChannels() = 0; + + // seek + virtual unsigned int GetPosition() = 0; + + // NOTE: BUGBUG: Only supports seeking forward currently! + virtual void SetPosition( unsigned int position ) = 0; + + // reset? +}; + + +#define VAUDIO_INTERFACE_VERSION "VAudio002" +class IVAudio +{ +public: + virtual ~IVAudio() {} + + virtual IAudioStream *CreateMP3StreamDecoder( IAudioStreamEvent *pEventHandler ) = 0; + virtual void DestroyMP3StreamDecoder( IAudioStream *pDecoder ) = 0; +}; + + +#endif // IVAUDIO_H diff --git a/public/vcollide.h b/public/vcollide.h new file mode 100644 index 0000000..ae490d9 --- /dev/null +++ b/public/vcollide.h @@ -0,0 +1,27 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VCOLLIDE_H +#define VCOLLIDE_H +#ifdef _WIN32 +#pragma once +#endif + +class CPhysCollide; + +struct vcollide_t +{ + unsigned short solidCount : 15; + unsigned short isPacked : 1; + unsigned short descSize; + // VPhysicsSolids + CPhysCollide **solids; + char *pKeyValues; + void *pUserData; +}; + +#endif // VCOLLIDE_H diff --git a/public/vcollide_parse.h b/public/vcollide_parse.h new file mode 100644 index 0000000..a18a7f0 --- /dev/null +++ b/public/vcollide_parse.h @@ -0,0 +1,91 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VCOLLIDE_PARSE_H +#define VCOLLIDE_PARSE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vphysics_interface.h" + +struct solid_t +{ + char name[512]; + char parent[512]; + char surfaceprop[512]; + Vector massCenterOverride; + int index; + int contents; + objectparams_t params; +}; + +struct fluid_t +{ + int index; + char surfaceprop[512]; + + fluidparams_t params; + + fluid_t() {} + fluid_t( fluid_t const& src ) : params(src.params) + { + index = src.index; + } +}; + +struct ragdollcollisionrules_t +{ + void Defaults( IPhysics *pPhysics, IPhysicsCollisionSet *pSetIn ) + { + pCollisionSet = pSetIn; + bSelfCollisions = true; + } + int bSelfCollisions; + IPhysicsCollisionSet *pCollisionSet; +}; + +struct ragdollanimatedfriction_t +{ + float minFriction; + float maxFriction; + float timeIn; + float timeOut; + float timeHold; +}; + +//----------------------------------------------------------------------------- +// Purpose: Pass this into the parser to handle the keys that vphysics does not +// parse. +//----------------------------------------------------------------------------- +class IVPhysicsKeyHandler +{ +public: + virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue ) = 0; + virtual void SetDefaults( void *pData ) = 0; +}; + + +class IVPhysicsKeyParser +{ +public: + virtual ~IVPhysicsKeyParser() {} + + virtual const char *GetCurrentBlockName( void ) = 0; + virtual bool Finished( void ) = 0; + virtual void ParseSolid( solid_t *pSolid, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseFluid( fluid_t *pFluid, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseRagdollConstraint( constraint_ragdollparams_t *pConstraint, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseSurfaceTable( int *table, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseCustom( void *pCustom, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseVehicle( vehicleparams_t *pVehicle, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void SkipBlock( void ) = 0; + virtual void ParseCollisionRules( ragdollcollisionrules_t *pRules, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; + virtual void ParseRagdollAnimatedFriction( ragdollanimatedfriction_t *pFriction, IVPhysicsKeyHandler *unknownKeyHandler ) = 0; +}; + +#endif // VCOLLIDE_PARSE_H diff --git a/public/vgui/Cursor.h b/public/vgui/Cursor.h new file mode 100644 index 0000000..3aa5fc1 --- /dev/null +++ b/public/vgui/Cursor.h @@ -0,0 +1,45 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Holds the enumerated list of default cursors +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CURSOR_H +#define CURSOR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +enum CursorCode +{ + dc_user, + dc_none, + dc_arrow, + dc_ibeam, + dc_hourglass, + dc_waitarrow, + dc_crosshair, + dc_up, + dc_sizenwse, + dc_sizenesw, + dc_sizewe, + dc_sizens, + dc_sizeall, + dc_no, + dc_hand, + dc_blank, // don't show any custom vgui cursor, just let windows do it stuff (for HTML widget) + dc_last, +}; + +typedef unsigned long HCursor; + +} + +#endif // CURSOR_H diff --git a/public/vgui/Dar.h b/public/vgui/Dar.h new file mode 100644 index 0000000..72471ab --- /dev/null +++ b/public/vgui/Dar.h @@ -0,0 +1,134 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Holds the enumerated list of default cursors +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DAR_H +#define DAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include "tier1/utlvector.h" + +#include "tier0/memdbgon.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Simple lightweight dynamic array implementation +//----------------------------------------------------------------------------- +template class Dar : public CUtlVector< ELEMTYPE > +{ + typedef CUtlVector< ELEMTYPE > BaseClass; + +public: + Dar() + { + } + Dar(int initialCapacity) : + BaseClass( 0, initialCapacity ) + { + } + +public: + void SetCount(int count) + { + this->EnsureCount( count ); + } + int GetCount() + { + return this->Count(); + } + int AddElement(ELEMTYPE elem) + { + return this->AddToTail( elem ); + } + void MoveElementToEnd( ELEMTYPE elem ) + { + if ( this->Count() == 0 ) + return; + + // quick check to see if it's already at the end + if ( Element( this->Count() - 1 ) == elem ) + return; + + int idx = this->Find( elem ); + if ( idx == this->InvalidIndex() ) + return; + + this->Remove( idx ); + this->AddToTail( elem ); + } + // returns the index of the element in the array, -1 if not found + int FindElement(ELEMTYPE elem) + { + return this->Find( elem ); + } + bool HasElement(ELEMTYPE elem) + { + if ( FindElement(elem) != this->InvalidIndex() ) + { + return true; + } + return false; + } + int PutElement(ELEMTYPE elem) + { + int index = FindElement(elem); + if (index >= 0) + { + return index; + } + return AddElement(elem); + } + // insert element at index and move all the others down 1 + void InsertElementAt(ELEMTYPE elem,int index) + { + InsertBefore( index, elem ); + } + void SetElementAt(ELEMTYPE elem,int index) + { + this->EnsureCount( index + 1 ); + this->Element( index ) = elem; + } + void RemoveElementAt(int index) + { + this->Remove( index ); + } + + void RemoveElementsBefore(int index) + { + if ( index <= 0 ) + return; + this->RemoveMultiple( 0, index - 1 ); + } + + void RemoveElement(ELEMTYPE elem) + { + FindAndRemove( elem ); + } + + void *GetBaseData() + { + return this->Base(); + } + + void CopyFrom(Dar &dar) + { + CoypArray( dar.Base(), dar.Count() ); + } +}; + +} + +#include "tier0/memdbgoff.h" + +#endif // DAR_H diff --git a/public/vgui/IBorder.h b/public/vgui/IBorder.h new file mode 100644 index 0000000..1dd1907 --- /dev/null +++ b/public/vgui/IBorder.h @@ -0,0 +1,63 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IBORDER_H +#define IBORDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +class KeyValues; + +namespace vgui +{ + +class IScheme; + +//----------------------------------------------------------------------------- +// Purpose: Interface to panel borders +// Borders have a close relationship with panels +// They are the edges of the panel. +//----------------------------------------------------------------------------- +class IBorder +{ +public: + virtual void Paint(VPANEL panel) = 0; + virtual void Paint(int x0, int y0, int x1, int y1) = 0; + virtual void Paint(int x0, int y0, int x1, int y1, int breakSide, int breakStart, int breakStop) = 0; + virtual void SetInset(int left, int top, int right, int bottom) = 0; + virtual void GetInset(int &left, int &top, int &right, int &bottom) = 0; + virtual void ApplySchemeSettings(IScheme *pScheme, KeyValues *inResourceData) = 0; + virtual const char *GetName() = 0; + virtual void SetName(const char *name) = 0; + + enum backgroundtype_e + { + BACKGROUND_FILLED, + BACKGROUND_TEXTURED, + BACKGROUND_ROUNDEDCORNERS, + }; + virtual backgroundtype_e GetBackgroundType() = 0; + + enum sides_e + { + SIDE_LEFT = 0, + SIDE_TOP = 1, + SIDE_RIGHT = 2, + SIDE_BOTTOM = 3 + }; + + virtual bool PaintFirst( void ) = 0; +}; + +} // namespace vgui + + +#endif // IBORDER_H diff --git a/public/vgui/IClientPanel.h b/public/vgui/IClientPanel.h new file mode 100644 index 0000000..f70d785 --- /dev/null +++ b/public/vgui/IClientPanel.h @@ -0,0 +1,98 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ICLIENTPANEL_H +#define ICLIENTPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +#ifdef GetClassName +#undef GetClassName +#endif + +class KeyValues; + +namespace vgui +{ + +class Panel; +class SurfaceBase; + +enum EInterfaceID +{ + ICLIENTPANEL_STANDARD_INTERFACE = 0, +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface from vgui panels -> Client panels +// This interface cannot be changed without rebuilding all vgui projects +// Primarily this interface handles dispatching messages from core vgui to controls +// The additional functions are all their for debugging or optimization reasons +// To add to this later, use QueryInterface() to see if they support new interfaces +//----------------------------------------------------------------------------- +class IClientPanel +{ +public: + virtual VPANEL GetVPanel() = 0; + + // straight interface to Panel functions + virtual void Think() = 0; + virtual void PerformApplySchemeSettings() = 0; + virtual void PaintTraverse(bool forceRepaint, bool allowForce) = 0; + virtual void Repaint() = 0; + virtual VPANEL IsWithinTraverse(int x, int y, bool traversePopups) = 0; + virtual void GetInset(int &top, int &left, int &right, int &bottom) = 0; + virtual void GetClipRect(int &x0, int &y0, int &x1, int &y1) = 0; + virtual void OnChildAdded(VPANEL child) = 0; + virtual void OnSizeChanged(int newWide, int newTall) = 0; + + virtual void InternalFocusChanged(bool lost) = 0; + virtual bool RequestInfo(KeyValues *outputData) = 0; + virtual void RequestFocus(int direction) = 0; + virtual bool RequestFocusPrev(VPANEL existingPanel) = 0; + virtual bool RequestFocusNext(VPANEL existingPanel) = 0; + virtual void OnMessage(const KeyValues *params, VPANEL ifromPanel) = 0; + virtual VPANEL GetCurrentKeyFocus() = 0; + virtual int GetTabPosition() = 0; + + // for debugging purposes + virtual const char *GetName() = 0; + virtual const char *GetClassName() = 0; + + // get scheme handles from panels + virtual HScheme GetScheme() = 0; + // gets whether or not this panel should scale with screen resolution + virtual bool IsProportional() = 0; + // auto-deletion + virtual bool IsAutoDeleteSet() = 0; + // deletes this + virtual void DeletePanel() = 0; + + // Mouse Codes... + virtual bool HandleMouseCode( MouseCode code ) = 0; + + // interfaces + virtual void *QueryInterface(EInterfaceID id) = 0; + + // returns a pointer to the vgui controls baseclass Panel * + virtual Panel *GetPanel() = 0; + + // returns the name of the module this panel is part of + virtual const char *GetModuleName() = 0; + + virtual void OnTick() = 0; +}; + +} // namespace vgui + + +#endif // ICLIENTPANEL_H diff --git a/public/vgui/IHTML.h b/public/vgui/IHTML.h new file mode 100644 index 0000000..75f091c --- /dev/null +++ b/public/vgui/IHTML.h @@ -0,0 +1,86 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IHTML_H +#define IHTML_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: basic interface for a HTML window +//----------------------------------------------------------------------------- +class IHTML +{ +public: + // open a new page + virtual void OpenURL(const char *)=0; + + // stops the existing page from loading + virtual bool StopLoading()=0; + + // refreshes the current page + virtual bool Refresh()=0; + + // display the control + virtual bool Show(bool shown)=0; + + // return the currently opened page + virtual char *GetOpenedPage()=0; + + // called when the browser needs to be resized + virtual void OnSize(int x,int y, int w,int h)=0; + + // returns the width and height (in pixels) of the HTML page + virtual void GetHTMLSize(int &wide,int &tall)=0; + + // clear the text in an existing control + virtual void Clear()=0; + + // add text to the browser control (as a HTML formated string) + virtual void AddText(const char *text)=0; + + + enum MOUSE_STATE { UP,DOWN,MOVE }; + + virtual void OnMouse(MouseCode code,MOUSE_STATE s,int x,int y)=0; + virtual void OnChar(wchar_t unichar)=0; + virtual void OnKeyDown(KeyCode code)=0; + + virtual vgui::IImage *GetBitmap()=0; + + virtual void SetVisible( bool state ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: basic callback interface for a HTML window +//----------------------------------------------------------------------------- +class IHTMLEvents +{ +public: + // call backs for events + virtual bool OnStartURL(const char *url, const char *target, bool first)=0; + virtual void OnFinishURL(const char *url)=0; + virtual void OnProgressURL(long current, long maximum)=0; + virtual void OnSetStatusText(const char *text) =0; + virtual void OnUpdate() =0; + virtual void OnLink()=0; + virtual void OffLink()=0; +}; + +} + +#endif // IHTML_H diff --git a/public/vgui/IImage.h b/public/vgui/IImage.h new file mode 100644 index 0000000..bfce98d --- /dev/null +++ b/public/vgui/IImage.h @@ -0,0 +1,75 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IIMAGE_H +#define IIMAGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +class Color; + +namespace vgui +{ + +typedef unsigned long HTexture; + +enum iimage_rotation_t +{ + ROTATED_UNROTATED = 0, + ROTATED_CLOCKWISE_90, + ROTATED_ANTICLOCKWISE_90, + ROTATED_FLIPPED, +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface to drawing an image +//----------------------------------------------------------------------------- +class IImage +{ +public: + // Call to Paint the image + // Image will draw within the current panel context at the specified position + virtual void Paint() = 0; + + // Set the position of the image + virtual void SetPos(int x, int y) = 0; + + // Gets the size of the content + virtual void GetContentSize(int &wide, int &tall) = 0; + + // Get the size the image will actually draw in (usually defaults to the content size) + virtual void GetSize(int &wide, int &tall) = 0; + + // Sets the size of the image + virtual void SetSize(int wide, int tall) = 0; + + // Set the draw color + virtual void SetColor(Color col) = 0; + + // virtual destructor + virtual ~IImage() {} + + // not for general purpose use + // evicts the underlying image from memory if refcounts permit, otherwise ignored + // returns true if eviction occurred, otherwise false + virtual bool Evict() = 0; + + virtual int GetNumFrames() = 0; + virtual void SetFrame( int nFrame ) = 0; + virtual HTexture GetID() = 0; + + virtual void SetRotation( int iRotation ) = 0; +}; + +} // namespace vgui + + +#endif // IIMAGE_H diff --git a/public/vgui/IInput.h b/public/vgui/IInput.h new file mode 100644 index 0000000..f6265f5 --- /dev/null +++ b/public/vgui/IInput.h @@ -0,0 +1,193 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VGUI_IINPUT_H +#define VGUI_IINPUT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier1/interface.h" +#include "vgui/MouseCode.h" +#include "vgui/KeyCode.h" + +namespace vgui +{ + +class Cursor; +typedef unsigned long HCursor; + +#define VGUI_GCS_COMPREADSTR 0x0001 +#define VGUI_GCS_COMPREADATTR 0x0002 +#define VGUI_GCS_COMPREADCLAUSE 0x0004 +#define VGUI_GCS_COMPSTR 0x0008 +#define VGUI_GCS_COMPATTR 0x0010 +#define VGUI_GCS_COMPCLAUSE 0x0020 +#define VGUI_GCS_CURSORPOS 0x0080 +#define VGUI_GCS_DELTASTART 0x0100 +#define VGUI_GCS_RESULTREADSTR 0x0200 +#define VGUI_GCS_RESULTREADCLAUSE 0x0400 +#define VGUI_GCS_RESULTSTR 0x0800 +#define VGUI_GCS_RESULTCLAUSE 0x1000 +// style bit flags for WM_IME_COMPOSITION +#define VGUI_CS_INSERTCHAR 0x2000 +#define VGUI_CS_NOMOVECARET 0x4000 + +#define MESSAGE_CURSOR_POS -1 +#define MESSAGE_CURRENT_KEYFOCUS -2 + + +class IInput : public IBaseInterface +{ +public: + virtual void SetMouseFocus(VPANEL newMouseFocus) = 0; + virtual void SetMouseCapture(VPANEL panel) = 0; + + // returns the string name of a scan code + virtual void GetKeyCodeText(KeyCode code, char *buf, int buflen) = 0; + + // focus + virtual VPANEL GetFocus() = 0; + virtual VPANEL GetCalculatedFocus() = 0;// to handle cases where the focus changes inside a frame. + virtual VPANEL GetMouseOver() = 0; // returns the panel the mouse is currently over, ignoring mouse capture + + // mouse state + virtual void SetCursorPos(int x, int y) = 0; + virtual void GetCursorPos(int &x, int &y) = 0; + virtual bool WasMousePressed(MouseCode code) = 0; + virtual bool WasMouseDoublePressed(MouseCode code) = 0; + virtual bool IsMouseDown(MouseCode code) = 0; + + // cursor override + virtual void SetCursorOveride(HCursor cursor) = 0; + virtual HCursor GetCursorOveride() = 0; + + // key state + virtual bool WasMouseReleased(MouseCode code) = 0; + virtual bool WasKeyPressed(KeyCode code) = 0; + virtual bool IsKeyDown(KeyCode code) = 0; + virtual bool WasKeyTyped(KeyCode code) = 0; + virtual bool WasKeyReleased(KeyCode code) = 0; + + virtual VPANEL GetAppModalSurface() = 0; + // set the modal dialog panel. + // all events will go only to this panel and its children. + virtual void SetAppModalSurface(VPANEL panel) = 0; + // release the modal dialog panel + // do this when your modal dialog finishes. + virtual void ReleaseAppModalSurface() = 0; + + virtual void GetCursorPosition( int &x, int &y ) = 0; + + virtual void SetIMEWindow( void *hwnd ) = 0; + virtual void *GetIMEWindow() = 0; + + virtual void OnChangeIME( bool forward ) = 0; + virtual int GetCurrentIMEHandle() = 0; + virtual int GetEnglishIMEHandle() = 0; + + // Returns the Language Bar label (Chinese, Korean, Japanese, Russion, Thai, etc.) + virtual void GetIMELanguageName( wchar_t *buf, int unicodeBufferSizeInBytes ) = 0; + // Returns the short code for the language (EN, CH, KO, JP, RU, TH, etc. ). + virtual void GetIMELanguageShortCode( wchar_t *buf, int unicodeBufferSizeInBytes ) = 0; + + struct LanguageItem + { + wchar_t shortname[ 4 ]; + wchar_t menuname[ 128 ]; + int handleValue; + bool active; // true if this is the active language + }; + + struct ConversionModeItem + { + wchar_t menuname[ 128 ]; + int handleValue; + bool active; // true if this is the active conversion mode + }; + + struct SentenceModeItem + { + wchar_t menuname[ 128 ]; + int handleValue; + bool active; // true if this is the active sentence mode + }; + + // Call with NULL dest to get item count + virtual int GetIMELanguageList( LanguageItem *dest, int destcount ) = 0; + virtual int GetIMEConversionModes( ConversionModeItem *dest, int destcount ) = 0; + virtual int GetIMESentenceModes( SentenceModeItem *dest, int destcount ) = 0; + + virtual void OnChangeIMEByHandle( int handleValue ) = 0; + virtual void OnChangeIMEConversionModeByHandle( int handleValue ) = 0; + virtual void OnChangeIMESentenceModeByHandle( int handleValue ) = 0; + + virtual void OnInputLanguageChanged() = 0; + virtual void OnIMEStartComposition() = 0; + virtual void OnIMEComposition( int flags ) = 0; + virtual void OnIMEEndComposition() = 0; + + virtual void OnIMEShowCandidates() = 0; + virtual void OnIMEChangeCandidates() = 0; + virtual void OnIMECloseCandidates() = 0; + virtual void OnIMERecomputeModes() = 0; + + virtual int GetCandidateListCount() = 0; + virtual void GetCandidate( int num, wchar_t *dest, int destSizeBytes ) = 0; + virtual int GetCandidateListSelectedItem() = 0; + virtual int GetCandidateListPageSize() = 0; + virtual int GetCandidateListPageStart() = 0; + + //NOTE: We render our own candidate lists most of the time... + virtual void SetCandidateWindowPos( int x, int y ) = 0; + + virtual bool GetShouldInvertCompositionString() = 0; + virtual bool CandidateListStartsAtOne() = 0; + + virtual void SetCandidateListPageStart( int start ) = 0; + + // Passes in a keycode which allows hitting other mouse buttons w/o cancelling capture mode + virtual void SetMouseCaptureEx(VPANEL panel, MouseCode captureStartMouseCode ) = 0; + + // Because OnKeyCodeTyped uses CallParentFunction and is therefore message based, there's no way + // to know if handler actually swallowed the specified keycode. To get around this, I set a global before calling the + // kb focus OnKeyCodeTyped function and if we ever get to a Panel::OnKeyCodeTypes we know that nobody handled the message + // and in that case we can post a message to any "unhandled keycode" listeners + // This will generate an MESSAGE_FUNC_INT( "KeyCodeUnhandled" "code" code ) message to each such listener + virtual void RegisterKeyCodeUnhandledListener( VPANEL panel ) = 0; + virtual void UnregisterKeyCodeUnhandledListener( VPANEL panel ) = 0; + + // Posts unhandled message to all interested panels + virtual void OnKeyCodeUnhandled( int keyCode ) = 0; + + // Assumes subTree is a child panel of the root panel for the vgui contect + // if restrictMessagesToSubTree is true, then mouse and kb messages are only routed to the subTree and it's children and mouse/kb focus + // can only be on one of the subTree children, if a mouse click occurs outside of the subtree, and "UnhandledMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + // if restrictMessagesToSubTree is false, then mouse and kb messages are routed as normal except that they are not routed down into the subtree + // however, if a mouse click occurs outside of the subtree, and "UnhandleMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + virtual void SetModalSubTree( VPANEL subTree, VPANEL unhandledMouseClickListener, bool restrictMessagesToSubTree = true ) = 0; + virtual void ReleaseModalSubTree() = 0; + virtual VPANEL GetModalSubTree() = 0; + + // These toggle whether the modal subtree is exclusively receiving messages or conversely whether it's being excluded from receiving messages + // Sends a "ModalSubTree", state message + virtual void SetModalSubTreeReceiveMessages( bool state ) = 0; + virtual bool ShouldModalSubTreeReceiveMessages() const = 0; + + virtual VPANEL GetMouseCapture() = 0; + + virtual VPANEL GetMouseFocus() = 0; +}; + +} // namespace vgui + + +#endif // VGUI_IINPUT_H diff --git a/public/vgui/IInputInternal.h b/public/vgui/IInputInternal.h new file mode 100644 index 0000000..be5864e --- /dev/null +++ b/public/vgui/IInputInternal.h @@ -0,0 +1,90 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IINPUTINTERNAL_H +#define IINPUTINTERNAL_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +enum MouseCodeState_t +{ + BUTTON_RELEASED = 0, + BUTTON_PRESSED, + BUTTON_DOUBLECLICKED, +}; + +typedef int HInputContext; + +#define DEFAULT_INPUT_CONTEXT ((vgui::HInputContext)~0) + +class IInputInternal : public IInput +{ +public: + // processes input for a frame + virtual void RunFrame() = 0; + + virtual void UpdateMouseFocus(int x, int y) = 0; + + // called when a panel becomes invalid + virtual void PanelDeleted(VPANEL panel) = 0; + + // inputs into vgui input handling + virtual bool InternalCursorMoved(int x,int y) = 0; //expects input in surface space + virtual bool InternalMousePressed(MouseCode code) = 0; + virtual bool InternalMouseDoublePressed(MouseCode code) = 0; + virtual bool InternalMouseReleased(MouseCode code) = 0; + virtual bool InternalMouseWheeled(int delta) = 0; + virtual bool InternalKeyCodePressed(KeyCode code) = 0; + virtual void InternalKeyCodeTyped(KeyCode code) = 0; + virtual void InternalKeyTyped(wchar_t unichar) = 0; + virtual bool InternalKeyCodeReleased(KeyCode code) = 0; + + // Creates/ destroys "input" contexts, which contains information + // about which controls have mouse + key focus, for example. + virtual HInputContext CreateInputContext() = 0; + virtual void DestroyInputContext( HInputContext context ) = 0; + + // Associates a particular panel with an input context + // Associating NULL is valid; it disconnects the panel from the context + virtual void AssociatePanelWithInputContext( HInputContext context, VPANEL pRoot ) = 0; + + // Activates a particular input context, use DEFAULT_INPUT_CONTEXT + // to get the one normally used by VGUI + virtual void ActivateInputContext( HInputContext context ) = 0; + + // This method is called to post a cursor message to the current input context + virtual void PostCursorMessage() = 0; + + // Cursor position; this is the current position read from the input queue. + // We need to set it because client code may read this during Mouse Pressed + // events, etc. + virtual void UpdateCursorPosInternal( int x, int y ) = 0; + + // Called to handle explicit calls to CursorSetPos after input processing is complete + virtual void HandleExplicitSetCursor( ) = 0; + + // Updates the internal key/mouse state associated with the current input context without sending messages + virtual void SetKeyCodeState( KeyCode code, bool bPressed ) = 0; + virtual void SetMouseCodeState( MouseCode code, MouseCodeState_t state ) = 0; + virtual void UpdateButtonState( const InputEvent_t &event ) = 0; + + // Resets a particular input context, use DEFAULT_INPUT_CONTEXT + // to get the one normally used by VGUI + virtual void ResetInputContext( HInputContext context ) = 0; +}; + +} // namespace vgui + +#define VGUI_INPUTINTERNAL_INTERFACE_VERSION "VGUI_InputInternal001" + +#endif // IINPUTINTERNAL_H diff --git a/public/vgui/ILocalize.h b/public/vgui/ILocalize.h new file mode 100644 index 0000000..e1548ec --- /dev/null +++ b/public/vgui/ILocalize.h @@ -0,0 +1,30 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VGUI_ILOCALIZE_H +#define VGUI_ILOCALIZE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "localize/ilocalize.h" + +// Everything moved to localize lib; this is here for backward compat. +namespace vgui +{ +// direct references to localized strings +typedef uint32 StringIndex_t; +const uint32 INVALID_STRING_INDEX = (uint32) -1; + +abstract_class ILocalize : public ::ILocalize +{ +}; + +}; // namespace vgui + +#endif // VGUI_ILOCALIZE_H diff --git a/public/vgui/IPanel.h b/public/vgui/IPanel.h new file mode 100644 index 0000000..6bab198 --- /dev/null +++ b/public/vgui/IPanel.h @@ -0,0 +1,148 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IPANEL_H +#define IPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier1/interface.h" +#include "tier1/utlvector.h" + +#ifdef SendMessage +#undef SendMessage +#endif + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class KeyValues; +struct DmxElementUnpackStructure_t; +class CDmxElement; + +namespace vgui +{ + +class SurfacePlat; +class IClientPanel; + +//!! must be removed +class Panel; + +//----------------------------------------------------------------------------- +// Purpose: interface from Client panels -> vgui panels +//----------------------------------------------------------------------------- +class IPanel : public IBaseInterface +{ +public: + virtual void Init(VPANEL vguiPanel, IClientPanel *panel) = 0; + + // methods + virtual void SetPos(VPANEL vguiPanel, int x, int y) = 0; + virtual void GetPos(VPANEL vguiPanel, int &x, int &y) = 0; + virtual void SetSize(VPANEL vguiPanel, int wide,int tall) = 0; + virtual void GetSize(VPANEL vguiPanel, int &wide, int &tall) = 0; + virtual void SetMinimumSize(VPANEL vguiPanel, int wide, int tall) = 0; + virtual void GetMinimumSize(VPANEL vguiPanel, int &wide, int &tall) = 0; + virtual void SetZPos(VPANEL vguiPanel, int z) = 0; + virtual int GetZPos(VPANEL vguiPanel) = 0; + + virtual void GetAbsPos(VPANEL vguiPanel, int &x, int &y) = 0; + virtual void GetClipRect(VPANEL vguiPanel, int &x0, int &y0, int &x1, int &y1) = 0; + virtual void SetInset(VPANEL vguiPanel, int left, int top, int right, int bottom) = 0; + virtual void GetInset(VPANEL vguiPanel, int &left, int &top, int &right, int &bottom) = 0; + + virtual void SetVisible(VPANEL vguiPanel, bool state) = 0; + virtual bool IsVisible(VPANEL vguiPanel) = 0; + virtual void SetParent(VPANEL vguiPanel, VPANEL newParent) = 0; + virtual int GetChildCount(VPANEL vguiPanel) = 0; + virtual VPANEL GetChild(VPANEL vguiPanel, int index) = 0; + virtual CUtlVector< VPANEL > &GetChildren( VPANEL vguiPanel ) = 0; + virtual VPANEL GetParent(VPANEL vguiPanel) = 0; + virtual void MoveToFront(VPANEL vguiPanel) = 0; + virtual void MoveToBack(VPANEL vguiPanel) = 0; + virtual bool HasParent(VPANEL vguiPanel, VPANEL potentialParent) = 0; + virtual bool IsPopup(VPANEL vguiPanel) = 0; + virtual void SetPopup(VPANEL vguiPanel, bool state) = 0; + virtual bool IsFullyVisible( VPANEL vguiPanel ) = 0; + + // gets the scheme this panel uses + virtual HScheme GetScheme(VPANEL vguiPanel) = 0; + // gets whether or not this panel should scale with screen resolution + virtual bool IsProportional(VPANEL vguiPanel) = 0; + // returns true if auto-deletion flag is set + virtual bool IsAutoDeleteSet(VPANEL vguiPanel) = 0; + // deletes the Panel * associated with the vpanel + virtual void DeletePanel(VPANEL vguiPanel) = 0; + + // input interest + virtual void SetKeyBoardInputEnabled(VPANEL vguiPanel, bool state) = 0; + virtual void SetMouseInputEnabled(VPANEL vguiPanel, bool state) = 0; + virtual bool IsKeyBoardInputEnabled(VPANEL vguiPanel) = 0; + virtual bool IsMouseInputEnabled(VPANEL vguiPanel) = 0; + + // calculates the panels current position within the hierarchy + virtual void Solve(VPANEL vguiPanel) = 0; + + // gets names of the object (for debugging purposes) + virtual const char *GetName(VPANEL vguiPanel) = 0; + virtual const char *GetClassName(VPANEL vguiPanel) = 0; + + // delivers a message to the panel + virtual void SendMessage(VPANEL vguiPanel, KeyValues *params, VPANEL ifromPanel) = 0; + + // these pass through to the IClientPanel + virtual void Think(VPANEL vguiPanel) = 0; + virtual void PerformApplySchemeSettings(VPANEL vguiPanel) = 0; + virtual void PaintTraverse(VPANEL vguiPanel, bool forceRepaint, bool allowForce = true) = 0; + virtual void Repaint(VPANEL vguiPanel) = 0; + virtual VPANEL IsWithinTraverse(VPANEL vguiPanel, int x, int y, bool traversePopups) = 0; + virtual void OnChildAdded(VPANEL vguiPanel, VPANEL child) = 0; + virtual void OnSizeChanged(VPANEL vguiPanel, int newWide, int newTall) = 0; + + virtual void InternalFocusChanged(VPANEL vguiPanel, bool lost) = 0; + virtual bool RequestInfo(VPANEL vguiPanel, KeyValues *outputData) = 0; + virtual void RequestFocus(VPANEL vguiPanel, int direction = 0) = 0; + virtual bool RequestFocusPrev(VPANEL vguiPanel, VPANEL existingPanel) = 0; + virtual bool RequestFocusNext(VPANEL vguiPanel, VPANEL existingPanel) = 0; + virtual VPANEL GetCurrentKeyFocus(VPANEL vguiPanel) = 0; + virtual int GetTabPosition(VPANEL vguiPanel) = 0; + + // used by ISurface to store platform-specific data + virtual SurfacePlat *Plat(VPANEL vguiPanel) = 0; + virtual void SetPlat(VPANEL vguiPanel, SurfacePlat *Plat) = 0; + + // returns a pointer to the vgui controls baseclass Panel * + // destinationModule needs to be passed in to verify that the returned Panel * is from the same module + // it must be from the same module since Panel * vtbl may be different in each module + virtual Panel *GetPanel(VPANEL vguiPanel, const char *destinationModule) = 0; + + virtual bool IsEnabled(VPANEL vguiPanel) = 0; + virtual void SetEnabled(VPANEL vguiPanel, bool state) = 0; + + // Used by the drag/drop manager to always draw on top + virtual bool IsTopmostPopup( VPANEL vguiPanel) = 0; + virtual void SetTopmostPopup( VPANEL vguiPanel, bool state ) = 0; + + virtual void SetMessageContextId( VPANEL vguiPanel, int nContextId ) = 0; + virtual int GetMessageContextId( VPANEL vguiPanel ) = 0; + + virtual const DmxElementUnpackStructure_t *GetUnpackStructure( VPANEL vguiPanel ) const = 0; + virtual void OnUnserialized( VPANEL vguiPanel, CDmxElement *pElement ) = 0; + +// sibling pins + virtual void SetSiblingPin(VPANEL vguiPanel, VPANEL newSibling, byte iMyCornerToPin = 0, byte iSiblingCornerToPinTo = 0 ) = 0; +}; + + +} // namespace vgui + + +#endif // IPANEL_H diff --git a/public/vgui/IScheme.h b/public/vgui/IScheme.h new file mode 100644 index 0000000..7cceaed --- /dev/null +++ b/public/vgui/IScheme.h @@ -0,0 +1,140 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISCHEME_H +#define ISCHEME_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/vgui.h" +#include "tier1/interface.h" +#include "tier1/utlsymbol.h" + +class Color; +class KeyValues; +class ISchemeSurface; + +namespace vgui +{ + +typedef unsigned long HScheme; +typedef unsigned long HTexture; + +class IBorder; +class IImage; + + +//----------------------------------------------------------------------------- +// Purpose: Holds all panel rendering data +// This functionality is all wrapped in the Panel::GetScheme*() functions +//----------------------------------------------------------------------------- +class IScheme : public IBaseInterface +{ +public: +#pragma pack(1) + struct fontalias_t + { + CUtlSymbol _fontName; + CUtlSymbol _trueFontName; + unsigned short _font : 15; + unsigned short m_bProportional : 1; + }; +#pragma pack() + + struct fontrange_t + { + CUtlSymbol _fontName; + int _min; + int _max; + }; + + // gets a string from the default settings section + virtual const char *GetResourceString(const char *stringName) = 0; + + // returns a pointer to an existing border + virtual IBorder *GetBorder(const char *borderName) = 0; + + // returns a pointer to an existing font + virtual HFont GetFont(const char *fontName, bool proportional = false) = 0; + + // inverse font lookup + virtual char const *GetFontName( const HFont& font ) = 0; + + // colors + virtual Color GetColor(const char *colorName, Color defaultColor) = 0; + + // Gets at the scheme's short name + virtual const char *GetName() const = 0; + // Gets at the scheme's resource file name + virtual const char *GetFileName() const = 0; +}; + + + +class ISchemeManager: public IBaseInterface +{ +public: + // loads a scheme from a file + // first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that + virtual HScheme LoadSchemeFromFile(const char *fileName, const char *tag) = 0; + + // reloads the scheme from the file - should only be used during development + virtual void ReloadSchemes() = 0; + + // reloads scheme fonts + virtual void ReloadFonts( int inScreenTall = -1 ) = 0; + + // returns a handle to the default (first loaded) scheme + virtual HScheme GetDefaultScheme() = 0; + + // returns a handle to the scheme identified by "tag" + virtual HScheme GetScheme(const char *tag) = 0; + + // returns a pointer to an image + virtual IImage *GetImage(const char *imageName, bool hardwareFiltered) = 0; + virtual HTexture GetImageID(const char *imageName, bool hardwareFiltered) = 0; + + // This can only be called at certain times, like during paint() + // It will assert-fail if you call it at the wrong time... + + // FIXME: This interface should go away!!! It's an icky back-door + // If you're using this interface, try instead to cache off the information + // in ApplySchemeSettings + virtual IScheme *GetIScheme( HScheme scheme ) = 0; + + // unload all schemes + virtual void Shutdown( bool full = true ) = 0; + + // gets the proportional coordinates for doing screen-size independant panel layouts + // use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) + virtual int GetProportionalScaledValue( int normalizedValue) = 0; + virtual int GetProportionalNormalizedValue(int scaledValue) = 0; + + // loads a scheme from a file + // first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that + virtual HScheme LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag) = 0; + // gets the proportional coordinates for doing screen-size independant panel layouts + // use these for font, image and panel size scaling (they all use the pixel height of the display for scaling) + virtual int GetProportionalScaledValueEx( HScheme scheme, int normalizedValue ) = 0; + virtual int GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue ) = 0; + + // Returns true if image evicted, false otherwise + virtual bool DeleteImage( const char *pImageName ) = 0; + + virtual ISchemeSurface *GetSurface() = 0; + + virtual void SetLanguage( const char *pLanguage ) = 0; + virtual const char *GetLanguage() = 0; +}; + + +} // namespace vgui + + +#endif // ISCHEME_H diff --git a/public/vgui/ISurface.h b/public/vgui/ISurface.h new file mode 100644 index 0000000..b8f49b2 --- /dev/null +++ b/public/vgui/ISurface.h @@ -0,0 +1,330 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ISURFACE_H +#define ISURFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/VGUI.h" +#include "vgui/IHTML.h" // CreateHTML, PaintHTML +#include "tier1/interface.h" +#include "bitmap/imageformat.h" + +#include "appframework/IAppSystem.h" +#include "mathlib/vector2d.h" // must be before the namespace line +#include "vgui/ischemesurface.h" + +#include "IVguiMatInfo.h" + + +#ifdef PlaySound +#undef PlaySound +#endif + +class Color; + +namespace vgui +{ + +class IImage; +class Image; +class Point; + + +typedef FontHandle_t HFont; +typedef FontVertex_t Vertex_t; + + +struct IntRect +{ + int x0; + int y0; + int x1; + int y1; +}; + +struct DrawTexturedRectParms_t +{ + DrawTexturedRectParms_t() + { + s0 = t0 = 0; + s1 = t1 = 1.0f; + alpha_ul = alpha_ur = alpha_lr = alpha_ll = 255; + angle = 0; + } + + int x0; + int y0; + int x1; + int y1; + + float s0; + float t0; + float s1; + float t1; + + unsigned char alpha_ul; + unsigned char alpha_ur; + unsigned char alpha_lr; + unsigned char alpha_ll; + + float angle; +}; + +//----------------------------------------------------------------------------- +// Purpose: Wraps contextless windows system functions +//----------------------------------------------------------------------------- +class ISurface : public IAppSystem +{ +public: + // call to Shutdown surface; surface can no longer be used after this is called + virtual void Shutdown() = 0; + + // frame + virtual void RunFrame() = 0; + + // hierarchy root + virtual VPANEL GetEmbeddedPanel() = 0; + virtual void SetEmbeddedPanel( VPANEL pPanel ) = 0; + + // drawing context + virtual void PushMakeCurrent(VPANEL panel, bool useInsets) = 0; + virtual void PopMakeCurrent(VPANEL panel) = 0; + + // rendering functions + virtual void DrawSetColor(int r, int g, int b, int a) = 0; + virtual void DrawSetColor(Color col) = 0; + + virtual void DrawFilledRect(int x0, int y0, int x1, int y1) = 0; + virtual void DrawFilledRectArray( IntRect *pRects, int numRects ) = 0; + virtual void DrawOutlinedRect(int x0, int y0, int x1, int y1) = 0; + + virtual void DrawLine(int x0, int y0, int x1, int y1) = 0; + virtual void DrawPolyLine(int *px, int *py, int numPoints) = 0; + + virtual void DrawSetTextFont(HFont font) = 0; + virtual void DrawSetTextColor(int r, int g, int b, int a) = 0; + virtual void DrawSetTextColor(Color col) = 0; + virtual void DrawSetTextPos(int x, int y) = 0; + virtual void DrawGetTextPos(int& x,int& y) = 0; + virtual void DrawPrintText(const wchar_t *text, int textLen, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + virtual void DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + + virtual void DrawFlushText() = 0; // flushes any buffered text (for rendering optimizations) + virtual IHTML *CreateHTMLWindow(vgui::IHTMLEvents *events,VPANEL context)=0; + virtual void PaintHTMLWindow(vgui::IHTML *htmlwin) =0; + virtual void DeleteHTMLWindow(IHTML *htmlwin)=0; + + virtual int DrawGetTextureId( char const *filename ) = 0; + virtual bool DrawGetTextureFile( int id, char *filename, int maxlen ) = 0; + virtual void DrawSetTextureFile( int id, const char *filename, int hardwareFilter, bool forceReload ) = 0; + virtual void DrawSetTextureRGBA( int id, const unsigned char *rgba, int wide, int tall ) = 0 ; + virtual void DrawSetTexture(int id) = 0; + virtual void DrawGetTextureSize(int id, int &wide, int &tall) = 0; + virtual void DrawTexturedRect(int x0, int y0, int x1, int y1) = 0; + virtual bool IsTextureIDValid(int id) = 0; + + virtual int CreateNewTextureID( bool procedural = false ) = 0; + + virtual void GetScreenSize(int &wide, int &tall) = 0; + virtual void SetAsTopMost(VPANEL panel, bool state) = 0; + virtual void BringToFront(VPANEL panel) = 0; + virtual void SetForegroundWindow (VPANEL panel) = 0; + virtual void SetPanelVisible(VPANEL panel, bool state) = 0; + virtual void SetMinimized(VPANEL panel, bool state) = 0; + virtual bool IsMinimized(VPANEL panel) = 0; + virtual void FlashWindow(VPANEL panel, bool state) = 0; + virtual void SetTitle(VPANEL panel, const wchar_t *title) = 0; + virtual void SetAsToolBar(VPANEL panel, bool state) = 0; // removes the window's task bar entry (for context menu's, etc.) + + // windows stuff + virtual void CreatePopup(VPANEL panel, bool minimised, bool showTaskbarIcon = true, bool disabled = false, bool mouseInput = true , bool kbInput = true) = 0; + virtual void SwapBuffers(VPANEL panel) = 0; + virtual void Invalidate(VPANEL panel) = 0; + virtual void SetCursor(HCursor cursor) = 0; + virtual bool IsCursorVisible() = 0; + virtual void ApplyChanges() = 0; + virtual bool IsWithin(int x, int y) = 0; + virtual bool HasFocus() = 0; + + // returns true if the surface supports minimize & maximize capabilities + enum SurfaceFeature_t + { + ANTIALIASED_FONTS = FONT_FEATURE_ANTIALIASED_FONTS, + DROPSHADOW_FONTS = FONT_FEATURE_DROPSHADOW_FONTS, + ESCAPE_KEY = 3, + OPENING_NEW_HTML_WINDOWS = 4, + FRAME_MINIMIZE_MAXIMIZE = 5, + OUTLINE_FONTS = FONT_FEATURE_OUTLINE_FONTS, + DIRECT_HWND_RENDER = 7, + }; + virtual bool SupportsFeature( SurfaceFeature_t feature ) = 0; + + // restricts what gets drawn to one panel and it's children + // currently only works in the game + virtual void RestrictPaintToSinglePanel(VPANEL panel, bool bForceAllowNonModalSurface = false) = 0; + + // these two functions obselete, use IInput::SetAppModalSurface() instead + virtual void SetModalPanel(VPANEL ) = 0; + virtual VPANEL GetModalPanel() = 0; + + virtual void UnlockCursor() = 0; + virtual void LockCursor() = 0; + virtual void SetTranslateExtendedKeys(bool state) = 0; + virtual VPANEL GetTopmostPopup() = 0; + + // engine-only focus handling (replacing WM_FOCUS windows handling) + virtual void SetTopLevelFocus(VPANEL panel) = 0; + + // fonts + // creates an empty handle to a vgui font. windows fonts can be add to this via SetFontGlyphSet(). + virtual HFont CreateFont() = 0; + + virtual bool SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0) = 0; + + // adds a custom font file (only supports true type font files (.ttf) for now) + virtual bool AddCustomFontFile(const char *fontFileName) = 0; + + // returns the details about the font + virtual int GetFontTall(HFont font) = 0; + virtual int GetFontAscent(HFont font, wchar_t wch) = 0; + virtual bool IsFontAdditive(HFont font) = 0; + virtual void GetCharABCwide(HFont font, int ch, int &a, int &b, int &c) = 0; + virtual int GetCharacterWidth(HFont font, int ch) = 0; + virtual void GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall) = 0; + + // notify icons?!? + virtual VPANEL GetNotifyPanel() = 0; + virtual void SetNotifyIcon(VPANEL context, HTexture icon, VPANEL panelToReceiveMessages, const char *text) = 0; + + // plays a sound + virtual void PlaySound(const char *fileName) = 0; + + //!! these functions should not be accessed directly, but only through other vgui items + //!! need to move these to seperate interface + virtual int GetPopupCount() = 0; + virtual VPANEL GetPopup(int index) = 0; + virtual bool ShouldPaintChildPanel(VPANEL childPanel) = 0; + virtual bool RecreateContext(VPANEL panel) = 0; + virtual void AddPanel(VPANEL panel) = 0; + virtual void ReleasePanel(VPANEL panel) = 0; + virtual void MovePopupToFront(VPANEL panel) = 0; + virtual void MovePopupToBack(VPANEL panel) = 0; + + virtual void SolveTraverse(VPANEL panel, bool forceApplySchemeSettings = false) = 0; + virtual void PaintTraverse(VPANEL panel) = 0; + + virtual void EnableMouseCapture(VPANEL panel, bool state) = 0; + + // returns the size of the workspace + virtual void GetWorkspaceBounds(int &x, int &y, int &wide, int &tall) = 0; + + // gets the absolute coordinates of the screen (in windows space) + virtual void GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall) = 0; + + // gets the base resolution used in proportional mode + virtual void GetProportionalBase( int &width, int &height ) = 0; + + virtual void CalculateMouseVisible() = 0; + virtual bool NeedKBInput() = 0; + + virtual bool HasCursorPosFunctions() = 0; + virtual void SurfaceGetCursorPos(int &x, int &y) = 0; + virtual void SurfaceSetCursorPos(int x, int y) = 0; + + // SRC only functions!!! + virtual void DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ) = 0; + virtual void DrawOutlinedCircle(int x, int y, int radius, int segments) = 0; + virtual void DrawTexturedPolyLine( const Vertex_t *p,int n ) = 0; // (Note: this connects the first and last points). + virtual void DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ) = 0; + virtual void DrawTexturedPolygon(int n, Vertex_t *pVertice, bool bClipVertices = true ) = 0; + virtual const wchar_t *GetTitle(VPANEL panel) = 0; + virtual bool IsCursorLocked( void ) const = 0; + virtual void SetWorkspaceInsets( int left, int top, int right, int bottom ) = 0; + + // squarish comic book word bubble with pointer, rect params specify the space inside the bubble + virtual void DrawWordBubble( int x0, int y0, int x1, int y1, int nBorderThickness, Color rgbaBackground, Color rgbaBorder, + bool bPointer = false, int nPointerX = 0, int nPointerY = 0, int nPointerBaseThickness = 16 ) = 0; + + // Lower level char drawing code, call DrawGet then pass in info to DrawRender + virtual bool DrawGetUnicodeCharRenderInfo( wchar_t ch, FontCharRenderInfo& info ) = 0; + virtual void DrawRenderCharFromInfo( const FontCharRenderInfo& info ) = 0; + + // global alpha setting functions + // affect all subsequent draw calls - shouldn't normally be used directly, only in Panel::PaintTraverse() + virtual void DrawSetAlphaMultiplier( float alpha /* [0..1] */ ) = 0; + virtual float DrawGetAlphaMultiplier() = 0; + + // web browser + virtual void SetAllowHTMLJavaScript( bool state ) = 0; + + // video mode changing + virtual void OnScreenSizeChanged( int nOldWidth, int nOldHeight ) = 0; + + virtual vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID = 0 ) = 0; + + // create IVguiMatInfo object ( IMaterial wrapper in VguiMatSurface, NULL in CWin32Surface ) + virtual IVguiMatInfo *DrawGetTextureMatInfoFactory( int id ) = 0; + + virtual void PaintTraverseEx(VPANEL panel, bool paintPopups = false ) = 0; + + virtual float GetZPos() const = 0; + + // From the Xbox + virtual void SetPanelForInput( VPANEL vpanel ) = 0; + virtual void DrawFilledRectFastFade( int x0, int y0, int x1, int y1, int fadeStartPt, int fadeEndPt, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) = 0; + virtual void DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) = 0; + virtual void DrawSetTextureRGBAEx(int id, const unsigned char *rgba, int wide, int tall, ImageFormat imageFormat ) = 0; + virtual void DrawSetTextScale(float sx, float sy) = 0; + virtual bool SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags) = 0; + // adds a bitmap font file + virtual bool AddBitmapFontFile(const char *fontFileName) = 0; + // sets a symbol for the bitmap font + virtual void SetBitmapFontName( const char *pName, const char *pFontFilename ) = 0; + // gets the bitmap font filename + virtual const char *GetBitmapFontName( const char *pName ) = 0; + virtual void ClearTemporaryFontCache( void ) = 0; + + virtual IImage *GetIconImageForFullPath( char const *pFullPath ) = 0; + virtual void DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + virtual void PrecacheFontCharacters(HFont font, wchar_t *pCharacters) = 0; + // Console-only. Get the string to use for the current video mode for layout files. + virtual const char *GetResolutionKey( void ) const = 0; + + virtual const char *GetFontName( HFont font ) = 0; + + virtual bool ForceScreenSizeOverride( bool bState, int wide, int tall ) = 0; + // LocalToScreen, ParentLocalToScreen fixups for explicit PaintTraverse calls on Panels not at 0, 0 position + virtual bool ForceScreenPosOffset( bool bState, int x, int y ) = 0; + virtual void OffsetAbsPos( int &x, int &y ) = 0; + + virtual void SetAbsPosForContext( int id, int x, int y ) = 0; + virtual void GetAbsPosForContext( int id, int &x, int& y ) = 0; + + // Causes fonts to get reloaded, etc. + virtual void ResetFontCaches() = 0; + + virtual bool IsScreenSizeOverrideActive( void ) = 0; + virtual bool IsScreenPosOverrideActive( void ) = 0; + + virtual void DestroyTextureID( int id ) = 0; + + virtual int GetTextureNumFrames( int id ) = 0; + virtual void DrawSetTextureFrame( int id, int nFrame, unsigned int *pFrameCache ) = 0; + + virtual void GetClipRect( int &x0, int &y0, int &x1, int &y1 ) = 0; + virtual void SetClipRect( int x0, int y0, int x1, int y1 ) = 0; + + virtual void DrawTexturedRectEx( DrawTexturedRectParms_t *pDrawParms ) = 0; +}; + +} + +#endif // ISURFACE_H diff --git a/public/vgui/ISurfaceV30.h b/public/vgui/ISurfaceV30.h new file mode 100644 index 0000000..bfb1314 --- /dev/null +++ b/public/vgui/ISurfaceV30.h @@ -0,0 +1,375 @@ +//========= Copyright © 1996-2003, Valve LLC, All rights reserved. ============ +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef ISURFACE_V30_H +#define ISURFACE_V30_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include // CreateHTML, PaintHTML +#include "interface.h" +#include "IVguiMatInfo.h" + +#include "appframework/IAppSystem.h" +#include "bitmap/ImageFormat.h" +#include "Vector2D.h" // must be before the namespace line + +#ifdef CreateFont +#undef CreateFont +#endif + +#ifdef PlaySound +#undef PlaySound +#endif + +class Color; + +namespace vgui +{ + + class Image; + class Point; + + // handles + typedef unsigned long HCursor; + typedef unsigned long HTexture; + typedef unsigned long HFont; +} + + + +namespace SurfaceV30 +{ + + //SRC only defines + + + struct Vertex_t + { + Vertex_t() {} + Vertex_t( const Vector2D &pos, const Vector2D &coord = Vector2D( 0, 0 ) ) + { + m_Position = pos; + m_TexCoord = coord; + } + void Init( const Vector2D &pos, const Vector2D &coord = Vector2D( 0, 0 ) ) + { + m_Position = pos; + m_TexCoord = coord; + } + + Vector2D m_Position; + Vector2D m_TexCoord; + }; + + + enum FontDrawType_t + { + // Use the "additive" value from the scheme file + FONT_DRAW_DEFAULT = 0, + + // Overrides + FONT_DRAW_NONADDITIVE, + FONT_DRAW_ADDITIVE, + + FONT_DRAW_TYPE_COUNT = 2, + }; + + + // Refactor these two + struct CharRenderInfo + { + // In: + FontDrawType_t drawType; + wchar_t ch; + + // Out + bool valid; + + // In/Out (true by default) + bool shouldclip; + // Text pos + int x, y; + // Top left and bottom right + Vertex_t verts[ 2 ]; + int textureId; + int abcA; + int abcB; + int abcC; + int fontTall; + vgui::HFont currentFont; + }; + + + struct IntRect + { + int x0; + int y0; + int x1; + int y1; + }; + + + //----------------------------------------------------------------------------- + // Purpose: Wraps contextless windows system functions + //----------------------------------------------------------------------------- + class ISurface : public IAppSystem + { + public: + // call to Shutdown surface; surface can no longer be used after this is called + virtual void Shutdown() = 0; + + // frame + virtual void RunFrame() = 0; + + // hierarchy root + virtual vgui::VPANEL GetEmbeddedPanel() = 0; + virtual void SetEmbeddedPanel( vgui::VPANEL pPanel ) = 0; + + // drawing context + virtual void PushMakeCurrent(vgui::VPANEL panel, bool useInsets) = 0; + virtual void PopMakeCurrent(vgui::VPANEL panel) = 0; + + // rendering functions + virtual void DrawSetColor(int r, int g, int b, int a) = 0; + virtual void DrawSetColor(Color col) = 0; + + virtual void DrawFilledRect(int x0, int y0, int x1, int y1) = 0; + virtual void DrawFilledRectArray( IntRect *pRects, int numRects ) = 0; + virtual void DrawOutlinedRect(int x0, int y0, int x1, int y1) = 0; + + virtual void DrawLine(int x0, int y0, int x1, int y1) = 0; + virtual void DrawPolyLine(int *px, int *py, int numPoints) = 0; + + virtual void DrawSetTextFont(vgui::HFont font) = 0; + virtual void DrawSetTextColor(int r, int g, int b, int a) = 0; + virtual void DrawSetTextColor(Color col) = 0; + virtual void DrawSetTextPos(int x, int y) = 0; + virtual void DrawGetTextPos(int& x,int& y) = 0; + virtual void DrawPrintText(const wchar_t *text, int textLen, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + virtual void DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + + virtual void DrawFlushText() = 0; // flushes any buffered text (for rendering optimizations) + virtual vgui::IHTML *CreateHTMLWindow(vgui::IHTMLEvents *events,vgui::VPANEL context)=0; + virtual void PaintHTMLWindow(vgui::IHTML *htmlwin) =0; + virtual void DeleteHTMLWindow(vgui::IHTML *htmlwin)=0; + + virtual int DrawGetTextureId( char const *filename ) = 0; + virtual bool DrawGetTextureFile(int id, char *filename, int maxlen ) = 0; + virtual void DrawSetTextureFile(int id, const char *filename, int hardwareFilter, bool forceReload) = 0; + virtual void DrawSetTextureRGBA(int id, const unsigned char *rgba, int wide, int tall, int hardwareFilter, bool forceReload)=0; + virtual void DrawSetTexture(int id) = 0; + virtual void DrawGetTextureSize(int id, int &wide, int &tall) = 0; + virtual void DrawTexturedRect(int x0, int y0, int x1, int y1) = 0; + virtual bool IsTextureIDValid(int id) = 0; + + virtual int CreateNewTextureID( bool procedural = false ) = 0; +#ifdef _XBOX + virtual void DestroyTextureID( int id ) = 0; + virtual bool IsCachedForRendering( int id, bool bSyncWait ) = 0; + virtual void CopyFrontBufferToBackBuffer() = 0; + virtual void UncacheUnusedMaterials() = 0; +#endif + + virtual void GetScreenSize(int &wide, int &tall) = 0; + virtual void SetAsTopMost(vgui::VPANEL panel, bool state) = 0; + virtual void BringToFront(vgui::VPANEL panel) = 0; + virtual void SetForegroundWindow (vgui::VPANEL panel) = 0; + virtual void SetPanelVisible(vgui::VPANEL panel, bool state) = 0; + virtual void SetMinimized(vgui::VPANEL panel, bool state) = 0; + virtual bool IsMinimized(vgui::VPANEL panel) = 0; + virtual void FlashWindow(vgui::VPANEL panel, bool state) = 0; + virtual void SetTitle(vgui::VPANEL panel, const wchar_t *title) = 0; + virtual void SetAsToolBar(vgui::VPANEL panel, bool state) = 0; // removes the window's task bar entry (for context menu's, etc.) + + // windows stuff + virtual void CreatePopup(vgui::VPANEL panel, bool minimised, bool showTaskbarIcon = true, bool disabled = false, bool mouseInput = true , bool kbInput = true) = 0; + virtual void SwapBuffers(vgui::VPANEL panel) = 0; + virtual void Invalidate(vgui::VPANEL panel) = 0; + virtual void SetCursor(vgui::HCursor cursor) = 0; + virtual bool IsCursorVisible() = 0; + virtual void ApplyChanges() = 0; + virtual bool IsWithin(int x, int y) = 0; + virtual bool HasFocus() = 0; + + // returns true if the surface supports minimize & maximize capabilities + enum SurfaceFeature_e + { + ANTIALIASED_FONTS = 1, + DROPSHADOW_FONTS = 2, + ESCAPE_KEY = 3, + OPENING_NEW_HTML_WINDOWS = 4, + FRAME_MINIMIZE_MAXIMIZE = 5, + OUTLINE_FONTS = 6, + DIRECT_HWND_RENDER = 7, + }; + virtual bool SupportsFeature(SurfaceFeature_e feature) = 0; + + // restricts what gets drawn to one panel and it's children + // currently only works in the game + virtual void RestrictPaintToSinglePanel(vgui::VPANEL panel, bool bForceAllowNonModalSurface = false) = 0; + + // these two functions obselete, use IInput::SetAppModalSurface() instead + virtual void SetModalPanel(vgui::VPANEL ) = 0; + virtual vgui::VPANEL GetModalPanel() = 0; + + virtual void UnlockCursor() = 0; + virtual void LockCursor() = 0; + virtual void SetTranslateExtendedKeys(bool state) = 0; + virtual vgui::VPANEL GetTopmostPopup() = 0; + + // engine-only focus handling (replacing WM_FOCUS windows handling) + virtual void SetTopLevelFocus(vgui::VPANEL panel) = 0; + + // fonts + // creates an empty handle to a vgui font. windows fonts can be add to this via SetFontGlyphSet(). + virtual vgui::HFont CreateFont() = 0; + + // adds to the font + enum EFontFlags + { + FONTFLAG_NONE, + FONTFLAG_ITALIC = 0x001, + FONTFLAG_UNDERLINE = 0x002, + FONTFLAG_STRIKEOUT = 0x004, + FONTFLAG_SYMBOL = 0x008, + FONTFLAG_ANTIALIAS = 0x010, + FONTFLAG_GAUSSIANBLUR = 0x020, + FONTFLAG_ROTARY = 0x040, + FONTFLAG_DROPSHADOW = 0x080, + FONTFLAG_ADDITIVE = 0x100, + FONTFLAG_OUTLINE = 0x200, + FONTFLAG_CUSTOM = 0x400, // custom generated font - never fall back to asian compatibility mode + FONTFLAG_BITMAP = 0x800, // compiled bitmap font - no fallbacks + }; + + virtual bool SetFontGlyphSet(vgui::HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) = 0; + + // adds a custom font file (only supports true type font files (.ttf) for now) + virtual bool AddCustomFontFile(const char *fontFileName) = 0; + + // returns the details about the font + virtual int GetFontTall(vgui::HFont font) = 0; + virtual int GetFontAscent(vgui::HFont font, wchar_t wch) = 0; + virtual bool IsFontAdditive(vgui::HFont font) = 0; + virtual void GetCharABCwide(vgui::HFont font, int ch, int &a, int &b, int &c) = 0; + virtual int GetCharacterWidth(vgui::HFont font, int ch) = 0; + virtual void GetTextSize(vgui::HFont font, const wchar_t *text, int &wide, int &tall) = 0; + + // notify icons?!? + virtual vgui::VPANEL GetNotifyPanel() = 0; + virtual void SetNotifyIcon(vgui::VPANEL context, vgui::HTexture icon, vgui::VPANEL panelToReceiveMessages, const char *text) = 0; + + // plays a sound + virtual void PlaySound(const char *fileName) = 0; + + //!! these functions should not be accessed directly, but only through other vgui items + //!! need to move these to seperate interface + virtual int GetPopupCount() = 0; + virtual vgui::VPANEL GetPopup(int index) = 0; + virtual bool ShouldPaintChildPanel(vgui::VPANEL childPanel) = 0; + virtual bool RecreateContext(vgui::VPANEL panel) = 0; + virtual void AddPanel(vgui::VPANEL panel) = 0; + virtual void ReleasePanel(vgui::VPANEL panel) = 0; + virtual void MovePopupToFront(vgui::VPANEL panel) = 0; + virtual void MovePopupToBack(vgui::VPANEL panel) = 0; + + virtual void SolveTraverse(vgui::VPANEL panel, bool forceApplySchemeSettings = false) = 0; + virtual void PaintTraverse(vgui::VPANEL panel) = 0; + + virtual void EnableMouseCapture(vgui::VPANEL panel, bool state) = 0; + + // returns the size of the workspace + virtual void GetWorkspaceBounds(int &x, int &y, int &wide, int &tall) = 0; + + // gets the absolute coordinates of the screen (in windows space) + virtual void GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall) = 0; + + // gets the base resolution used in proportional mode + virtual void GetProportionalBase( int &width, int &height ) = 0; + + virtual void CalculateMouseVisible() = 0; + virtual bool NeedKBInput() = 0; + + virtual bool HasCursorPosFunctions() = 0; + virtual void SurfaceGetCursorPos(int &x, int &y) = 0; + virtual void SurfaceSetCursorPos(int x, int y) = 0; + + + // SRC only functions!!! + virtual void DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ) = 0; + virtual void DrawOutlinedCircle(int x, int y, int radius, int segments) = 0; + virtual void DrawTexturedPolyLine( const Vertex_t *p,int n ) = 0; // (Note: this connects the first and last points). + virtual void DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ) = 0; + virtual void DrawTexturedPolygon(int n, Vertex_t *pVertices) = 0; + virtual const wchar_t *GetTitle(vgui::VPANEL panel) = 0; + virtual bool IsCursorLocked( void ) const = 0; + virtual void SetWorkspaceInsets( int left, int top, int right, int bottom ) = 0; + + // Lower level char drawing code, call DrawGet then pass in info to DrawRender + virtual bool DrawGetUnicodeCharRenderInfo( wchar_t ch, CharRenderInfo& info ) = 0; + virtual void DrawRenderCharFromInfo( const CharRenderInfo& info ) = 0; + + // global alpha setting functions + // affect all subsequent draw calls - shouldn't normally be used directly, only in Panel::PaintTraverse() + virtual void DrawSetAlphaMultiplier( float alpha /* [0..1] */ ) = 0; + virtual float DrawGetAlphaMultiplier() = 0; + + // web browser + virtual void SetAllowHTMLJavaScript( bool state ) = 0; + + // video mode changing + virtual void OnScreenSizeChanged( int nOldWidth, int nOldHeight ) = 0; +#if !defined( _XBOX ) + virtual vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID = 0 ) = 0; +#endif + // create IVguiMatInfo object ( IMaterial wrapper in VguiMatSurface, NULL in CWin32Surface ) + virtual IVguiMatInfo *DrawGetTextureMatInfoFactory( int id ) = 0; + + virtual void PaintTraverseEx(vgui::VPANEL panel, bool paintPopups = false ) = 0; + + virtual float GetZPos() const = 0; + + // From the Xbox + virtual void SetPanelForInput( vgui::VPANEL vpanel ) = 0; + virtual void DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) = 0; + virtual void DrawSetTextureRGBAEx(int id, const unsigned char *rgba, int wide, int tall, ImageFormat imageFormat ) = 0; + virtual void DrawSetTextScale(float sx, float sy) = 0; + virtual bool SetBitmapFontGlyphSet(vgui::HFont font, const char *windowsFontName, float scalex, float scaley, int flags) = 0; + // adds a bitmap font file + virtual bool AddBitmapFontFile(const char *fontFileName) = 0; + // sets a symbol for the bitmap font + virtual void SetBitmapFontName( const char *pName, const char *pFontFilename ) = 0; + // gets the bitmap font filename + virtual const char *GetBitmapFontName( const char *pName ) = 0; + + virtual vgui::IImage *GetIconImageForFullPath( char const *pFullPath ) = 0; + virtual void DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType = FONT_DRAW_DEFAULT ) = 0; + }; + +} // end namespace + +//----------------------------------------------------------------------------- +// FIXME: This works around using scoped interfaces w/ EXPOSE_SINGLE_INTERFACE +//----------------------------------------------------------------------------- +class ISurfaceV30 : public SurfaceV30::ISurface +{ +public: +}; + + +#define VGUI_SURFACE_INTERFACE_VERSION_30 "VGUI_Surface030" + +#endif // ISURFACE_V30_H \ No newline at end of file diff --git a/public/vgui/ISystem.h b/public/vgui/ISystem.h new file mode 100644 index 0000000..ea4ccad --- /dev/null +++ b/public/vgui/ISystem.h @@ -0,0 +1,132 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ISYSTEM_H +#define ISYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include "tier1/keyvalues.h" +#include +#include + +#ifdef PlaySound +#undef PlaySound +#endif + + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Wraps contextless windows system functions +//----------------------------------------------------------------------------- +class ISystem : public IBaseInterface +{ +public: + // call when done with ISystem to clean up any memory allocation + virtual void Shutdown() = 0; + + // called every frame + virtual void RunFrame() = 0; + + // use this with the "open" command to launch web browsers/explorer windows, eg. ShellExecute("open", "www.valvesoftware.com") + virtual void ShellExecute(const char *command, const char *file) = 0; + + // returns the time at the start of the frame, in seconds + virtual double GetFrameTime() = 0; + + // returns the current time, in seconds + virtual double GetCurrentTime() = 0; + + // returns the current time, in milliseconds + virtual long GetTimeMillis() = 0; + + // clipboard access + virtual int GetClipboardTextCount() = 0; + virtual void SetClipboardText(const char *text, int textLen) = 0; + virtual void SetClipboardText(const wchar_t *text, int textLen) = 0; + virtual int GetClipboardText(int offset, char *buf, int bufLen) = 0; + virtual int GetClipboardText(int offset, wchar_t *buf, int bufLen) = 0; + + // windows registry + virtual bool SetRegistryString(const char *key, const char *value) = 0; + virtual bool GetRegistryString(const char *key, char *value, int valueLen) = 0; + virtual bool SetRegistryInteger(const char *key, int value) = 0; + virtual bool GetRegistryInteger(const char *key, int &value) = 0; + + // user config + virtual KeyValues *GetUserConfigFileData(const char *dialogName, int dialogID) = 0; + // sets the name of the config file to save/restore from. Settings are loaded immediately. + virtual void SetUserConfigFile(const char *fileName, const char *pathName) = 0; + // saves all the current settings to the user config file + virtual void SaveUserConfigFile() = 0; + + // sets the watch on global computer use + // returns true if supported + virtual bool SetWatchForComputerUse(bool state) = 0; + // returns the time, in seconds, since the last computer use. + virtual double GetTimeSinceLastUse() = 0; + + // Get a string containing the available drives + // If the function succeeds, the return value is the length, in characters, + // of the strings copied to the buffer, + // not including the terminating null character. + virtual int GetAvailableDrives(char *buf, int bufLen) = 0; + + // exe command line options accessors + // returns whether or not the parameter was on the command line + virtual bool CommandLineParamExists(const char *paramName) = 0; + + // returns the full command line, including the exe name + virtual const char *GetFullCommandLine() = 0; + + // Convert a windows virtual key code to a VGUI key code. + virtual KeyCode KeyCode_VirtualKeyToVGUI( int keyCode ) = 0; + + // returns the current local time and date + // fills in every field that a pointer is given to it for + virtual bool GetCurrentTimeAndDate(int *year, int *month, int *dayOfWeek, int *day, int *hour, int *minute, int *second) = 0; + + // returns the amount of available disk space, in bytes, on the drive + // path can be any path, drive letter is stripped out + virtual double GetFreeDiskSpace(const char *path) = 0; + + // shortcut (.lnk) modification functions + virtual bool CreateShortcut(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory, const char *iconFile) = 0; + virtual bool GetShortcutTarget(const char *linkFileName, char *targetPath, char *arguments, int destBufferSizes) = 0; + virtual bool ModifyShortcutTarget(const char *linkFileName, const char *targetPath, const char *arguments, const char *workingDirectory) = 0; + + // gets the string following a command line param + //!! move this function up on changing interface version number + virtual bool GetCommandLineParamValue(const char *paramName, char *value, int valueBufferSize) = 0; + + // recursively deletes a registry key and all it's subkeys + //!! move this function next to other registry function on changing interface version number + virtual bool DeleteRegistryKey(const char *keyName) = 0; + + virtual const char *GetDesktopFolderPath() = 0; + + // use this with the "open" command to launch web browsers/explorer windows, eg. ShellExecute("open", "www.valvesoftware.com") + virtual void ShellExecuteEx( const char *command, const char *file, const char *pParams ) = 0; + + // Copy a portion of the application client area to the clipboard + // (x1,y1) specifies the top-left corner of the client rect to copy + // (x2,y2) specifies the bottom-right corner of the client rect to copy + // Requires: x2 > x1 && y2 > y1 + // Dimensions of the copied rectangle are (x2 - x1) x (y2 - y1) + // Pixel at (x1,y1) is copied, pixels at column x2 and row y2 are *not* copied + virtual void SetClipboardImage( void *pWnd, int x1, int y1, int x2, int y2 ) = 0; +}; + +} + + +#endif // ISYSTEM_H diff --git a/public/vgui/IVGui.h b/public/vgui/IVGui.h new file mode 100644 index 0000000..11dc22e --- /dev/null +++ b/public/vgui/IVGui.h @@ -0,0 +1,113 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef IVGUI_H +#define IVGUI_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" +#include + +#include "appframework/IAppSystem.h" + +class KeyValues; + +namespace vgui +{ + +// safe handle to a panel - can be converted to and from a VPANEL +typedef unsigned long HPanel; +typedef int HContext; + +enum +{ + DEFAULT_VGUI_CONTEXT = ((vgui::HContext)~0) +}; + +// safe handle to a panel - can be converted to and from a VPANEL +typedef unsigned long HPanel; + +class IMessageContextIdHandler +{ +public: + virtual void SetMessageContextId( int id ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface to core vgui components +//----------------------------------------------------------------------------- +class IVGui : public IAppSystem +{ +public: + // activates vgui message pump + virtual void Start() = 0; + + // signals vgui to Stop running + virtual void Stop() = 0; + + // returns true if vgui is current active + virtual bool IsRunning() = 0; + + // runs a single frame of vgui + virtual void RunFrame() = 0; + + // broadcasts "ShutdownRequest" "id" message to all top-level panels in the app + virtual void ShutdownMessage(unsigned int shutdownID) = 0; + + // panel allocation + virtual VPANEL AllocPanel() = 0; + virtual void FreePanel(VPANEL panel) = 0; + + // debugging prints + virtual void DPrintf(const char *format, ...) = 0; + virtual void DPrintf2(const char *format, ...) = 0; + virtual void SpewAllActivePanelNames() = 0; + + // safe-pointer handle methods + virtual HPanel PanelToHandle(VPANEL panel) = 0; + virtual VPANEL HandleToPanel(HPanel index) = 0; + virtual void MarkPanelForDeletion(VPANEL panel) = 0; + + // makes panel receive a 'Tick' message every frame (~50ms, depending on sleep times/framerate) + // panel is automatically removed from tick signal list when it's deleted + virtual void AddTickSignal(VPANEL panel, int intervalMilliseconds = 0 ) = 0; + virtual void RemoveTickSignal(VPANEL panel) = 0; + + // message sending + virtual void PostMessage(VPANEL target, KeyValues *params, VPANEL from, float delaySeconds = 0.0f) = 0; + + // Creates/ destroys vgui contexts, which contains information + // about which controls have mouse + key focus, for example. + virtual HContext CreateContext() = 0; + virtual void DestroyContext( HContext context ) = 0; + + // Associates a particular panel with a vgui context + // Associating NULL is valid; it disconnects the panel from the context + virtual void AssociatePanelWithContext( HContext context, VPANEL pRoot ) = 0; + + // Activates a particular context, use DEFAULT_VGUI_CONTEXT + // to get the one normally used by VGUI + virtual void ActivateContext( HContext context ) = 0; + + // whether to sleep each frame or not, true = sleep + virtual void SetSleep( bool state) = 0; + + // data accessor for above + virtual bool GetShouldVGuiControlSleep() = 0; + + // Resets a particular context, use DEFAULT_VGUI_CONTEXT + // to get the one normally used by VGUI + virtual void ResetContext( HContext context ) = 0; +}; + +}; + + +#endif // IVGUI_H diff --git a/public/vgui/IVguiMatInfo.h b/public/vgui/IVguiMatInfo.h new file mode 100644 index 0000000..0ce4621 --- /dev/null +++ b/public/vgui/IVguiMatInfo.h @@ -0,0 +1,25 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVGUIMATINFO_H +#define IVGUIMATINFO_H + +#include "IVguiMatInfoVar.h" + +// wrapper for IMaterial +class IVguiMatInfo +{ +public: + // make sure to delete the returned object after use! + virtual IVguiMatInfoVar* FindVarFactory ( const char *varName, bool *found ) = 0; + + virtual int GetNumAnimationFrames ( ) = 0; + + // todo: if you need to add more IMaterial functions add them here +}; + +#endif //IVGUIMATINFO_H diff --git a/public/vgui/IVguiMatInfoVar.h b/public/vgui/IVguiMatInfoVar.h new file mode 100644 index 0000000..63f3826 --- /dev/null +++ b/public/vgui/IVguiMatInfoVar.h @@ -0,0 +1,22 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IVGUIMATINFOVAR_H +#define IVGUIMATINFOVAR_H + + +// wrapper for IMaterialVar +class IVguiMatInfoVar +{ +public: + virtual int GetIntValue ( void ) const = 0; + virtual void SetIntValue ( int val ) = 0; + + // todo: if you need to add more IMaterialVar functions add them here +}; + +#endif //IVGUIMATINFOVAR_H diff --git a/public/vgui/KeyCode.h b/public/vgui/KeyCode.h new file mode 100644 index 0000000..61025e7 --- /dev/null +++ b/public/vgui/KeyCode.h @@ -0,0 +1,24 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: this is a map for virtual key codes +// virtual key codes may exist outside this range for other languages +// NOTE: Button codes also contain mouse codes, but we won't worry about that +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef KEYCODE_H +#define KEYCODE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "inputsystem/ButtonCode.h" + +namespace vgui +{ +typedef ButtonCode_t KeyCode; +} + +#endif // KEYCODE_H diff --git a/public/vgui/MouseCode.h b/public/vgui/MouseCode.h new file mode 100644 index 0000000..2dfc10d --- /dev/null +++ b/public/vgui/MouseCode.h @@ -0,0 +1,23 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: names mouse button inputs +// NOTE: Button codes also contain key codes, but we won't worry about that +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef MOUSECODE_H +#define MOUSECODE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "inputsystem/ButtonCode.h" + +namespace vgui +{ +typedef ButtonCode_t MouseCode; +} + +#endif // MOUSECODE_H diff --git a/public/vgui/Point.h b/public/vgui/Point.h new file mode 100644 index 0000000..ed87fb4 --- /dev/null +++ b/public/vgui/Point.h @@ -0,0 +1,61 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef POINT_H +#define POINT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Basic handler for a Points in 2 dimensions +// This class is fully inline +//----------------------------------------------------------------------------- +class Point +{ +public: + // constructors + Point() + { + SetPoint(0, 0); + } + Point(int x,int y) + { + SetPoint(x,y); + } + + void SetPoint(int x1, int y1) + { + x=x1; + y=y1; + } + + void GetPoint(int &x1, int &y1) const + { + x1 = x; + y1 = y; + + } + + bool operator == (Point &rhs) const + { + return (x == rhs.x && y == rhs.y); + } + +private: + int x, y; +}; + +} // namespace vgui + +#endif // POINT_H diff --git a/public/vgui/VGUI.h b/public/vgui/VGUI.h new file mode 100644 index 0000000..949c7a0 --- /dev/null +++ b/public/vgui/VGUI.h @@ -0,0 +1,68 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Basic header for using vgui +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VGUI_H +#define VGUI_H + +#ifdef _WIN32 +#pragma once +#endif + +#define null 0L + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#pragma warning( disable: 4786 ) // disables 'identifier truncated in browser information' warning +#pragma warning( disable: 4355 ) // disables 'this' : used in base member initializer list +#pragma warning( disable: 4097 ) // warning C4097: typedef-name 'BaseClass' used as synonym for class-name +#pragma warning( disable: 4514 ) // warning C4514: 'Color::Color' : unreferenced inline function has been removed +#pragma warning( disable: 4100 ) // warning C4100: 'code' : unreferenced formal parameter +#pragma warning( disable: 4127 ) // warning C4127: conditional expression is constant + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifndef _WCHAR_T_DEFINED +// DAL - wchar_t is a built in define in gcc 3.2 with a size of 4 bytes +#if !defined( __x86_64__ ) && !defined( __WCHAR_TYPE__ ) +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif +#endif + +// do this in GOLDSRC only!!! +//#define Assert assert + +namespace vgui +{ +// handle to an internal vgui panel +// this is the only handle to a panel that is valid across dll boundaries +typedef unsigned int VPANEL; + +// handles to vgui objects +// NULL values signify an invalid value +typedef unsigned long HScheme; +typedef unsigned long HTexture; +typedef unsigned long HCursor; +typedef unsigned long HPanel; +const HPanel INVALID_PANEL = 0xffffffff; +typedef unsigned long HFont; +const HFont INVALID_FONT = 0; // the value of an invalid font handle +} + +#include "tier1/strtools.h" + + +#endif // VGUI_H diff --git a/public/vgui/ischemesurface.h b/public/vgui/ischemesurface.h new file mode 100644 index 0000000..987cd96 --- /dev/null +++ b/public/vgui/ischemesurface.h @@ -0,0 +1,84 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ISCHEMESURFACE_H +#define ISCHEMESURFACE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_surfacelib/ifontsurface.h" +#include "appframework/IAppSystem.h" + +class IMaterial; + +//----------------------------------------------------------------------------- +// Purpose: Wraps functions that the scheme system needs access to. +//----------------------------------------------------------------------------- +abstract_class ISchemeSurface : public IAppSystem +{ +public: + virtual InitReturnVal_t Init() = 0; + + // creates an empty handle to a vgui font. windows fonts can be add to this via SetFontGlyphSet(). + virtual FontHandle_t CreateFont() = 0; + virtual bool SetFontGlyphSet( FontHandle_t font, const char *windowsFontName, int tall, int weight, + int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0) = 0; + virtual const char *GetFontName( FontHandle_t font ) = 0; + virtual int GetFontTall( FontHandle_t font ) = 0; + virtual int GetCharacterWidth( FontHandle_t font, int ch ) = 0; + virtual void GetCharABCwide( FontHandle_t font, int ch, int &a, int &b, int &c ) = 0; + + // Custom font support + virtual bool AddCustomFontFile(const char *fontFileName) = 0; + + // Bitmap Font support + virtual bool AddBitmapFontFile(const char *fontFileName) = 0; + virtual void SetBitmapFontName( const char *pName, const char *pFontFilename ) = 0; + virtual const char *GetBitmapFontName( const char *pName ) = 0; + virtual bool SetBitmapFontGlyphSet( FontHandle_t font, const char *windowsFontName, float scalex, + float scaley, int flags) = 0; + + virtual bool SupportsFontFeature( FontFeature_t feature ) = 0; + + // Console-only. Get the string to use for the current video mode for layout files. + virtual const char *GetResolutionKey( void ) const = 0; + + virtual void GetScreenSize(int &wide, int &tall) = 0; + + // Gets the base resolution used in proportional mode + virtual void GetProportionalBase( int &width, int &height ) = 0; + + // Functions used by game ui editor. + virtual IMaterial *GetTextureForChar( FontCharRenderInfo &info, float **texCoords ) + { + Assert(0); + return NULL; + } + + // Returns an array of the 4 render positions for the character. + virtual bool GetUnicodeCharRenderPositions( FontCharRenderInfo& info, Vector2D *pPositions ) + { + Assert(0); + return false; + } + + virtual IMaterial *GetMaterial( int textureId ) + { + Assert(0); + return false; + } + + + + virtual void SetLanguage( const char *pLanguage ) = 0; + virtual const char *GetLanguage() = 0; +}; + + +#endif // ISCHEMESURFACE_H diff --git a/public/vgui_controls/AnalogBar.h b/public/vgui_controls/AnalogBar.h new file mode 100644 index 0000000..cd1a956 --- /dev/null +++ b/public/vgui_controls/AnalogBar.h @@ -0,0 +1,109 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ANALOGBAR_H +#define ANALOGBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Status bar that visually displays discrete analogValue in the form +// of a segmented strip +//----------------------------------------------------------------------------- +class AnalogBar : public Panel +{ + DECLARE_CLASS_SIMPLE( AnalogBar, Panel ); + +public: + AnalogBar(Panel *parent, const char *panelName); + ~AnalogBar(); + + // 'analogValue' is in the range [0.0f, 1.0f] + MESSAGE_FUNC_FLOAT( SetAnalogValue, "SetAnalogValue", analogValue ); + float GetAnalogValue(); + virtual void SetSegmentInfo( int gap, int width ); + + // utility function for calculating a time remaining string + static bool ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentAnalogValue, float lastAnalogValueUpdateTime, bool addRemainingSuffix); + + void SetBarInset( int pixels ); + int GetBarInset( void ); + + virtual void ApplySettings(KeyValues *inResourceData); + virtual void GetSettings(KeyValues *outResourceData); + virtual const char *GetDescription(); + + // returns the number of segment blocks drawn + int GetDrawnSegmentCount(); + int GetTotalSegmentCount(); + + enum AnalogValueDir_e + { + PROGRESS_EAST, + PROGRESS_WEST, + PROGRESS_NORTH, + PROGRESS_SOUTH + }; + + int GetAnalogValueDirection() const { return m_iAnalogValueDirection; } + void SetAnalogValueDirection( int val ) { m_iAnalogValueDirection = val; } + + void SetHomeValue( float val ) { m_fHomeValue = val; } + + const Color& GetHomeColor( void ) { return m_HomeColor; } + void SetHomeColor( const Color &color ) { m_HomeColor = color; } + +protected: + virtual void Paint(); + void PaintSegment( int &x, int &y, int tall, int wide, Color color, bool bHome ); + virtual void PaintBackground(); + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_PARAMS( OnDialogVariablesChanged, "DialogVariables", dialogVariables ); + /* CUSTOM MESSAGE HANDLING + "SetAnalogValue" + input: "analogValue" - float value of the analogValue to set + */ + +protected: + int m_iAnalogValueDirection; + float _analogValue; + +private: + int _segmentCount; + int _segmentGap; + int _segmentWide; + int m_iBarInset; + char *m_pszDialogVar; + + float m_fHomeValue; + Color m_HomeColor; +}; + +//----------------------------------------------------------------------------- +// Purpose: Non-segmented analogValue bar +//----------------------------------------------------------------------------- +class ContinuousAnalogBar : public AnalogBar +{ + DECLARE_CLASS_SIMPLE( ContinuousAnalogBar, AnalogBar ); + +public: + ContinuousAnalogBar(Panel *parent, const char *panelName); + + virtual void Paint(); +}; + +} // namespace vgui + +#endif // ANALOGBAR_H diff --git a/public/vgui_controls/AnimatingImagePanel.h b/public/vgui_controls/AnimatingImagePanel.h new file mode 100644 index 0000000..ccd862f --- /dev/null +++ b/public/vgui_controls/AnimatingImagePanel.h @@ -0,0 +1,66 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ANIMATINGIMAGEPANEL_H +#define ANIMATINGIMAGEPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Animating image +//----------------------------------------------------------------------------- +class AnimatingImagePanel : public Panel +{ + DECLARE_CLASS_SIMPLE( AnimatingImagePanel, Panel ); + +public: + AnimatingImagePanel(Panel *parent, const char *name); + + // Add an image to the end of the list of animations + // image - pointer to the image to add to the end of the list + virtual void AddImage(IImage *image); + + // Load a set of animations by name. + // baseName - The name of the animations without their frame number or file extension, (e.g. c1.tga becomes just c.) + // framecount: number of frames in the animation + virtual void LoadAnimation(const char *baseName, int frameCount); + + virtual void StartAnimation(); + virtual void StopAnimation(); + virtual void ResetAnimation(int frame = 0); + +protected: + virtual void OnTick(); + virtual void PerformLayout(); + virtual void PaintBackground(); + + virtual void GetSettings(KeyValues *outResourceData); + virtual void ApplySettings(KeyValues *inResourceData); + virtual const char *GetDescription(); + +private: + int m_iCurrentImage; + int m_iNextFrameTime; + int m_iFrameTimeMillis; + CUtlVector m_Frames; + char *m_pImageName; + bool m_bAnimating; + bool m_bFiltered; + bool m_bScaleImage; +}; + +}; // namespace vgui + +#endif // ANIMATINGIMAGEPANEL_H diff --git a/public/vgui_controls/AnimationController.h b/public/vgui_controls/AnimationController.h new file mode 100644 index 0000000..785b921 --- /dev/null +++ b/public/vgui_controls/AnimationController.h @@ -0,0 +1,267 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ANIMATIONCONTROLLER_H +#define ANIMATIONCONTROLLER_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +#include "tier1/utlsymbol.h" +#include "tier1/utlvector.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Handles controlling panel animation +// It is never visible, but needs to be a panel so that can receive messages +//----------------------------------------------------------------------------- +class AnimationController : public Panel +{ + DECLARE_CLASS_SIMPLE( AnimationController, Panel ); + +public: + AnimationController(Panel *parent); + ~AnimationController(); + + // sets which script file to use + bool SetScriptFile( VPANEL sizingPanel, const char *fileName, bool wipeAll = false ); + + // reloads the currently set script file + void ReloadScriptFile(); + + // runs a frame of animation (time is passed in so slow motion, etc. works) + void UpdateAnimations( float curtime ); + + // plays all animations to completion instantly + void RunAllAnimationsToCompletion(); + + // stops all animations + void CancelAllAnimations(); + + // starts an animation sequence script + bool StartAnimationSequence(const char *sequenceName); + bool StartAnimationSequence(Panel *pWithinParent, const char *sequenceName); + + // gets the length of an animation sequence, in seconds + float GetAnimationSequenceLength(const char *sequenceName); + + // sets that the script file should be reloaded each time a script is ran + // used for development + void SetAutoReloadScript(bool state); + + enum Interpolators_e + { + INTERPOLATOR_LINEAR, + INTERPOLATOR_ACCEL, + INTERPOLATOR_DEACCEL, + INTERPOLATOR_PULSE, + INTERPOLATOR_FLICKER, + INTERPOLATOR_SIMPLESPLINE, // ease in / out + }; + + // runs the specific animation command (doesn't use script file at all) + void RunAnimationCommand(vgui::Panel *panel, const char *variable, float targetValue, float startDelaySeconds, float durationSeconds, Interpolators_e interpolator, float animParameter = 0 ); + void RunAnimationCommand(vgui::Panel *panel, const char *variable, Color targetValue, float startDelaySeconds, float durationSeconds, Interpolators_e interpolator, float animParameter = 0 ); + +private: + bool UpdateScreenSize(); + + bool LoadScriptFile(const char *fileName); + bool ParseScriptFile(char *pMem, int length); + + void UpdatePostedMessages(bool bRunToCompletion); + void UpdateActiveAnimations(bool bRunToCompletion); + + bool m_bAutoReloadScript; + float m_flCurrentTime; + + enum AnimCommandType_e + { + CMD_ANIMATE, + CMD_RUNEVENT, + CMD_STOPEVENT, + CMD_STOPANIMATION, + CMD_STOPPANELANIMATIONS, + CMD_SETFONT, + CMD_SETTEXTURE, + CMD_SETSTRING, + }; + + enum RelativeAlignment + { + a_northwest = 0, + a_north, + a_northeast, + a_west, + a_center, + a_east, + a_southwest, + a_south, + a_southeast, + }; + + struct RelativeAlignmentLookup + { + RelativeAlignment align; + char const *name; + }; + + // a single animatable value + // some var types use 1, 2, 3 or all 4 of the values + struct Value_t + { + float a, b, c, d; + }; + + struct AnimAlign_t + { + // For Position, Xpos, YPos + bool relativePosition; + UtlSymId_t alignPanel; + RelativeAlignment alignment; + }; + + // info for the animate command + struct AnimCmdAnimate_t + { + UtlSymId_t panel; + UtlSymId_t variable; + Value_t target; + int interpolationFunction; + float interpolationParameter; + float startTime; + float duration; + + AnimAlign_t align; + + }; + + // info for the run event command + struct AnimCmdEvent_t + { + UtlSymId_t event; + UtlSymId_t variable; + UtlSymId_t variable2; + float timeDelay; + }; + + // holds a single command from an animation sequence + struct AnimCommand_t + { + AnimCommandType_e commandType; + union + { + AnimCmdAnimate_t animate; + AnimCmdEvent_t runEvent; + } cmdData; + }; + + // holds a full sequence + struct AnimSequence_t + { + UtlSymId_t name; + float duration; + CUtlVector cmdList; + }; + + // holds the list of sequences + CUtlVector m_Sequences; + + // list of active animations + struct ActiveAnimation_t + { + PHandle panel; + UtlSymId_t seqName; // the sequence this belongs to + UtlSymId_t variable; + bool started; + Value_t startValue; + Value_t endValue; + int interpolator; + float interpolatorParam; + float startTime; + float endTime; + + AnimAlign_t align; + }; + CUtlVector m_ActiveAnimations; + + // posted messages + struct PostedMessage_t + { + AnimCommandType_e commandType; + UtlSymId_t seqName; + UtlSymId_t event; + UtlSymId_t variable; + UtlSymId_t variable2; + float startTime; + PHandle parent; + }; + CUtlVector m_PostedMessages; + + struct RanEvent_t + { + UtlSymId_t event; + Panel *pParent; + + bool operator==( const RanEvent_t &other ) const + { + return ( event == other.event && pParent == other.pParent ); + } + }; + + // variable names + UtlSymId_t m_sPosition, m_sSize, m_sFgColor, m_sBgColor; + UtlSymId_t m_sXPos, m_sYPos, m_sWide, m_sTall; + + // file name + CUtlVector m_ScriptFileNames; + + // runs a single line of the script + void ExecAnimationCommand(UtlSymId_t seqName, AnimCommand_t &animCommand, Panel *pWithinParent); + // removes all commands belonging to a script + void RemoveQueuedAnimationCommands(UtlSymId_t seqName, vgui::Panel *panel = NULL); + // removes an existing instance of a command + void RemoveQueuedAnimationByType(vgui::Panel *panel, UtlSymId_t variable, UtlSymId_t sequenceToIgnore); + + // handlers + void StartCmd_Animate(UtlSymId_t seqName, AnimCmdAnimate_t &cmd, Panel *pWithinParent); + void StartCmd_Animate(Panel *panel, UtlSymId_t seqName, AnimCmdAnimate_t &cmd); + void RunCmd_RunEvent(PostedMessage_t &msg); + void RunCmd_StopEvent(PostedMessage_t &msg); + void RunCmd_StopPanelAnimations(PostedMessage_t &msg); + void RunCmd_StopAnimation(PostedMessage_t &msg); + void RunCmd_SetFont(PostedMessage_t &msg); + void RunCmd_SetTexture(PostedMessage_t &msg); + void RunCmd_SetString(PostedMessage_t &msg); + + // value access + Value_t GetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var); + void SetValue(ActiveAnimation_t& anim, Panel *panel, UtlSymId_t var, Value_t &value); + + // interpolation + Value_t GetInterpolatedValue(int interpolator, float interpolatorParam, float currentTime, float startTime, float endTime, Value_t &startValue, Value_t &endValue); + + void SetupPosition( AnimCmdAnimate_t& cmd, float *output, char const *psz, int screendimension ); + static RelativeAlignment LookupAlignment( char const *token ); + static RelativeAlignmentLookup g_AlignmentLookup[]; + + int GetRelativeOffset( AnimAlign_t& cmd, bool xcoord ); + + VPANEL m_hSizePanel; + int m_nScreenBounds[ 4 ]; +}; + +// singleton accessor for use only by other vgui_controls +extern AnimationController *GetAnimationController(); + +} // namespace vgui + +#endif // ANIMATIONCONTROLLER_H diff --git a/public/vgui_controls/BitmapImagePanel.h b/public/vgui_controls/BitmapImagePanel.h new file mode 100644 index 0000000..1a8814c --- /dev/null +++ b/public/vgui_controls/BitmapImagePanel.h @@ -0,0 +1,57 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BITMAPIMAGEPANEL_H +#define BITMAPIMAGEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui { + +class CBitmapImagePanel : public vgui::Panel +{ +public: + CBitmapImagePanel( vgui::Panel *parent, char const *panelName, char const *filename = NULL ); + ~CBitmapImagePanel(); + + virtual void PaintBackground(); + + virtual void setTexture( char const *filename, bool hardwareFiltered = true ); + + void setImageColor( Color color ) { m_bgColor = color; } + + // Set how the image aligns itself within the panel + virtual void SetContentAlignment(Label::Alignment alignment); + +protected: + virtual void GetSettings(KeyValues *outResourceData); + virtual void ApplySettings(KeyValues *inResourceData); + virtual const char *GetDescription(); + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void PaintBorder(); + +private: + typedef vgui::Panel BaseClass; + + virtual void ComputeImagePosition(int &x, int &y, int &w, int &h); + Label::Alignment m_contentAlignment; + + bool m_preserveAspectRatio; + bool m_hardwareFiltered; + + IImage *m_pImage; + Color m_bgColor; + char *m_pszImageName; + char *m_pszColorName; +}; + +}; + +#endif // BITMAPIMAGEPANEL_H diff --git a/public/vgui_controls/BuildGroup.h b/public/vgui_controls/BuildGroup.h new file mode 100644 index 0000000..2db5ca4 --- /dev/null +++ b/public/vgui_controls/BuildGroup.h @@ -0,0 +1,185 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VGUI_BUILDGROUP_H +#define VGUI_BUILDGROUP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" +#include "tier1/utlsymbol.h" +#include +#include +#include +#include +#include +#include +#include "tier1/utlhandletable.h" + +class KeyValues; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: a BuildGroup is a list of panels contained in a window (the contextPanel) +// Members of this group are viewable and editable in Build Mode, via the BuildModeDialog wizard +//----------------------------------------------------------------------------- +class BuildGroup +{ + DECLARE_HANDLES( BuildGroup, 20 ); + +public: + BuildGroup(Panel *parentPanel, Panel *contextPanel); + ~BuildGroup(); + + // Toggle build mode on/off + virtual void SetEnabled(bool state); + + // Check if buildgroup is enabled + virtual bool IsEnabled(); + + // Return the currently selected panel + virtual Panel *GetCurrentPanel(); + + // Load the control settings from file + virtual void LoadControlSettings(const char *controlResourceName, const char *pathID = NULL, KeyValues *pPreloadedKeyValues = NULL, KeyValues *pConditions = NULL); + + // Reload the control settings from file + void ReloadControlSettings(); + + // changes which control settings are currently loaded + void ChangeControlSettingsFile(const char *controlResourceName); + + // Save control settings from file, using the same resource + // name as what LoadControlSettings() was called with + virtual bool SaveControlSettings(); + + // Serialize settings from a resource data container + virtual void ApplySettings(KeyValues *resourceData); + + // Serialize settings to a resource data container + virtual void GetSettings(KeyValues *resourceData); + + // Remove all objects in the current control group + virtual void RemoveSettings(); + + // Get a new unique fieldname for a new control + void GetNewFieldName(char *newFieldName, int newFieldNameSize, Panel *newPanel); + + // Check if a control name is already taken + Panel *FieldNameTaken(const char *fieldName); + + // Add a new control (via the BuildModeDialog) + Panel *NewControl( KeyValues *controlKeys, int x=0, int y=0); + Panel *NewControl( const char *name, int x=0, int y=0); + + // Set the panel from which the build group gets all it's object creation information + virtual void SetContextPanel(Panel *contextPanel); + + //Get the panel that build group is pointed at. + virtual Panel *GetContextPanel(); + + // Get the list of panels in the buildgroup + CUtlVector *GetPanelList(); + + // Get the resource file name used + virtual const char *GetResourceName(void) { return m_pResourceName; } + + void PanelAdded(Panel* panel); + void PanelRemoved(Panel *panel); + + virtual bool MousePressed(MouseCode code,Panel* panel); + virtual bool MouseReleased(MouseCode code,Panel* panel); + + // Get the list of panels that are currently selected + virtual CUtlVector *GetControlGroup(); + + // Toggle ruler display on/off + virtual void ToggleRulerDisplay(); + + // Toggle visibility of ruler number labels + virtual void SetRulerLabelsVisible(bool state); + + // Check if ruler display is activated + virtual bool HasRulersOn(); + + // Draw Rulers on screen + virtual void DrawRulers(); + + // registers that a control settings file may be loaded + // use when the dialog may have multiple states and the editor will need to be able to switch between them + void RegisterControlSettingsFile(const char *controlResourceName, const char *pathID = NULL); + + // iterator for registered files + int GetRegisteredControlSettingsFileCount(); + const char *GetRegisteredControlSettingsFileByIndex(int index); + + // dialog variables + KeyValues *GetDialogVariables(); + + // conditional keys for selectively reading keyvalues + void ProcessConditionalKeys( KeyValues *pDat, KeyValues *pConditions ); + +protected: + virtual bool CursorMoved(int x, int y, Panel *panel); + virtual bool MouseDoublePressed(MouseCode code, Panel *panel); + virtual bool KeyCodeTyped(KeyCode code, Panel *panel); + virtual bool KeyCodeReleased(KeyCode code, Panel *panel ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual bool KeyTyped( wchar_t unichar, Panel *panel ); + + virtual HCursor GetCursor(Panel *panel); + +private: + void ApplySnap(Panel* panel); + Panel *CreateBuildDialog(); + void ActivateBuildDialog(); + void DeleteAllControlsCreatedByControlSettingsFile(); + + bool _enabled; + int _snapX; + int _snapY; + HCursor _cursor_sizenwse; + HCursor _cursor_sizenesw; + HCursor _cursor_sizewe; + HCursor _cursor_sizens; + HCursor _cursor_sizeall; + bool _dragging; + MouseCode _dragMouseCode; + int _dragStartPanelPos[2]; + int _dragStartCursorPos[2]; + int _dragStartPanelSize[ 2 ]; + Panel * _currentPanel; + CUtlVector _panelDar; + char *m_pResourceName; + char *m_pResourcePathID; + PHandle m_hBuildDialog; + Panel *m_pBuildContext; // the panel from which the build dialog gets all the information it needs + Panel *m_pParentPanel; // panel to create new controls in + CUtlVector _controlGroup; // grouped panels + CUtlVector _groupDeltaX; // x offsets of panels in group from the selected panel + CUtlVector _groupDeltaY; // y offsets of panels in group from the selected panel + Label *_rulerNumber[4]; // 4 numbers to label rulers with + bool _showRulers; // toggles ruler display + CUtlVector m_RegisteredControlSettingsFiles; + + friend class Panel; +}; + + +//----------------------------------------------------------------------------- +// Handle to a build group +//----------------------------------------------------------------------------- +typedef CUtlHandle HBuildGroup; + + +} // namespace vgui + +#endif // VGUI_BUILDGROUP_H diff --git a/public/vgui_controls/BuildModeDialog.h b/public/vgui_controls/BuildModeDialog.h new file mode 100644 index 0000000..29859a2 --- /dev/null +++ b/public/vgui_controls/BuildModeDialog.h @@ -0,0 +1,136 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef BUILDMODEDIALOG_H +#define BUILDMODEDIALOG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +struct PanelItem_t; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Dialog for use in build mode editing +//----------------------------------------------------------------------------- +class BuildModeDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( BuildModeDialog, Frame ); + +public: + BuildModeDialog( BuildGroup *buildGroup ); + ~BuildModeDialog(); + + // Set the current control to edit + MESSAGE_FUNC_PTR( SetActiveControl, "SetActiveControl", panelPtr ); + + // Update the current control with the current resource settings. + MESSAGE_FUNC_PTR( UpdateControlData, "UpdateControlData", panel ); + + // Store the current settings of all panels in the build group. + virtual KeyValues *StoreSettings(); + + // Store the current settings of the current panel + MESSAGE_FUNC( StoreUndoSettings, "StoreUndo" ); + + /* CUSTOM MESSAGE HANDLING + "SetActiveControl" + input: "PanelPtr" - panel to set active control to edit to + */ + + MESSAGE_FUNC( OnShowNewControlMenu, "ShowNewControlMenu" ); + +protected: + virtual void PerformLayout(); + virtual void OnClose(); + virtual void OnCommand( const char *command ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual bool IsBuildGroupEnabled(); + +private: + void CreateControls(); + + void OnKeyCodeTyped(KeyCode code); + MESSAGE_FUNC( ApplyDataToControls, "ApplyDataToControls" ); + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + MESSAGE_FUNC( OnDeletePanel, "DeletePanel" ); + void ExitBuildMode(); + Panel *OnNewControl(const char *name, int x = 0, int y = 0); + MESSAGE_FUNC( DoUndo, "Undo" ); + MESSAGE_FUNC( DoCopy, "Copy" ); + MESSAGE_FUNC( DoPaste, "Paste" ); + MESSAGE_FUNC( EnableSaveButton, "EnableSaveButton" ); + void RevertToSaved(); + void ShowHelp(); + MESSAGE_FUNC( ShutdownBuildMode, "Close" ); + MESSAGE_FUNC( OnPanelMoved, "PanelMoved" ); + MESSAGE_FUNC( OnTextKillFocus, "TextKillFocus" ); + MESSAGE_FUNC( OnReloadLocalization, "ReloadLocalization" ); + MESSAGE_FUNC_CHARPTR( OnCreateNewControl, "CreateNewControl", text ); + + MESSAGE_FUNC_CHARPTR( OnSetClipboardText, "SetClipboardText", text ); + + MESSAGE_FUNC_INT( OnChangeChild, "OnChangeChild", direction ); + + Panel *m_pCurrentPanel; + BuildGroup *m_pBuildGroup; + Label *m_pStatusLabel; + ComboBox *m_pFileSelectionCombo; + Divider *m_pDivider; + + class PanelList; + PanelList *m_pPanelList; + + Button *m_pSaveButton; + Button *m_pApplyButton; + Button *m_pExitButton; + Button *m_pDeleteButton; + Button *m_pReloadLocalization; + MenuButton *m_pVarsButton; + + bool _autoUpdate; + + ComboBox *m_pAddNewControlCombo; // combo box for adding new controls + KeyValues *_undoSettings; // settings for the Undo command + KeyValues *_copySettings; // settings for the Copy/Paste command + char _copyClassName[255]; + int m_nClick[ 2 ]; + + void RemoveAllControls( void ); + void UpdateEditControl(PanelItem_t &panelItem, const char *datstring); + + enum { + TYPE_STRING, + TYPE_INTEGER, + TYPE_COLOR, + TYPE_ALIGNMENT, + TYPE_AUTORESIZE, + TYPE_CORNER, + TYPE_LOCALIZEDSTRING, + }; + + vgui::DHANDLE< Menu > m_hContextMenu; + + ComboBox *m_pEditableParents; + ComboBox *m_pEditableChildren; + + Button *m_pNextChild; + Button *m_pPrevChild; + + friend class PanelList; +}; + +} // namespace vgui + + +#endif // BUILDMODEDIALOG_H + diff --git a/public/vgui_controls/Button.h b/public/vgui_controls/Button.h new file mode 100644 index 0000000..7118627 --- /dev/null +++ b/public/vgui_controls/Button.h @@ -0,0 +1,229 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef BUTTON_H +#define BUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include "vgui/MouseCode.h" +#include "dmxloader/dmxelement.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class Button : public Label +{ + DECLARE_CLASS_SIMPLE( Button, Label ); + DECLARE_DMXELEMENT_UNPACK_NAMESPACE(vgui); + +public: + // You can optionally pass in the panel to send the click message to and the name of the command to send to that panel. + Button(Panel *parent, const char *panelName, const char *text, Panel *pActionSignalTarget=NULL, const char *pCmd=NULL); + Button(Panel *parent, const char *panelName, const wchar_t *text, Panel *pActionSignalTarget=NULL, const char *pCmd=NULL); + ~Button(); +private: + void Init(); +public: + // Set armed state. + virtual void SetArmed(bool state); + // Check armed state + virtual bool IsArmed( void ); + + // Check depressed state + virtual bool IsDepressed(); + // Set button force depressed state. + virtual void ForceDepressed(bool state); + // Set button depressed state with respect to the force depressed state. + virtual void RecalculateDepressedState( void ); + + // Set button selected state. + virtual void SetSelected(bool state); + // Check selected state + virtual bool IsSelected( void ); + + virtual void SetBlink(bool state); + virtual bool IsBlinking( void ); + + //Set whether or not the button captures all mouse input when depressed. + virtual void SetUseCaptureMouse( bool state ); + // Check if mouse capture is enabled. + virtual bool IsUseCaptureMouseEnabled( void ); + + // Activate a button click. + MESSAGE_FUNC( DoClick, "PressButton" ); + MESSAGE_FUNC( OnHotkey, "Hotkey" ) + { + DoClick(); + } + + // Set button to be mouse clickable or not. + virtual void SetMouseClickEnabled( MouseCode code, bool state ); + // Check if button is mouse clickable + virtual bool IsMouseClickEnabled( MouseCode code ); + // sets the how this button activates + enum ActivationType_t + { + ACTIVATE_ONPRESSEDANDRELEASED, // normal button behaviour + ACTIVATE_ONPRESSED, // menu buttons, toggle buttons + ACTIVATE_ONRELEASED, // menu items + }; + virtual void SetButtonActivationType(ActivationType_t activationType); + + // Message targets that the button has been pressed + virtual void FireActionSignal( void ); + // Perform graphical layout of button + virtual void PerformLayout(); + + virtual bool RequestInfo(KeyValues *data); + + virtual bool CanBeDefaultButton(void); + + // Set this button to be the button that is accessed by default when the user hits ENTER or SPACE + MESSAGE_FUNC_INT( SetAsDefaultButton, "SetAsDefaultButton", state ); + // Set this button to be the button that is currently accessed by default when the user hits ENTER or SPACE + MESSAGE_FUNC_INT( SetAsCurrentDefaultButton, "SetAsCurrentDefaultButton", state ); + + // Respond when key focus is received + virtual void OnSetFocus(); + // Respond when focus is killed + virtual void OnKillFocus(); + + // Set button border attribute enabled, controls display of button. + virtual void SetButtonBorderEnabled( bool state ); + + // Set default button colors. + virtual void SetDefaultColor(Color fgColor, Color bgColor); + // Set armed button colors + virtual void SetArmedColor(Color fgColor, Color bgColor); + // Set depressed button colors + virtual void SetDepressedColor(Color fgColor, Color bgColor); + // Set blink button color + virtual void SetBlinkColor(Color fgColor); + + // Get button foreground color + virtual Color GetButtonFgColor(); + // Get button background color + virtual Color GetButtonBgColor(); + + // Set default button border attributes. + virtual void SetDefaultBorder(IBorder *border); + // Set depressed button border attributes. + virtual void SetDepressedBorder(IBorder *border); + // Set key focused button border attributes. + virtual void SetKeyFocusBorder(IBorder *border); + + // Set the command to send when the button is pressed + // Set the panel to send the command to with AddActionSignalTarget() + virtual void SetCommand( const char *command ); + // Set the message to send when the button is pressed + virtual void SetCommand( KeyValues *message ); + + // sound handling + void SetArmedSound(const char *sound); + void SetDepressedSound(const char *sound); + void SetReleasedSound(const char *sound); + + /* CUSTOM MESSAGE HANDLING + "PressButton" - makes the button act as if it had just been pressed by the user (just like DoClick()) + input: none + */ + + virtual void OnCursorEntered(); + virtual void OnCursorExited(); + virtual void SizeToContents(); + + virtual KeyValues *GetCommand(); + + bool IsDrawingFocusBox(); + void DrawFocusBox( bool bEnable ); + + bool ShouldPaint(){ return _paint; } + void SetShouldPaint( bool paint ){ _paint = paint; } + + virtual void NavigateTo(); + virtual void NavigateFrom(); + + virtual void ApplySettings( KeyValues *inResourceData ); + + virtual void GetSizerMinimumSize(int &wide, int &tall); + +protected: + virtual void DrawFocusBorder(int tx0, int ty0, int tx1, int ty1); + + // Paint button on screen + virtual void Paint(void); + // Get button border attributes. + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_INT( OnSetState, "SetState", state ); + + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnKeyCodePressed(KeyCode code); + virtual void OnKeyCodeReleased(KeyCode code); + + // Get control settings for editing + virtual void GetSettings( KeyValues *outResourceData ); + virtual const char *GetDescription( void ); + + KeyValues *GetActionMessage(); + void PlayButtonReleasedSound(); + +private: + enum ButtonFlags_t + { + ARMED = 0x0001, + DEPRESSED = 0x0002, + FORCE_DEPRESSED = 0x0004, + BUTTON_BORDER_ENABLED = 0x0008, + USE_CAPTURE_MOUSE = 0x0010, + BUTTON_KEY_DOWN = 0x0020, + DEFAULT_BUTTON = 0x0040, + SELECTED = 0x0080, + DRAW_FOCUS_BOX = 0x0100, + BLINK = 0x0200, + ALL_FLAGS = 0xFFFF, + }; + + static inline int MouseCodeToMask( MouseCode code ); + + CUtlFlags< unsigned short > _buttonFlags; // see ButtonFlags_t + int _mouseClickMask; + KeyValues *_actionMessage; + ActivationType_t _activationType; + + IBorder *_defaultBorder; + IBorder *_depressedBorder; + IBorder *_keyFocusBorder; + + Color _defaultFgColor, _defaultBgColor; + Color _armedFgColor, _armedBgColor; + Color _depressedFgColor, _depressedBgColor; + Color _keyboardFocusColor; + Color _blinkFgColor; + + bool _paint; + + unsigned short m_sArmedSoundName, m_sDepressedSoundName, m_sReleasedSoundName; + bool m_bSelectionStateSaved; +}; + +} // namespace vgui + +#endif // BUTTON_H diff --git a/public/vgui_controls/CheckButton.h b/public/vgui_controls/CheckButton.h new file mode 100644 index 0000000..96903c6 --- /dev/null +++ b/public/vgui_controls/CheckButton.h @@ -0,0 +1,74 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CHECKBUTTON_H +#define CHECKBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +class CheckImage; + +namespace vgui +{ + +class TextImage; + +//----------------------------------------------------------------------------- +// Purpose: Tick-box button +//----------------------------------------------------------------------------- +class CheckButton : public ToggleButton +{ + DECLARE_CLASS_SIMPLE( CheckButton, ToggleButton ); + +public: + CheckButton(Panel *parent, const char *panelName, const char *text); + ~CheckButton(); + + // Check the button + virtual void SetSelected(bool state ); + + // Left4Dead: + void SetCheckDrawMode( int mode ); + + // sets whether or not the state of the check can be changed + // if this is set to false, then no input in the code or by the user can change it's state + virtual void SetCheckButtonCheckable(bool state); + virtual bool IsCheckButtonCheckable() const { return m_bCheckButtonCheckable; } + + Color GetDisabledFgColor() { return _disabledFgColor; } + Color GetDisabledBgColor() { return _disabledBgColor; } + +protected: + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_PTR( OnCheckButtonChecked, "CheckButtonChecked", panel ); + virtual Color GetButtonFgColor(); + + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + /* MESSAGES SENT + "CheckButtonChecked" - sent when the check button state is changed + "state" - button state: 1 is checked, 0 is unchecked + */ + + +private: + enum { CHECK_INSET = 6 }; + bool m_bCheckButtonCheckable; + CheckImage *_checkBoxImage; + Color _selectedFgColor; + Color _disabledFgColor; + Color _disabledBgColor; +}; + +} // namespace vgui + +#endif // CHECKBUTTON_H diff --git a/public/vgui_controls/CheckButtonList.h b/public/vgui_controls/CheckButtonList.h new file mode 100644 index 0000000..fefcc24 --- /dev/null +++ b/public/vgui_controls/CheckButtonList.h @@ -0,0 +1,74 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef CHECKBUTTONLIST_H +#define CHECKBUTTONLIST_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "UtlVector.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Contains a list of check boxes, displaying scrollbars if necessary +//----------------------------------------------------------------------------- +class CheckButtonList : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CheckButtonList, EditablePanel ); + +public: + CheckButtonList(Panel *parent, const char *name); + ~CheckButtonList(); + + // adds a check button to the list + int AddItem(const char *itemText, bool startsSelected, KeyValues *userData); + + // clears the list + void RemoveAll(); + + // number of items in list that are checked + int GetCheckedItemCount(); + + // item iteration + bool IsItemIDValid(int itemID); + int GetHighestItemID(); + int GetItemCount(); + + // item info + KeyValues *GetItemData(int itemID); + bool IsItemChecked(int itemID); + void SetItemCheckable(int itemID, bool state); + + /* MESSAGES SENT + "CheckButtonChecked" - sent when one of the check buttons state has changed + + */ + +protected: + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnMouseWheeled(int delta); + +private: + MESSAGE_FUNC_PARAMS( OnCheckButtonChecked, "CheckButtonChecked", pParams ); + MESSAGE_FUNC( OnScrollBarSliderMoved, "ScrollBarSliderMoved" ); + + struct CheckItem_t + { + vgui::CheckButton *checkButton; + KeyValues *userData; + }; + CUtlVector m_CheckItems; + vgui::ScrollBar *m_pScrollBar; +}; + +} + +#endif // CHECKBUTTONLIST_H diff --git a/public/vgui_controls/CircularProgressBar.h b/public/vgui_controls/CircularProgressBar.h new file mode 100644 index 0000000..c18616d --- /dev/null +++ b/public/vgui_controls/CircularProgressBar.h @@ -0,0 +1,64 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CIRCULARPROGRESSBAR_H +#define CIRCULARPROGRESSBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Progress Bar in the shape of a pie graph +//----------------------------------------------------------------------------- +class CircularProgressBar : public ProgressBar +{ + DECLARE_CLASS_SIMPLE( CircularProgressBar, ProgressBar ); + +public: + CircularProgressBar(Panel *parent, const char *panelName); + ~CircularProgressBar(); + + virtual void ApplySettings(KeyValues *inResourceData); + virtual void ApplySchemeSettings(IScheme *pScheme); + + void SetFgImage(const char *imageName) { SetImage( imageName, PROGRESS_TEXTURE_FG ); } + void SetBgImage(const char *imageName) { SetImage( imageName, PROGRESS_TEXTURE_BG ); } + + enum CircularProgressDir_e + { + PROGRESS_CW, + PROGRESS_CCW + }; + int GetProgressDirection() const { return m_iProgressDirection; } + void SetProgressDirection( int val ) { m_iProgressDirection = val; } + +protected: + virtual void Paint(); + virtual void PaintBackground(); + + void DrawCircleSegment( Color c, float flEndDegrees, bool clockwise /* = true */ ); + void SetImage(const char *imageName, progress_textures_t iPos); + +private: + int m_iProgressDirection; + + int m_nTextureId[NUM_PROGRESS_TEXTURES]; + char *m_pszImageName[NUM_PROGRESS_TEXTURES]; + int m_lenImageName[NUM_PROGRESS_TEXTURES]; +}; + +} // namespace vgui + +#endif // CIRCULARPROGRESSBAR_H \ No newline at end of file diff --git a/public/vgui_controls/ComboBox.h b/public/vgui_controls/ComboBox.h new file mode 100644 index 0000000..682bae0 --- /dev/null +++ b/public/vgui_controls/ComboBox.h @@ -0,0 +1,148 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef COMBOBOX_H +#define COMBOBOX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class ComboBoxButton; + +//----------------------------------------------------------------------------- +// Purpose: Text entry with drop down options list +//----------------------------------------------------------------------------- +class ComboBox : public TextEntry +{ + DECLARE_CLASS_SIMPLE( ComboBox, TextEntry ); + +public: + ComboBox(Panel *parent, const char *panelName, int numLines, bool allowEdit); + ~ComboBox(); + + // functions designed to be overriden + virtual void OnShowMenu(Menu *menu) {} + virtual void OnHideMenu(Menu *menu) {} + + // Set the number of items in the drop down menu. + virtual void SetNumberOfEditLines( int numLines ); + + // Add an item to the drop down + virtual int AddItem(const char *itemText, const KeyValues *userData); + virtual int AddItem(const wchar_t *itemText, const KeyValues *userData); + + virtual int GetItemCount(); + int GetItemIDFromRow( int row ); + + // update the item + virtual bool UpdateItem(int itemID, const char *itemText,const KeyValues *userData); + virtual bool UpdateItem(int itemID, const wchar_t *itemText, const KeyValues *userData); + virtual bool IsItemIDValid(int itemID); + + // set the enabled state of an item + virtual void SetItemEnabled(const char *itemText, bool state); + virtual void SetItemEnabled(int itemID, bool state); + + // Removes a single item + void DeleteItem( int itemID ); + + // Remove all items from the drop down menu + void RemoveAll(); + // deprecated, use above + void DeleteAllItems() { RemoveAll(); } + + // Sorts the items in the list - FIXME does nothing + virtual void SortItems(); + + // Set the visiblity of the drop down menu button. + virtual void SetDropdownButtonVisible(bool state); + + // Return true if the combobox current has the dropdown menu open + virtual bool IsDropdownVisible(); + + // Activate the item in the menu list,as if that + // menu item had been selected by the user + MESSAGE_FUNC_INT( ActivateItem, "ActivateItem", itemID ); + void ActivateItemByRow(int row); + + int GetActiveItem(); + KeyValues *GetActiveItemUserData(); + KeyValues *GetItemUserData(int itemID); + void GetItemText( int itemID, wchar_t *text, int bufLenInBytes ); + void GetItemText( int itemID, char *text, int bufLenInBytes ); + + // sets a custom menu to use for the dropdown + virtual void SetMenu( Menu *menu ); + virtual Menu *GetMenu() { return m_pDropDown; } + + // Layout the format of the combo box for drawing on screen + virtual void PerformLayout(); + + /* action signals + "TextChanged" - signals that the text has changed in the combo box + + */ + + virtual void ShowMenu(); + virtual void HideMenu(); + virtual void OnKillFocus(); + MESSAGE_FUNC( OnMenuClose, "MenuClose" ); + virtual void DoClick(); + virtual void OnSizeChanged(int wide, int tall); + + virtual void SetOpenDirection(Menu::MenuDirection_e direction); + + virtual void SetFont( HFont font ); + + virtual void SetUseFallbackFont( bool bState, HFont hFallback ); + +protected: + // overrides + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + MESSAGE_FUNC( OnMenuItemSelected, "MenuItemSelected" ); + virtual void OnCommand( const char *command ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnCursorEntered(); + virtual void OnCursorExited(); + + // custom message handlers + MESSAGE_FUNC_WCHARPTR( OnSetText, "SetText", text ); + virtual void OnSetFocus(); // called after the panel receives the keyboard focus +#ifdef _X360 + virtual void OnKeyCodePressed(KeyCode code); +#endif + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnKeyTyped(wchar_t unichar); + + void SelectMenuItem(int itemToSelect); + void MoveAlongMenuItemList(int direction); + void MoveToFirstMenuItem(); + void MoveToLastMenuItem(); + +private: + void DoMenuLayout(); + + Menu *m_pDropDown; + ComboBoxButton *m_pButton; + + bool m_bAllowEdit; + bool m_bHighlight; + Menu::MenuDirection_e m_iDirection; + int m_iOpenOffsetY; +}; + +} // namespace vgui + +#endif // COMBOBOX_H diff --git a/public/vgui_controls/ControllerMap.h b/public/vgui_controls/ControllerMap.h new file mode 100644 index 0000000..7984609 --- /dev/null +++ b/public/vgui_controls/ControllerMap.h @@ -0,0 +1,48 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CONTROLLERMAP_H +#define CONTROLLERMAP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "Panel.h" +#include "UtlMap.h" +#include "UtlSymbol.h" + +class CControllerMap : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CControllerMap, vgui::Panel ) + + virtual void OnKeyCodeTyped( vgui::KeyCode code ); + +public: + CControllerMap( vgui::Panel *parent, const char *name ); + + virtual void ApplySettings( KeyValues *inResourceData ); + + int NumButtons( void ) + { + return m_buttonMap.Count(); + } + + const char *GetBindingText( int idx ); + const char *GetBindingIcon( int idx ); + +private: + + struct button_t + { + CUtlSymbol cmd; + CUtlSymbol text; + CUtlSymbol icon; + }; + CUtlMap< int, button_t > m_buttonMap; +}; + +#endif // CONTROLLERMAP_H \ No newline at end of file diff --git a/public/vgui_controls/Controls.h b/public/vgui_controls/Controls.h new file mode 100644 index 0000000..26efa4a --- /dev/null +++ b/public/vgui_controls/Controls.h @@ -0,0 +1,160 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef CONTROLS_H +#define CONTROLS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +#include "tier1/interface.h" +#include "vgui/MouseCode.h" +#include "vgui/KeyCode.h" +#include "tier3/tier3.h" + + +namespace vgui +{ + +// handles the initialization of the vgui interfaces +// interfaces (listed below) are first attempted to be loaded from primaryProvider, then secondaryProvider +// moduleName should be the name of the module that this instance of the vgui_controls has been compiled into +bool VGui_InitInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ); + +// returns the name of the module as specified above +const char *GetControlsModuleName(); + +class IPanel; +class IInput; +class ISchemeManager; +class ISurface; +class ISystem; +class IVGui; + +//----------------------------------------------------------------------------- +// Backward compat interfaces, use the interfaces grabbed in tier3 +// set of accessor functions to vgui interfaces +// the appropriate header file for each is listed above the item +//----------------------------------------------------------------------------- + +// #include +inline vgui::IInput *input() +{ + return g_pVGuiInput; +} + +// #include +inline vgui::ISchemeManager *scheme() +{ + return g_pVGuiSchemeManager; +} + +// #include +inline vgui::ISurface *surface() +{ + return g_pVGuiSurface; +} + +// #include +inline vgui::ISystem *system() +{ + return g_pVGuiSystem; +} + +// #include +inline vgui::IVGui *ivgui() +{ + return g_pVGui; +} + +// #include +inline vgui::IPanel *ipanel() +{ + return g_pVGuiPanel; +} + +// predeclare all the vgui control class names +class AnalogBar; +class AnimatingImagePanel; +class AnimationController; +class BuildModeDialog; +class Button; +class CheckButton; +class CheckButtonList; +class CircularProgressBar; +template< class T >class CvarToggleCheckButton; +class ComboBox; +class DirectorySelectDialog; +class Divider; +class EditablePanel; +class FileOpenDialog; +class Frame; +class GraphPanel; +class HTML; +class ImagePanel; +class Label; +class ListPanel; +class ListViewPanel; +class Menu; +class MenuBar; +class MenuButton; +class MenuItem; +class MessageBox; +class Panel; +class PanelListPanel; +class ProgressBar; +class ProgressBox; +class PropertyDialog; +class PropertyPage; +class PropertySheet; +class QueryBox; +class RadioButton; +class RichText; +class ScalableImagePanel; +class ScrollBar; +class ScrollBarSlider; +class SectionedListPanel; +class Slider; +class Splitter; +class TextEntry; +class ToggleButton; +class Tooltip; +class TreeView; +class CTreeViewListControl; +class URLLabel; +class WizardPanel; +class WizardSubPanel; + +// vgui controls helper classes +class BuildGroup; +class FocusNavGroup; +class IBorder; +class IImage; +class Image; +class ImageList; +class TextImage; + +} // namespace vgui + +// hotkeys disabled until we work out exactly how we want to do them +#define VGUI_HOTKEYS_ENABLED +#define VGUI_DRAW_HOTKEYS_ENABLED + +#define USING_BUILD_FACTORY( className ) \ + extern className *g_##className##LinkerHack; \ + className *g_##className##PullInModule = g_##className##LinkerHack; + +#define USING_BUILD_FACTORY_ALIAS( className, factoryName ) \ + extern className *g_##factoryName##LinkerHack; \ + className *g_##factoryName##PullInModule = g_##factoryName##LinkerHack; + +#endif // CONTROLS_H diff --git a/public/vgui_controls/CvarToggleCheckButton.h b/public/vgui_controls/CvarToggleCheckButton.h new file mode 100644 index 0000000..1a04ac2 --- /dev/null +++ b/public/vgui_controls/CvarToggleCheckButton.h @@ -0,0 +1,191 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CVARTOGGLECHECKBUTTON_H +#define CVARTOGGLECHECKBUTTON_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/VGUI.h" +#include "vgui_controls/CheckButton.h" +#include "tier1/UtlString.h" +#include "tier1/Keyvalues.h" +namespace vgui +{ + +template< class T > +class CvarToggleCheckButton : public CheckButton +{ + DECLARE_CLASS_SIMPLE( CvarToggleCheckButton, CheckButton ); + +public: + CvarToggleCheckButton( Panel *parent, const char *panelName, const char *text = "", + char const *cvarname = NULL, bool ignoreMissingCvar = false ); + ~CvarToggleCheckButton(); + + virtual void SetSelected( bool state ); + + virtual void Paint(); + + void Reset(); + void ApplyChanges(); + bool HasBeenModified(); + virtual void ApplySettings( KeyValues *inResourceData ); + +private: + // Called when the OK / Apply button is pressed. Changed data should be written into cvar. + MESSAGE_FUNC( OnApplyChanges, "ApplyChanges" ); + MESSAGE_FUNC( OnButtonChecked, "CheckButtonChecked" ); + + T m_cvar; + bool m_bStartValue; + bool m_bIgnoreMissingCvar; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template< class T > +CvarToggleCheckButton::CvarToggleCheckButton( Panel *parent, const char *panelName, const char *text, char const *cvarname, bool ignoreMissingCvar ) + : CheckButton( parent, panelName, text ), m_cvar( (cvarname)?cvarname:"", (cvarname)?ignoreMissingCvar:true ) +{ + m_bIgnoreMissingCvar = ignoreMissingCvar; + + if (m_cvar.IsValid()) + { + Reset(); + } + AddActionSignalTarget( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +template< class T > +CvarToggleCheckButton::~CvarToggleCheckButton() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::Paint() +{ + if ( !m_cvar.IsValid() ) + { + BaseClass::Paint(); + return; + } + + bool value = m_cvar.GetBool(); + + if ( value != m_bStartValue ) + { + SetSelected( value ); + m_bStartValue = value; + } + BaseClass::Paint(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the OK / Apply button is pressed. Changed data should be written into cvar. +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::OnApplyChanges() +{ + ApplyChanges(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::ApplyChanges() +{ + if ( !m_cvar.IsValid() ) + return; + + m_bStartValue = IsSelected(); + m_cvar.SetValue( m_bStartValue ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::Reset() +{ + if ( !m_cvar.IsValid() ) + return; + + m_bStartValue = m_cvar.GetBool(); + SetSelected(m_bStartValue); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +bool CvarToggleCheckButton::HasBeenModified() +{ + return IsSelected() != m_bStartValue; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *panel - +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::SetSelected( bool state ) +{ + BaseClass::SetSelected( state ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::OnButtonChecked() +{ + if (HasBeenModified()) + { + PostActionSignal(new KeyValues("ControlModified")); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template< class T > +void CvarToggleCheckButton::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + const char *cvarName = inResourceData->GetString("cvar_name", ""); + const char *cvarValue = inResourceData->GetString("cvar_value", ""); + + if( Q_stricmp( cvarName, "") == 0 ) + return;// Doesn't have cvar set up in res file, must have been constructed with it. + + if( Q_stricmp( cvarValue, "1") == 0 ) + m_bStartValue = true; + else + m_bStartValue = false; + + m_cvar.Init( cvarName, m_bIgnoreMissingCvar ); + if ( m_cvar.IsValid() ) + { + SetSelected( m_cvar.GetBool() ); + } +} + +} // namespace vgui + +#endif // CVARTOGGLECHECKBUTTON_H diff --git a/public/vgui_controls/DialogManager.h b/public/vgui_controls/DialogManager.h new file mode 100644 index 0000000..545dc45 --- /dev/null +++ b/public/vgui_controls/DialogManager.h @@ -0,0 +1,196 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DIALOGMANAGER_H +#define DIALOGMANAGER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: utility class, maps a set of ID's to dialogs +// used to manage sets of similar dialogs (object property dialogs, etc.) +//----------------------------------------------------------------------------- +template +class DialogManager +{ +public: + // new dialog factory function + typedef TDialog *(*CreateNewDialogFunc_t)(I dialogID); + + // constructor + DialogManager(CreateNewDialogFunc_t createDialogFunc); + + // finds the dialog by the specified ID + TDialog *FindDialog(I dialogID, bool bCreate); + + // opens the dialog; creating it if specified + TDialog *ActivateDialog(I dialogID, bool bCreate); + + // closes all the dialogs + void CloseAll(); + + // closes and deletes all the dialogs + void CloseAndDeleteAll(); + + // returns number of active dialogs + int Count(); + + // sets parent to use + void SetParent( vgui::VPANEL parent ); + +private: + // checks if an index in the dialog list is valid; if it has been deleted, removes the entry + bool ValidateIndex(int index); + + struct DialogItem_t + { + I id; + DHANDLE dlg; + }; + + CUtlLinkedList m_Dialogs; + CreateNewDialogFunc_t m_CreateFunc; + vgui::VPANEL m_pVGUIParentPanel; +}; + + +// constructor +template +inline DialogManager::DialogManager(CreateNewDialogFunc_t createDialogFunc) +{ + m_CreateFunc = createDialogFunc; + m_pVGUIParentPanel = NULL; +} + +// finds the dialog; creating it if necessary +template +inline TDialog *DialogManager::FindDialog(I dialogID, bool bCreate) +{ + for (int i = 0; i < m_Dialogs.MaxElementIndex(); i++) + { + if (ValidateIndex(i) && m_Dialogs[i].id == dialogID) + { + return m_Dialogs[i].dlg; + } + } + + if (bCreate) + { + int newIndex = m_Dialogs.AddToTail(); + if (m_CreateFunc) + { + m_Dialogs[newIndex].dlg = m_CreateFunc(dialogID); + } + else + { + m_Dialogs[newIndex].dlg = new TDialog(NULL, dialogID); + } + Assert(m_pVGUIParentPanel); + m_Dialogs[newIndex].dlg->SetParent( m_pVGUIParentPanel ); + + m_Dialogs[newIndex].id = dialogID; + return m_Dialogs[newIndex].dlg; + } + + // dlg not found, not created + return NULL; +} + +// opens the dialog; creating it if necessary +template +inline TDialog *DialogManager::ActivateDialog(I dialogID, bool bCreate) +{ + TDialog *dlg = FindDialog(dialogID, bCreate); + if (dlg) + { + dlg->Activate(); + } + return dlg; +} + +// count +template +inline int DialogManager::Count() +{ + // validate all the indexes first + for (int i = 0; i < m_Dialogs.MaxElementIndex(); i++) + { + if (ValidateIndex(i)) + { + } + } + + // return the (remaining) count + return m_Dialogs.Count(); +} + +// closes all the dialogs +template +inline void DialogManager::CloseAll() +{ + for (int i = 0; i < m_Dialogs.MaxElementIndex(); i++) + { + if (ValidateIndex(i)) + { + m_Dialogs[i].dlg->PostMessage(m_Dialogs[i].dlg, new KeyValues("Close")); + } + } +} + +// closes and deletes all the dialogs +template +inline void DialogManager::CloseAndDeleteAll() +{ + CloseAll(); + for (int i = 0; i < m_Dialogs.MaxElementIndex(); i++) + { + if (ValidateIndex(i)) + { + m_Dialogs[i].dlg->MarkForDeletion(); + } + } + m_Dialogs.RemoveAll(); +} + +// checks if a dialog is valid; if it has been deleted, removes the entry +template +inline bool DialogManager::ValidateIndex(int index) +{ + if (m_Dialogs.IsValidIndex(index)) + { + if (m_Dialogs[index].dlg.Get()) + { + return true; + } + else + { + // entry has been deleted; removed + m_Dialogs.Remove(index); + } + } + return false; +} + +template +inline void DialogManager::SetParent( vgui::VPANEL parent ) +{ + m_pVGUIParentPanel = parent; +} + + +} // namespace vgui + +#endif // DIALOGMANAGER_H diff --git a/public/vgui_controls/DirectorySelectDialog.h b/public/vgui_controls/DirectorySelectDialog.h new file mode 100644 index 0000000..bcf3a8a --- /dev/null +++ b/public/vgui_controls/DirectorySelectDialog.h @@ -0,0 +1,96 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef DIRECTORYSELECTDIALOG_H +#define DIRECTORYSELECTDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Used to handle dynamically populating the tree view +//----------------------------------------------------------------------------- +class DirectoryTreeView : public TreeView +{ +public: + DirectoryTreeView(DirectorySelectDialog *parent, const char *name); + virtual void GenerateChildrenOfNode(int itemIndex); + +private: + DirectorySelectDialog *m_pParent; +}; + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to let user select a directory (like during install) +//----------------------------------------------------------------------------- +class DirectorySelectDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( DirectorySelectDialog, Frame ); + +public: + DirectorySelectDialog(vgui::Panel *parent, const char *title); + + // sets where it should start searching + void SetStartDirectory(const char *path); + + // sets what name should show up by default in the create directory dialog + void SetDefaultCreateDirectoryName(const char *defaultCreateDirName); + + // opens the dialog + void DoModal(); + + /* action signals + + "DirectorySelected" + "dir" - the directory that was selected + + */ + + // Expand the tree nodes to match a supplied path, optionally selecting the final directory + void ExpandTreeToPath( const char *lpszPath, bool bSelectFinalDirectory = true ); + +protected: + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnClose(); + + // command buttons + virtual void OnCommand(const char *command); + +private: + MESSAGE_FUNC( OnTextChanged, "TextChanged" ); + MESSAGE_FUNC( OnTreeViewItemSelected, "TreeViewItemSelected" ); + MESSAGE_FUNC_CHARPTR( OnCreateDirectory, "CreateDirectory", dir ); + void BuildDirTree(); + void BuildDriveChoices(); + void ExpandTreeNode(const char *path, int parentNodeIndex); + void GenerateChildrenOfDirectoryNode(int nodeIndex); + void GenerateFullPathForNode(int nodeIndex, char *path, int pathBufferSize); + bool DoesDirectoryHaveSubdirectories(const char *path, const char *dir); + + char m_szCurrentDir[512]; + char m_szDefaultCreateDirName[64]; + char m_szCurrentDrive[16]; + vgui::TreeView *m_pDirTree; + vgui::ComboBox *m_pDriveCombo; + vgui::Button *m_pCancelButton; + vgui::Button *m_pSelectButton; + vgui::Button *m_pCreateButton; + + friend class DirectoryTreeView; +}; + +} // namespace vgui + + +#endif // DIRECTORYSELECTDIALOG_H diff --git a/public/vgui_controls/Divider.h b/public/vgui_controls/Divider.h new file mode 100644 index 0000000..cc32932 --- /dev/null +++ b/public/vgui_controls/Divider.h @@ -0,0 +1,38 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DIVIDER_H +#define DIVIDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Thin line used to divide sections in dialogs +//----------------------------------------------------------------------------- +class Divider : public Panel +{ + DECLARE_CLASS_SIMPLE( Divider, Panel ); + +public: + Divider(Panel *parent, const char *name); + ~Divider(); + + virtual void ApplySchemeSettings(IScheme *pScheme); +}; + + +} // namespace vgui + + +#endif // DIVIDER_H diff --git a/public/vgui_controls/EditablePanel.h b/public/vgui_controls/EditablePanel.h new file mode 100644 index 0000000..4d7babc --- /dev/null +++ b/public/vgui_controls/EditablePanel.h @@ -0,0 +1,164 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef EDITABLEPANEL_H +#define EDITABLEPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Panel that supports editing via the build dialog +//----------------------------------------------------------------------------- +class EditablePanel : public Panel +{ + DECLARE_CLASS_SIMPLE( EditablePanel, Panel ); + +public: + EditablePanel(Panel *parent, const char *panelName); + EditablePanel(Panel *parent, const char *panelName, HScheme hScheme); + + virtual ~EditablePanel(); + + // Load the control settings - should be done after all the children are added + // If you pass in pPreloadedKeyValues, it won't actually load the file. That way, you can cache + // the keyvalues outside of here if you want to prevent file accesses in the middle of the game. + virtual void LoadControlSettings(const char *dialogResourceName, const char *pathID = NULL, KeyValues *pPreloadedKeyValues = NULL, KeyValues *pConditions = NULL); + virtual void ApplySettings(KeyValues *inResourceData); + + virtual void PerformLayout(); + + // sets the name of this dialog so it can be saved in the user config area + // use dialogID to differentiate multiple instances of the same dialog + virtual void LoadUserConfig(const char *configName, int dialogID = 0); + virtual void SaveUserConfig(); + + // combines both of the above, LoadControlSettings & LoadUserConfig + virtual void LoadControlSettingsAndUserConfig(const char *dialogResourceName, int dialogID = 0); + + // Override to change how build mode is activated + virtual void ActivateBuildMode(); + + // Return the buildgroup that this panel is part of. + virtual BuildGroup *GetBuildGroup(); + + // Virtual factory for control creation + // controlName is a string which is the same as the class name + virtual Panel *CreateControlByName(const char *controlName); + + // Shortcut function to set data in child controls + virtual void SetControlString(const char *controlName, const char *string); + // Shortcut function to set data in child controls + virtual void SetControlString(const char *controlName, const wchar_t *string); + // Shortcut function to set data in child controls + virtual void SetControlInt(const char *controlName, int state); + // Shortcut function to get data in child controls + virtual int GetControlInt(const char *controlName, int defaultState); + // Shortcut function to get data in child controls + // Returns a maximum of 511 characters in the string + virtual const char *GetControlString(const char *controlName, const char *defaultString = ""); + // as above, but copies the result into the specified buffer instead of a static buffer + virtual void GetControlString(const char *controlName, char *buf, int bufSize, const char *defaultString = ""); + // sets the enabled state of a control + virtual void SetControlEnabled(const char *controlName, bool enabled); + virtual void SetControlVisible(const char *controlName, bool visible); + + // localization variables (used in constructing UI strings) + // after the variable is set, causes all the necessary sub-panels to update + virtual void SetDialogVariable(const char *varName, const char *value); + virtual void SetDialogVariable(const char *varName, const wchar_t *value); + virtual void SetDialogVariable(const char *varName, int value); + virtual void SetDialogVariable(const char *varName, float value); + + // Focus handling + // Delegate focus to a sub panel + virtual void RequestFocus(int direction = 0); + virtual bool RequestFocusNext(VPANEL panel); + virtual bool RequestFocusPrev(VPANEL panel); + // Pass the focus down onto the last used panel + virtual void OnSetFocus(); + // Update focus info for navigation + virtual void OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel); + // Get the panel that currently has keyfocus + virtual VPANEL GetCurrentKeyFocus(); + // Get the panel with the specified hotkey + virtual Panel *HasHotkey(wchar_t key); + + virtual void OnKeyCodeTyped( KeyCode code ); + + // Handle information requests + virtual bool RequestInfo(KeyValues *data); + /* INFO HANDLING + "BuildDialog" + input: + "BuildGroupPtr" - pointer to the panel/dialog to edit + returns: + "PanelPtr" - pointer to a new BuildModeDialog() + + "ControlFactory" + input: + "ControlName" - class name of the control to create + returns: + "PanelPtr" - pointer to the newly created panel, or NULL if no such class exists + */ + // registers a file in the list of control settings, so the vgui dialog can choose between them to edit + virtual void RegisterControlSettingsFile(const char *dialogResourceName, const char *pathID = NULL); + + // localization variables - only use this if you need to iterate the variables, use the SetLoc*() to set them + KeyValues *GetDialogVariables(); + +protected: + virtual void PaintBackground(); + + // nav group access + virtual FocusNavGroup &GetFocusNavGroup(); + + // called when default button has been set + MESSAGE_FUNC_HANDLE( OnDefaultButtonSet, "DefaultButtonSet", button ); + // called when the current default button has been set + MESSAGE_FUNC_HANDLE( OnCurrentDefaultButtonSet, "CurrentDefaultButtonSet", button ); + MESSAGE_FUNC( OnFindDefaultButton, "FindDefaultButton" ); + + // overrides + virtual void OnChildAdded(VPANEL child); + virtual void OnSizeChanged(int wide, int tall); + virtual void OnClose(); + + // user configuration settings + // this is used for any control details the user wants saved between sessions + // eg. dialog positions, last directory opened, list column width + virtual void ApplyUserConfigSettings(KeyValues *userConfig); + + // returns user config settings for this control + virtual void GetUserConfigSettings(KeyValues *userConfig); + + // optimization for text rendering, returns true if text should be rendered immediately after Paint() + // disabled for now + // virtual bool ShouldFlushText(); + +private: + void ForceSubPanelsToUpdateWithNewDialogVariables(); + + BuildGroup *_buildGroup; + FocusNavGroup m_NavGroup; + KeyValues *m_pDialogVariables; + + // the wide and tall to which all controls are locked - used for autolayout deltas + char *m_pszConfigName; + int m_iConfigID; +}; + +} // namespace vgui + +#endif // EDITABLEPANEL_H diff --git a/public/vgui_controls/ExpandButton.h b/public/vgui_controls/ExpandButton.h new file mode 100644 index 0000000..34d6275 --- /dev/null +++ b/public/vgui_controls/ExpandButton.h @@ -0,0 +1,61 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A button with no borders that shows a left-pointing or down-pointing triangle +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef EXPANDBUTTON_H +#define EXPANDBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: A button with no borders that shows a left-pointing or down-pointing arrow +//----------------------------------------------------------------------------- +class ExpandButton : public ToggleButton +{ + DECLARE_CLASS_SIMPLE( ExpandButton, ToggleButton ); + +public: + ExpandButton( Panel *parent, const char *panelName ); + ~ExpandButton(); + + // Expand the button (selected == expanded) + virtual void SetSelected( bool bExpand ); + + // sets whether or not the state of the check can be changed + // if this is set to false, then no input in the code or by the user can change it's state + void SetExpandable(bool state); + + virtual void Paint(); + +protected: + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_PTR( OnExpanded, "Expanded", panel ); + + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + /* MESSAGES SENT + "Expanded" - sent when the expand button state is changed + "state" - button state: 1 is expanded, 0 is unexpanded + */ + +private: + bool m_bExpandable; + HFont m_hFont; + Color m_Color; +}; + +} // namespace vgui + +#endif // EXPANDBUTTON_H diff --git a/public/vgui_controls/FileOpenDialog.h b/public/vgui_controls/FileOpenDialog.h new file mode 100644 index 0000000..69765c2 --- /dev/null +++ b/public/vgui_controls/FileOpenDialog.h @@ -0,0 +1,197 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Declaration of FileOpenDialog class, a generic open/save as file dialog +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef FILEOPENDIALOG_H +#define FILEOPENDIALOG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" + +namespace vgui +{ + +class FileCompletionEdit; // local +class InputDialog; + +//----------------------------------------------------------------------------- +// Purpose: generic open/save as file dialog, by default deletes itself on close +//----------------------------------------------------------------------------- +enum FileOpenDialogType_t +{ + FOD_SAVE = 0, + FOD_OPEN, + FOD_SELECT_DIRECTORY, + FOD_OPEN_MULTIPLE, +}; + +struct FileData_t +{ + CUtlString m_FileAttributes; + CUtlString m_CreationTime; + int64 m_nCreationTime; + CUtlString m_LastAccessTime; + CUtlString m_LastWriteTime; + int64 m_nLastWriteTime; + int64 m_nFileSize; + CUtlString m_FileName; + CUtlString m_FullPath; + wchar_t m_FileType[ 80 ]; + + bool m_bDirectory; + + void PrepareKV( KeyValues *kv ); +}; + +class FileOpenDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( FileOpenDialog, Frame ); + +public: + // NOTE: Backward compat constructor + FileOpenDialog( Panel *parent, const char *title, bool bOpenFile, KeyValues *pContextKeyValues = 0 ); + + // The context keyvalues are added to all messages sent by this dialog if they are specified + FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues = 0 ); + ~FileOpenDialog(); + + // Set the directory the file search starts in + void SetStartDirectory(const char *dir); + + // Sets the start directory context (and resets the start directory in the process) + // NOTE: If you specify a startdir context, then if you've already opened + // a file with that same start dir context before, it will start in the + // same directory it ended up in. + void SetStartDirectoryContext( const char *pContext, const char *pDefaultDir ); + + // Add filters for the drop down combo box + // The filter info, if specified, gets sent back to the app in the FileSelected message + void AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo = NULL ); + + // Activate the dialog + // NOTE: The argument is there for backward compat + void DoModal( bool bUnused = false ); + + // Get the directory this is currently in + void GetCurrentDirectory( char *buf, int bufSize ); + + // Get the last selected file name + void GetSelectedFileName( char *buf, int bufSize ); + + /* + messages sent: + "FileSelected" + "fullpath" // specifies the fullpath of the file + "filterinfo" // Returns the filter info associated with the active filter + "FileSelectionCancelled" + */ + + static bool FileNameWildCardMatch( char const *pchFileName, char const *pchPattern ); + +protected: + virtual void OnCommand( const char *command ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnClose(); + virtual void OnKeyCodeTyped(KeyCode code); + + // handles the open button being pressed + // checks on what has changed and acts accordingly + MESSAGE_FUNC( OnOpen, "OnOpen" ); + MESSAGE_FUNC( OnSelectFolder, "SelectFolder" ); + MESSAGE_FUNC( OnFolderUp, "OnFolderUp" ); + MESSAGE_FUNC( OnNewFolder, "OnNewFolder" ); + MESSAGE_FUNC( OnOpenInExplorer, "OpenInExplorer" ); + + MESSAGE_FUNC( PopulateFileList, "PopulateFileList" ); + MESSAGE_FUNC( PopulateDriveList, "PopulateDriveList" ); + MESSAGE_FUNC( PopulateFileNameSearchHistory, "PopulateFileNameSearchHistory" ); + + // moves the directory structure up + virtual void MoveUpFolder(); + + // validates that the current path is valid + virtual void ValidatePath(); + + // handles an item in the list being selected + MESSAGE_FUNC( OnItemSelected, "ItemSelected" ); + MESSAGE_FUNC( OnListItemSelected, "ListItemSelected" ) + { + OnItemSelected(); + } + + // changes directories in response to selecting drives from the combo box + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", kv ); + + MESSAGE_FUNC( OnInputCanceled, "InputCanceled" ); + MESSAGE_FUNC_PARAMS( OnInputCompleted, "InputCompleted", data ); + MESSAGE_FUNC( OnMatchStringSelected, "OnMatchStringSelected" ); + +private: + // Necessary because we have 2 constructors + void Init( const char *title, KeyValues *pContextKeyValues ); + + // Does the specified extension match something in the filter list? + bool ExtensionMatchesFilter( const char *pExt ); + + // Choose the first non *.* filter in the filter list + void ChooseExtension( char *pExt, int nBufLen ); + + // Saves the file to the start dir context + void SaveFileToStartDirContext( const char *pFullPath ); + + // Posts a file selected message + void PostFileSelectedMessage( const char *pFileName ); + + // Posts a multiple file selected message + void PostMultiFileSelectedMessage( void ); + + // Creates a new folder + void NewFolder( char const *folderName ); + + void BuildFileList(); + void FilterFileList(); + + bool PassesFilter( FileData_t *fd ); + int CountSubstringMatches(); + + void AddSearchHistoryString( char const *str ); + + vgui::ComboBox *m_pFullPathEdit; + vgui::ListPanel *m_pFileList; + + vgui::ComboBox *m_pFileNameCombo; + + vgui::ComboBox *m_pFileTypeCombo; + vgui::Button *m_pOpenButton; + vgui::Button *m_pCancelButton; + vgui::Button *m_pFolderUpButton; + vgui::Button *m_pNewFolderButton; + vgui::Button *m_pOpenInExplorerButton; + vgui::Label *m_pFileTypeLabel; + + KeyValues *m_pContextKeyValues; + + char m_szLastPath[1024]; + unsigned short m_nStartDirContext; + FileOpenDialogType_t m_DialogType; + bool m_bFileSelected : 1; + + VPANEL m_SaveModal; + vgui::DHANDLE< vgui::InputDialog > m_hInputDialog; + + CUtlVector< FileData_t > m_Files; + CUtlVector< FileData_t * > m_Filtered; + CUtlVector< CUtlString > m_SearchHistory; + + CUtlString m_CurrentSubstringFilter; +}; + +} // namespace vgui + +#endif // FILEOPENDIALOG_H diff --git a/public/vgui_controls/FileOpenStateMachine.h b/public/vgui_controls/FileOpenStateMachine.h new file mode 100644 index 0000000..f5b917f --- /dev/null +++ b/public/vgui_controls/FileOpenStateMachine.h @@ -0,0 +1,172 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// This is a helper class designed to help with the chains of modal dialogs +// encountered when trying to open or save a particular file +// +//============================================================================= + +#ifndef FILEOPENSTATEMACHINE_H +#define FILEOPENSTATEMACHINE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/panel.h" +#include "tier1/utlstring.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Interface for things using the file open state machine +//----------------------------------------------------------------------------- +abstract_class IFileOpenStateMachineClient +{ +public: + // Called by to allow clients to set up the save dialog + virtual void SetupFileOpenDialog( vgui::FileOpenDialog *pDialog, bool bOpenFile, const char *pFileFormat, KeyValues *pContextKeyValues ) = 0; + + // Called by to allow clients to actually read the file in + virtual bool OnReadFileFromDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) = 0; + + // Called by to allow clients to actually write the file out + virtual bool OnWriteFileToDisk( const char *pFileName, const char *pFileFormat, KeyValues *pContextKeyValues ) = 0; +}; + + +//----------------------------------------------------------------------------- +// This is a helper class designed to help with chains of modal dialogs +//----------------------------------------------------------------------------- +enum FileOpenStateMachineFlags_t +{ + FOSM_SHOW_PERFORCE_DIALOGS = 0x1, + FOSM_SHOW_SAVE_QUERY = 0x2, +}; + +class FileOpenStateMachine : public Panel +{ + DECLARE_CLASS_SIMPLE( FileOpenStateMachine, Panel ); + +public: + enum CompletionState_t + { + IN_PROGRESS = 0, // Still not finished, not successful or error + SUCCESSFUL, // Operation finished successfully + FILE_SAVE_CANCELLED, // The user chose 'cancel' in the dialog asking if he wanted to save + FILE_SAVE_NAME_NOT_SPECIFIED, // User hit cancel in the SaveAs dialog + FILE_NOT_OVERWRITTEN, // Operation aborted; existed file and user chose to not write over it + FILE_NOT_CHECKED_OUT, // Operation aborted; file wasn't checked out so couldn't be written over + ERROR_WRITING_FILE, // Error occurred writing the file out + ERROR_MAKING_FILE_WRITEABLE, // Error occurred when making the file writeable + FILE_NOT_MADE_WRITEABLE, // User chose to not make the file be writeable + FILE_OPEN_NAME_NOT_SPECIFIED, // User hit cancel in the Open dialog + ERROR_READING_FILE, // Error occurred reading the file in + }; + + FileOpenStateMachine( vgui::Panel *pParent, IFileOpenStateMachineClient *pClient ); + virtual ~FileOpenStateMachine(); + + // Opens a file, saves an existing one if necessary + void OpenFile( const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName = NULL, const char *pSaveFileType = NULL, int nFlags = 0 ); + + // Version of OpenFile that skips browsing for a particular file to open + void OpenFile( const char *pOpenFileName, const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName = NULL, const char *pSaveFileType = NULL, int nFlags = 0 ); + + // Used to save a specified file, and deal with all the lovely dialogs + // Pass in NULL to get a dialog to choose a filename to save + void SaveFile( KeyValues *pContextKeyValues, const char *pFileName, const char *pFileType, int nFlags = FOSM_SHOW_PERFORCE_DIALOGS ); + + // Returns the state machine completion state + CompletionState_t GetCompletionState(); + + /* MESSAGES SENT + "FileStateMachineFinished" - Called when we exit the state machine for any reason + "completionState" - See the CompletionState_t enum above + "wroteFile" - Indicates whether a file was written or not + "fullPath" - Indicates the full path of the file read for OpenFile or written for SaveFile + "fileType" - Indicates the file type of the file read for OpenFile or written for SaveFile + Use GetFirstTrueSubKey() to get the context passed into the OpenFile/SaveFile methods + */ + +private: + enum FOSMState_t + { + STATE_NONE = -1, + STATE_SHOWING_SAVE_DIRTY_FILE_DIALOG = 0, + STATE_SHOWING_SAVE_DIALOG, + STATE_SHOWING_OVERWRITE_DIALOG, + STATE_SHOWING_CHECK_OUT_DIALOG, + STATE_SHOWING_MAKE_FILE_WRITEABLE_DIALOG, + STATE_WRITING_FILE, + STATE_SHOWING_PERFORCE_ADD_DIALOG, + STATE_SHOWING_OPEN_DIALOG, + STATE_READING_FILE, + }; + + MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", pKeyValues ); + MESSAGE_FUNC( OnFileSelectionCancelled, "FileSelectionCancelled" ); + MESSAGE_FUNC_PARAMS( OnPerforceQueryCompleted, "PerforceQueryCompleted", pKeyValues ); + MESSAGE_FUNC( OnMakeFileWriteable, "MakeFileWriteable" ); + MESSAGE_FUNC( OnCancelMakeFileWriteable, "CancelMakeFileWriteable" ); + + // These messages are related to the dialog in OverwriteFileDialog + MESSAGE_FUNC( OnOverwriteFile, "OverwriteFile" ); + MESSAGE_FUNC( OnCancelOverwriteFile, "CancelOverwriteFile" ); + + // These messages come from the savedocumentquery dialog + MESSAGE_FUNC( OnSaveFile, "OnSaveFile" ); + MESSAGE_FUNC( OnMarkNotDirty, "OnMarkNotDirty" ); + MESSAGE_FUNC( OnCancelSaveDocument, "OnCancelSaveDocument" ); + + // Cleans up keyvalues + void CleanUpContextKeyValues(); + + // Utility to set the completion state + void SetCompletionState( CompletionState_t state ); + + // Show the save document query dialog + void ShowSaveQuery( ); + + // Shows the overwrite existing file dialog + void OverwriteFileDialog( ); + + // Shows the open file for edit dialog + void CheckOutDialog( ); + + // Shows the make file writeable dialog + void MakeFileWriteableDialog( ); + + // Writes the file out + void WriteFile(); + + // Shows the open file dialog + void OpenFileDialog( ); + + // Reads the file in + void ReadFile(); + + IFileOpenStateMachineClient *m_pClient; + KeyValues *m_pContextKeyValues; + FOSMState_t m_CurrentState; + CompletionState_t m_CompletionState; + CUtlString m_FileName; + CUtlString m_SaveFileType; + CUtlString m_OpenFileType; + CUtlString m_OpenFileName; + bool m_bShowPerforceDialogs : 1; + bool m_bShowSaveQuery : 1; + bool m_bIsOpeningFile : 1; + bool m_bWroteFile : 1; +}; + +} // end namespace vgui + + + +#endif // FILEOPENSTATEMACHINE_H diff --git a/public/vgui_controls/FocusNavGroup.h b/public/vgui_controls/FocusNavGroup.h new file mode 100644 index 0000000..6fb3ea5 --- /dev/null +++ b/public/vgui_controls/FocusNavGroup.h @@ -0,0 +1,61 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef FOCUSNAVGROUP_H +#define FOCUSNAVGROUP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class Panel; + +//----------------------------------------------------------------------------- +// Purpose: Handles navigation through a set of panels, with tab order & hotkeys +//----------------------------------------------------------------------------- +class FocusNavGroup +{ +public: + FocusNavGroup(Panel *panel); + ~FocusNavGroup(); + virtual Panel *GetDefaultPanel(); // returns a pointer to the panel with the default focus + + virtual void SetDefaultButton(Panel *panel); // sets which panel should receive input when ENTER is hit + virtual VPANEL GetDefaultButton(); // panel which receives default input when ENTER is hit, if current focused item cannot accept ENTER + virtual VPANEL GetCurrentDefaultButton(); // panel which receives input when ENTER is hit + virtual Panel *FindPanelByHotkey(wchar_t key); // finds the panel which is activated by the specified key + virtual bool RequestFocusPrev(VPANEL panel = NULL); // if panel is NULL, then the tab increment is based last known panel that had key focus + virtual bool RequestFocusNext(VPANEL panel = NULL); + + virtual Panel *GetCurrentFocus(); + virtual VPANEL SetCurrentFocus(VPANEL panel, VPANEL defaultPanel); // returns the Default panel + + // sets the panel that owns this FocusNavGroup to be the root in the focus traversal heirarchy + // focus change via KEY_TAB will only travel to children of this main panel + virtual void SetFocusTopLevel(bool state); + + virtual void SetCurrentDefaultButton(VPANEL panel, bool sendCurrentDefaultButtonMessage = true); +private: + bool CanButtonBeDefault(VPANEL panel); + + VPanelHandle _defaultButton; + VPanelHandle _currentDefaultButton; + VPanelHandle _currentFocus; + + Panel *_mainPanel; + bool _topLevelFocus; +}; + +} // namespace vgui + +#endif // FOCUSNAVGROUP_H diff --git a/public/vgui_controls/Frame.h b/public/vgui_controls/Frame.h new file mode 100644 index 0000000..9487403 --- /dev/null +++ b/public/vgui_controls/Frame.h @@ -0,0 +1,265 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VGUI_FRAME_H +#define VGUI_FRAME_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace vgui +{ + +class FrameButton; +class FrameSystemButton; + +//----------------------------------------------------------------------------- +// Purpose: Windowed frame +//----------------------------------------------------------------------------- +class Frame : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( Frame, EditablePanel ); + +public: + Frame(Panel *parent, const char *panelName, bool showTaskbarIcon = true, bool bPopup = true ); + virtual ~Frame(); + + // Set the text in the title bar. Set surfaceTitle=true if you want this to be the taskbar text as well. + virtual void SetTitle(const char *title, bool surfaceTitle); + virtual void SetTitle(const wchar_t *title, bool surfaceTitle); + + // Bring the frame to the front and requests focus, ensures it's not minimized + virtual void Activate(); + + // activates the dialog; if dialog is not currently visible it starts it minimized and flashing in the taskbar + virtual void ActivateMinimized(); + + // closes the dialog + MESSAGE_FUNC( Close, "Close" ); + MESSAGE_FUNC( CloseModal, "CloseModal" ); + + // sets the dialog to delete self on close + virtual void SetDeleteSelfOnClose( bool state ); + + // Move the dialog to the center of the screen + virtual void MoveToCenterOfScreen(); + + // Set the movability of the panel + virtual void SetMoveable(bool state); + // Check the movability of the panel + virtual bool IsMoveable(); + + // Set the resizability of the panel + virtual void SetSizeable(bool state); + // Check the resizability of the panel + virtual bool IsSizeable(); + // Toggle visibility of the system menu button + virtual void SetMenuButtonVisible(bool state); + void SetMenuButtonResponsive(bool state); + + // Toggle visibility of the minimize button + virtual void SetMinimizeButtonVisible(bool state); + // Toggle visibility of the maximize button + virtual void SetMaximizeButtonVisible(bool state); + // Toggles visibility of the minimize-to-systray icon (defaults to false) + virtual void SetMinimizeToSysTrayButtonVisible(bool state); + + // Toggle visibility of the close button + virtual void SetCloseButtonVisible(bool state); + + // returns true if the dialog is currently minimized + virtual bool IsMinimized(); + // Flash the window system tray button until the frame gets focus + virtual void FlashWindow(); + // Stops any window flashing + virtual void FlashWindowStop(); + // command handling + virtual void OnCommand(const char *command); + + // Get the system menu + virtual Menu *GetSysMenu(); + // Set the system menu + virtual void SetSysMenu(Menu *menu); + + // Set the system menu images + void SetImages( const char *pEnabledImage, const char *pDisabledImage = NULL ); + + // set whether the title bar should be rendered + virtual void SetTitleBarVisible( bool state ); + + // When moving via caption, don't let any part of window go outside parent's bounds + virtual void SetClipToParent( bool state ); + virtual bool GetClipToParent() const; + + // Set to true to make the caption height small + virtual void SetSmallCaption( bool state ); + virtual bool IsSmallCaption() const; + + virtual int GetDraggerSize(); + virtual int GetCornerSize(); + virtual int GetBottomRightSize(); + virtual int GetCaptionHeight(); + + /* CUSTOM MESSAGE HANDLING + "SetTitle" + input: "text" - string to set the title to be + */ + + // Load the control settings + virtual void LoadControlSettings( const char *dialogResourceName, const char *pathID = NULL, KeyValues *pPreloadedKeyValues = NULL, KeyValues *pConditions = NULL ); + + void SetChainKeysToParent( bool state ); + bool CanChainKeysToParent() const; + + // Shows the dialog in a modal fashion + virtual void DoModal(); + + void PlaceUnderCursor( ); + + // Disables the fade-in/out-effect even if configured in the scheme settings + void DisableFadeEffect( void ); + + // Temporarily enables or disables the fade effect rather than zeroing the fade times as done in DisableFadeEffect + void SetFadeEffectDisableOverride( bool disabled ); + + virtual void OnGripPanelMoved( int nNewX, int nNewY, int nNewW, int nNewH ); + virtual void OnGripPanelMoveFinished() {} + +protected: + // Respond to mouse presses + virtual void OnMousePressed(MouseCode code); + // Respond to Key typing + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnKeyTyped(wchar_t unichar); + // Respond to Key releases + virtual void OnKeyCodeReleased(KeyCode code); + // Respond to Key focus ticks + virtual void OnKeyFocusTicked(); + virtual void ApplySchemeSettings(IScheme *pScheme); + // Recalculate the position of all items + virtual void PerformLayout(); + // Respond when a close message is recieved. Can be called directly to close a frame. + virtual void OnClose(); + // Respond to a window finishing its closure. i.e. when a fading window has fully finished its fadeout. + virtual void OnFinishedClose(); + // Minimize the window on the taskbar. + MESSAGE_FUNC( OnMinimize, "Minimize" ); + // Called when minimize-to-systray button is pressed (does nothing by default) + virtual void OnMinimizeToSysTray(); + // the frame close button was pressed + MESSAGE_FUNC( OnCloseFrameButtonPressed, "CloseFrameButtonPressed" ); + // Add the child to the focus nav group + virtual void OnChildAdded(VPANEL child); + // settings + virtual void ApplySettings(KeyValues *inResourceData); + // records the settings into the resource data + virtual void GetSettings(KeyValues *outResourceData); + virtual const char *GetDescription( void ); + + // gets the default position and size on the screen to appear the first time (defaults to centered) + virtual bool GetDefaultScreenPosition(int &x, int &y, int &wide, int &tall); + + // painting + virtual void PaintBackground(); + + // per-frame thinking, used for transition effects + virtual void OnThink(); + + // screen size + virtual void OnScreenSizeChanged(int iOldWide, int iOldTall); + + // Get the size of the panel inside the frame edges. + virtual void GetClientArea(int &x, int &y, int &wide, int &tall); + + // user configuration settings + // this is used for any control details the user wants saved between sessions + // eg. dialog positions, last directory opened, list column width + virtual void ApplyUserConfigSettings(KeyValues *userConfig); + + // returns user config settings for this control + virtual void GetUserConfigSettings(KeyValues *userConfig); + + // optimization, return true if this control has any user config settings + virtual bool HasUserConfigSettings(); + + virtual void GetSizerClientArea(int &x, int &y, int &wide, int &tall); + +private: + MESSAGE_FUNC_CHARPTR( InternalSetTitle, "SetTitle", text ); + MESSAGE_FUNC( InternalFlashWindow, "FlashWindow" ); + MESSAGE_FUNC_PARAMS( OnDialogVariablesChanged, "DialogVariables", dialogVariables ); + + void SetupResizeCursors(); + void LayoutProportional( FrameButton *bt); + void FinishClose(); + void OnFrameFocusChanged(bool bHasFocus); + + Color _titleBarBgColor; + Color _titleBarDisabledBgColor; + Color _titleBarFgColor; + Color _titleBarDisabledFgColor; + Color m_InFocusBgColor; + Color m_OutOfFocusBgColor; + TextImage *_title; + +#if !defined( _X360 ) + Panel *_topGrip; + Panel *_bottomGrip; + Panel *_leftGrip; + Panel *_rightGrip; + Panel *_topLeftGrip; + Panel *_topRightGrip; + Panel *_bottomLeftGrip; + Panel *_bottomRightGrip; + Panel *_captionGrip; + FrameButton *_minimizeButton; + FrameButton *_maximizeButton; + FrameButton *_minimizeToSysTrayButton; + FrameButton *_closeButton; + FrameSystemButton *_menuButton; + Menu *_sysMenu; +#endif + + float m_flTransitionEffectTime; + float m_flFocusTransitionEffectTime; + int m_iClientInsetX; + int m_iClientInsetY; + int m_iTitleTextInsetX; + int m_nGripperWidth; + VPANEL m_hPreviousModal; + HFont m_hCustomTitleFont; + + bool _sizeable : 1; + bool _moveable : 1; + bool m_bHasFocus : 1; + bool _flashWindow : 1; + bool _nextFlashState : 1; + bool _drawTitleBar : 1; + bool m_bPreviouslyVisible : 1; + bool m_bFadingOut : 1; + bool m_bDeleteSelfOnClose : 1; + bool m_bDisableFadeEffect : 1; + bool m_bClipToParent : 1; + bool m_bSmallCaption : 1; + bool m_bChainKeysToParent : 1; + bool m_bPrimed : 1; + bool m_iClientInsetXOverridden : 1; + + CPanelAnimationVarAliasType( int, m_iTitleTextInsetXOverride, "titletextinsetX", "0", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTitleTextInsetYOverride, "titletextinsetY", "0", "proportional_int" ); +}; + +} // namespace vgui + +#endif // VGUI_FRAME_H diff --git a/public/vgui_controls/GraphPanel.h b/public/vgui_controls/GraphPanel.h new file mode 100644 index 0000000..31d23ea --- /dev/null +++ b/public/vgui_controls/GraphPanel.h @@ -0,0 +1,81 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef GRAPHPANEL_H +#define GRAPHPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "UtlLinkedList.h" +#include "UtlVector.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Holds and displays a chart +//----------------------------------------------------------------------------- +class GraphPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( GraphPanel, Panel ); + +public: + GraphPanel(Panel *parent, const char *name); + + // domain settings (x-axis settings) + // sets the window of samples to display + void SetDisplayDomainSize(float size); + // sets the range of samples the graph should keep + // should be set to the max you would set the display domain size + void SetMaxDomainSize(float size); + // sets the minimum domain that will be displayed; used to collapse samples + void SetMinDomainSize(float size); + + // range settings (y-axis settings) + void SetUseFixedRange(float lowRange, float highRange); + void SetUseDynamicRange(float *rangeList, int numRanges); + void GetDisplayedRange(float &lowRange, float &highRange); + + // adds an item to the end of the list + // sampleEnd is assumed to be the trailing edge of the sample + // assumes that the samples are fairly evenly spaced (not much more work to do to fix this though) + void AddItem(float sampleEnd, float sampleValue); + +protected: + virtual void Paint(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + +private: + int GetVisibleItemCount(); + + struct Sample_t + { + float sampleEnd; + float value; + }; + CUtlLinkedList m_Samples; + + // the window to show + float m_flDomainSize; + float m_flMaxDomainSize, m_flMinDomainSize; + bool m_bMaxDomainSizeSet; + + // range + float m_flLowRange, m_flHighRange; + bool m_bUseDynamicRange; + CUtlVector m_RangeList; + + // rendering + int m_iGraphBarWidth; + int m_iGraphBarGapWidth; +}; + +} // namespace vgui + +#endif // GRAPHPANEL_H diff --git a/public/vgui_controls/HTML.h b/public/vgui_controls/HTML.h new file mode 100644 index 0000000..c90a113 --- /dev/null +++ b/public/vgui_controls/HTML.h @@ -0,0 +1,138 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Creates a HTML control +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef HTML_H +#define HTML_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Control to display HTML content +// This control utilises a hidden IE window to render a HTML page for you. +// It can load any valid URL (i.e local files or web pages), you cannot dynamically change the +// content however (internally to the control that is). +//----------------------------------------------------------------------------- +class HTML: public Panel, public IHTMLEvents +{ + DECLARE_CLASS_SIMPLE( HTML, Panel ); + +public: + + HTML(Panel *parent,const char *name, bool allowJavaScript = false); + ~HTML(); + + // start and stop the HTML control repainting itself periodically + void StartAnimate(int time); + void StopAnimate(); + + // IHTML pass through functions + virtual void OpenURL(const char *URL, bool force = false); // force means ignore the offline mod override + virtual bool StopLoading(); + virtual bool Refresh(); + virtual void Clear(); + virtual void AddText(const char *text); + virtual void SetVisible( bool state ); + virtual void OnMove(); + + // configuration + virtual void SetScrollbarsEnabled(bool state); + virtual void SetContextMenuEnabled(bool state); + virtual void NewWindowsOnly( bool state ); + + // events callbacks + // Called when the control wishes to go to a new URL (either through the OpenURL() method or internally triggered). + // Return TRUE to allow it to continue and FALSE to stop it loading the URL. Target is the frame this page will load in + virtual bool OnStartURL(const char *url, const char *target, bool first); + // Called when a page has finished loading an is being render. If you derive from this make SURE to + // call this method also (i.e call Baseclass::OnFinishURL(url) in your derived version). + virtual void OnFinishURL(const char *url); + // Called as a page is loading. Maximum is the total number of "tick" events in the progress count, + // current is the current amount you are into the progress. This can be used to update another + // on screen control to show that the browser is active. + virtual void OnProgressURL(long current, long maximum); + // Called when IE has some status text it wishes to display. You can use this to provide status information + // in another control. + virtual void OnSetStatusText(const char *text); + virtual void OnUpdate(); + virtual void OnLink(); + virtual void OffLink(); + + // url handlers, lets you have web page links fire vgui events + // use to have custom web page links, eg. "steam://open/subscriptionpage" + // everything after the "://" is sent to the watcher in a message "CustomURL", "url", "protocol" + virtual void AddCustomURLHandler(const char *customProtocolName, vgui::Panel *target); + + // overridden to paint our special web browser texture + virtual void PaintBackground(); + + // pass messages to the texture component to tell it about resizes + virtual void OnSizeChanged(int wide,int tall); + // for animation task + virtual void OnTick(); + + // pass mouse clicks through + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnCursorMoved(int x,int y); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnKeyTyped(wchar_t unichar); + virtual void OnKeyCodePressed(KeyCode code); + virtual void PerformLayout(); + virtual void OnMouseWheeled(int delta); + + vgui::ScrollBar *GetVerticalScrollBar( void ) { return _vbar; } + vgui::ScrollBar *GetHorizontalScrollBar( void ) { return _hbar; } + +protected: + virtual void ApplySchemeSettings(IScheme *pScheme); + +private: + virtual void BrowserResize(); + virtual void CalcScrollBars(int w,int h); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); + + IHTML *browser; // the interface to the browser itself + + vgui::Label *loading; + IImage *picture; + vgui::ScrollBar *_hbar,*_vbar; + + int m_iMouseX,m_iMouseY; // where the mouse is on the control + long m_iNextFrameTime; // next time (in milliseconds) to repaint + int m_iAnimTime; // the time between repaints (in milliseconds) + int m_iScrollX,m_iScrollY; + int m_iScrollBorderX,m_iScrollBorderY; + bool m_bRegenerateHTMLBitmap; + + bool m_bScrollBarEnabled; + bool m_bContextMenuEnabled; + int m_iScrollbarSize; + bool m_bNewWindowsOnly; + bool m_bSetVisibleOnPerformLayout; + + struct CustomURLHandler_t + { + PHandle hPanel; + char url[32]; + }; + CUtlVector m_CustomURLHandlers; +}; + +} // namespace vgui + +#endif // HTML_H diff --git a/public/vgui_controls/Image.h b/public/vgui_controls/Image.h new file mode 100644 index 0000000..ff4ba8a --- /dev/null +++ b/public/vgui_controls/Image.h @@ -0,0 +1,84 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMAGE_H +#define IMAGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +class Panel; + +//----------------------------------------------------------------------------- +// Purpose: Basic image control +//----------------------------------------------------------------------------- +class Image : public IImage +{ +public: + Image(); + virtual ~Image(); + + // Set the position of the image + virtual void SetPos( int x, int y ); + // Get the position of the image + virtual void GetPos( int &x, int &y ); + // Get the size of the image + virtual void GetSize( int &wide, int &tall ); + virtual void GetContentSize( int &wide, int &tall ); + // Set the draw color + virtual void SetColor( Color color ); + // set the background color + virtual void SetBkColor( Color color ) { DrawSetColor( color ); } + // Get the draw color + virtual Color GetColor(); + virtual bool Evict(); + virtual int GetNumFrames(); + virtual void SetFrame( int nFrame ); + virtual HTexture GetID(); + virtual void SetRotation( int iRotation ) { return; }; + + // Get the size of the image + virtual int GetTall(); + virtual int GetWide(); + +protected: + virtual void SetSize(int wide, int tall); + virtual void DrawSetColor(Color color); + virtual void DrawSetColor(int r, int g, int b, int a); + virtual void DrawFilledRect(int x0, int y0, int x1, int y1); + virtual void DrawOutlinedRect(int x0, int y0, int x1, int y1); + virtual void DrawLine(int x0,int y0,int x1,int y1); + virtual void DrawPolyLine(int *px, int *py, int numPoints); + virtual void DrawSetTextFont(HFont font); + virtual void DrawSetTextColor(Color color); + virtual void DrawSetTextColor(int r, int g, int b, int a); + virtual void DrawSetTextPos(int x,int y); + virtual void DrawPrintText(const wchar_t *str, int strlen); + virtual void DrawPrintText(int x, int y, const wchar_t *str, int strlen); + virtual void DrawPrintChar(wchar_t ch); + virtual void DrawPrintChar(int x, int y, wchar_t ch); + virtual void DrawSetTexture(int id); + virtual void DrawTexturedRect(int x0, int y0, int x1, int y1); + virtual void Paint() = 0; + +private: + int _pos[2]; + int _size[2]; + Color _color; +}; + +} // namespace vgui + +#endif // IMAGE_H diff --git a/public/vgui_controls/ImageList.h b/public/vgui_controls/ImageList.h new file mode 100644 index 0000000..be4f12f --- /dev/null +++ b/public/vgui_controls/ImageList.h @@ -0,0 +1,56 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMAGELIST_H +#define IMAGELIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: holds a collection of images +// used by controls so that images can be refered to by indices +//----------------------------------------------------------------------------- +class ImageList +{ +public: + ImageList(bool deleteImagesWhenDone); + ~ImageList(); + + // adds a new image to the list, returning the index it was placed at + int AddImage(vgui::IImage *image); + + // returns the number of images + int GetImageCount(); + + // returns true if an index is valid + bool IsValidIndex(int imageIndex); + + // sets an image at a specified index, growing and adding NULL images if necessary + void SetImageAtIndex(int index, vgui::IImage *image); + + // gets an image, imageIndex is of range [0, GetImageCount) + // image index 0 is always the blank image + vgui::IImage *GetImage(int imageIndex); + +private: + CUtlVector m_Images; + bool m_bDeleteImagesWhenDone; +}; + + +} // namespace vgui + +#endif // IMAGELIST_H diff --git a/public/vgui_controls/ImagePanel.h b/public/vgui_controls/ImagePanel.h new file mode 100644 index 0000000..538638a --- /dev/null +++ b/public/vgui_controls/ImagePanel.h @@ -0,0 +1,88 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef IMAGEPANEL_H +#define IMAGEPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class IImage; + +//----------------------------------------------------------------------------- +// Purpose: Panel that holds a single image +//----------------------------------------------------------------------------- +class ImagePanel : public Panel +{ + DECLARE_CLASS_SIMPLE( ImagePanel, Panel ); +public: + ImagePanel(Panel *parent, const char *name); + ~ImagePanel(); + + virtual void SetImage(IImage *image); + virtual void SetImage(const char *imageName); + virtual IImage *GetImage(); + char *GetImageName(); + + // sets whether or not the image should scale to fit the size of the ImagePanel (defaults to false) + void SetShouldScaleImage( bool state ); + void SetScaleAmount( float scale ); + float GetScaleAmount( void ); + + // set the color to fill with, if no image is specified + void SetFillColor( Color col ); + Color GetFillColor(); + + virtual Color GetDrawColor( void ); + virtual void SetDrawColor( Color color ); + + virtual void ApplySettings(KeyValues *inResourceData); + + // unhooks and evicts image if possible, caller must re-establish + bool EvictImage(); + + int GetNumFrames(); + void SetFrame( int nFrame ); + int GetFrame() const; + + void SetRotation( int iRotation ) { m_iRotation = iRotation; } + +protected: + virtual void PaintBackground(); + virtual void GetSettings(KeyValues *outResourceData); + virtual const char *GetDescription(); + virtual void OnSizeChanged(int newWide, int newTall); + virtual void ApplySchemeSettings( IScheme *pScheme ); + +private: + IImage *m_pImage; + char *m_pszImageName; + char *m_pszFillColorName; + char *m_pszDrawColorName; + bool m_bScaleImage; + bool m_bTileImage; + bool m_bTileHorizontally; + bool m_bTileVertically; + float m_fScaleAmount; + Color m_FillColor; + Color m_DrawColor; + int m_iRotation; + + bool m_bFrameImage; + int m_iFrame; +}; + +} // namespace vgui + +#endif // IMAGEPANEL_H diff --git a/public/vgui_controls/InputDialog.h b/public/vgui_controls/InputDialog.h new file mode 100644 index 0000000..84444bb --- /dev/null +++ b/public/vgui_controls/InputDialog.h @@ -0,0 +1,152 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef INPUTDIALOG_H +#define INPUTDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class Label; +class Button; +class TextEntry; + + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog base class - just has context kv and ok/cancel buttons +//----------------------------------------------------------------------------- +class BaseInputDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( BaseInputDialog, Frame ); + +public: + BaseInputDialog( vgui::Panel *parent, const char *title, bool bShowCancelButton = true ); + ~BaseInputDialog(); + + void DoModal( KeyValues *pContextKeyValues = NULL ); + +protected: + virtual void PerformLayout(); + virtual void PerformLayout( int x, int y, int w, int h ) {} + + // command buttons + virtual void OnCommand( const char *command ); + virtual void WriteDataToKeyValues( KeyValues *pKV, bool bOk ) {} + + void CleanUpContextKeyValues(); + KeyValues *m_pContextKeyValues; + vgui::Button *m_pCancelButton; + vgui::Button *m_pOKButton; +}; + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to ask yes/no questions of the user +//----------------------------------------------------------------------------- +class InputMessageBox : public BaseInputDialog +{ + DECLARE_CLASS_SIMPLE( InputMessageBox, BaseInputDialog ); + +public: + InputMessageBox( vgui::Panel *parent, const char *title, char const *prompt ); + ~InputMessageBox(); + +protected: + virtual void PerformLayout( int x, int y, int w, int h ); + +private: + vgui::Label *m_pPrompt; +}; + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to let user type in some text +//----------------------------------------------------------------------------- +class InputDialog : public BaseInputDialog +{ + DECLARE_CLASS_SIMPLE( InputDialog, BaseInputDialog ); + +public: + InputDialog( vgui::Panel *parent, const char *title, char const *prompt, char const *defaultValue = "" ); + ~InputDialog(); + + void SetMultiline( bool state ); + + /* action signals + + "InputCompleted" + "text" - the text entered + + "InputCanceled" + */ + void AllowNumericInputOnly( bool bOnlyNumeric ); + +protected: + virtual void PerformLayout( int x, int y, int w, int h ); + + // command buttons + virtual void WriteDataToKeyValues( KeyValues *pKV, bool bOk ); + +private: + vgui::Label *m_pPrompt; + vgui::TextEntry *m_pInput; +}; + +//----------------------------------------------------------------------------- +// Purpose: Utility dialog, used to let user specify multiple bool/float/string values +//----------------------------------------------------------------------------- +class MultiInputDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( MultiInputDialog, Frame ); + +public: + MultiInputDialog( Panel *pParent, const char *pTitle, const char *pOKText = "#VGui_OK", const char *pCancelText = "#VGui_Cancel" ); + ~MultiInputDialog(); + + void SetOKCommand ( KeyValues *pOKCommand ); // defaults to "InputCompleted" to match InputDialog + void SetCancelCommand( KeyValues *pCancelCommand ); // defaults to "InputCanceled" to match InputDialog (yes, this is spelled incorrectly) + + void AddText( const char *pText ); + CheckButton *AddEntry( const char *pName, const char *pPrompt, bool bDefaultValue ); + TextEntry *AddEntry( const char *pName, const char *pPrompt, float flDefaultValue ); + TextEntry *AddEntry( const char *pName, const char *pPrompt, const char *pDefaultValue ); + ComboBox *AddComboEntry( const char *pName, const char *pPrompt, int nLines, bool bAllowEdit ); + + virtual void DoModal(); + virtual void PerformLayout(); + virtual void OnCommand( const char *pCommand ); + +protected: + void WriteDataToKeyValues( KeyValues *pKV ); + void PerformLayout( int x, int y, int w, int h ); + int GetLabelWidth(); + int GetContentWidth(); + Label *AddLabel( const char *pText ); + TextEntry *AddTextEntry( const char *pName, const char *pDefaultValue ); + + enum EntryType_t { T_NONE, T_BOOL, T_FLOAT, T_STRING, T_COMBO }; + + CUtlVector< Label* > m_prompts; + CUtlVector< Panel* > m_inputs; // TextEntry or CheckButton or ComboBox + CUtlVector< EntryType_t > m_entryTypes; + + Button *m_pOKButton; + Button *m_pCancelButton; + + KeyValues *m_pOKCommand; + KeyValues *m_pCancelCommand; + + int m_nCurrentTabPosition; +}; + + +} // namespace vgui + +#endif // INPUTDIALOG_H diff --git a/public/vgui_controls/KeyBindingHelpDialog.h b/public/vgui_controls/KeyBindingHelpDialog.h new file mode 100644 index 0000000..5814dae --- /dev/null +++ b/public/vgui_controls/KeyBindingHelpDialog.h @@ -0,0 +1,63 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef KEYBINDINGHELPDIALOG_H +#define KEYBINDINGHELPDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "vgui/KeyCode.h" + +namespace vgui +{ + +class ListPanel; +class CKeyBoardEditorDialog; + +//----------------------------------------------------------------------------- +// Purpose: Dialog for use in editing keybindings +//----------------------------------------------------------------------------- +class CKeyBindingHelpDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( CKeyBindingHelpDialog, Frame ); + +public: + CKeyBindingHelpDialog( Panel *parent, Panel *panelToView, KeyBindingContextHandle_t handle, KeyCode code, int modifiers ); + ~CKeyBindingHelpDialog(); + + virtual void OnCommand( char const *cmd ); + virtual void OnKeyCodeTyped(vgui::KeyCode code); + + // The key originally bound to help was pressed + void HelpKeyPressed(); +private: + + virtual void OnTick(); + + bool IsHelpKeyStillBeingHeld(); + + void PopulateList(); + void GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ); + + void AnsiText( char const *token, char *out, size_t buflen ); + + vgui::PHandle m_hPanel; + KeyBindingContextHandle_t m_Handle; + KeyCode m_KeyCode; + int m_Modifiers; + + ListPanel *m_pList; + double m_flShowTime; + bool m_bPermanent; + + DHANDLE< CKeyBoardEditorDialog > m_hKeyBindingsEditor; +}; + +} + +#endif // KEYBINDINGHELPDIALOG_H diff --git a/public/vgui_controls/KeyBindingMap.h b/public/vgui_controls/KeyBindingMap.h new file mode 100644 index 0000000..a4d5443 --- /dev/null +++ b/public/vgui_controls/KeyBindingMap.h @@ -0,0 +1,224 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef KEYBINDINGMAP_H +#define KEYBINDINGMAP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" + +// more flexible than default pointers to members code required for casting member function pointers +#pragma pointers_to_members( full_generality, virtual_inheritance ) + +namespace vgui +{ + +class Panel; + +enum +{ + MODIFIER_SHIFT = ( 1 << 0 ), + MODIFIER_CONTROL = ( 1 << 1 ), + MODIFIER_ALT = ( 1 << 2 ), +}; + +//----------------------------------------------------------------------------- +// Purpose: An actual keybinding, where bindingname references a bindingmap mentioned below +//----------------------------------------------------------------------------- +struct BoundKey_t +{ + BoundKey_t(); + BoundKey_t( const BoundKey_t& src ); + ~BoundKey_t(); + BoundKey_t& operator =( const BoundKey_t& src ); + + bool isbuiltin; // whether this was by the #DECLARE macros or via code/parsing a config file + char const *bindingname; // what it's bound to + int keycode; // vgui keycode + int modifiers; // which modifiers +}; + +//----------------------------------------------------------------------------- +// Purpose: Single item in a message map +// Contains the information to map a string message name with parameters +// to a function call +//----------------------------------------------------------------------------- +struct KeyBindingMap_t +{ + KeyBindingMap_t(); + KeyBindingMap_t( const KeyBindingMap_t& src ); + ~KeyBindingMap_t(); + + char const *bindingname; // for the script file + ALIGN16 MessageFunc_t func; + char const *helpstring; // e.g., #KeybindingPasteHelp + char const *docstring; // e.g., #KeybindingPasteHelp + bool passive; // dispatch command, but still chain +}; + +#define DECLARE_KEYBINDINGMAP( className ) \ + static void KB_AddToMap \ + ( \ + char const *bindingname, \ + vgui::KeyCode defaultcode, \ + int default_modifiers, \ + vgui::MessageFunc_t function, \ + char const *helpstring, \ + char const *docstring, \ + bool passive \ + ) \ + { \ + vgui::PanelKeyBindingMap *map = vgui::FindOrAddPanelKeyBindingMap( GetPanelClassName() ); \ + \ + vgui::KeyBindingMap_t entry; \ + entry.bindingname = bindingname; \ + \ + entry.func = function; \ + \ + entry.helpstring = helpstring; \ + entry.docstring = docstring; \ + \ + entry.passive = passive; \ + \ + map->entries.AddToTail( entry ); \ + \ + vgui::BoundKey_t kb; \ + kb.isbuiltin = true; \ + kb.bindingname = bindingname; \ + kb.keycode = defaultcode; \ + kb.modifiers = default_modifiers; \ + map->defaultkeys.AddToTail( kb ); \ + map->boundkeys.AddToTail( kb ); \ + } \ + \ + static void KB_ChainToMap( void ) \ + { \ + static bool chained = false; \ + if ( chained ) \ + return; \ + chained = true; \ + vgui::PanelKeyBindingMap *map = vgui::FindOrAddPanelKeyBindingMap( GetPanelClassName() ); \ + map->pfnClassName = &GetPanelClassName; \ + if ( map && GetPanelBaseClassName() && GetPanelBaseClassName()[0] ) \ + { \ + map->baseMap = vgui::FindOrAddPanelKeyBindingMap( GetPanelBaseClassName() ); \ + } \ + } \ + \ + static void KB_AddBoundKey \ + ( \ + char const *bindingname, \ + int keycode, \ + int modifiers \ + ) \ + { \ + vgui::PanelKeyBindingMap *map = vgui::FindOrAddPanelKeyBindingMap( GetPanelClassName() ); \ + vgui::BoundKey_t kb; \ + kb.isbuiltin = true; \ + kb.bindingname = bindingname; \ + kb.keycode = keycode; \ + kb.modifiers = modifiers; \ + map->defaultkeys.AddToTail( kb ); \ + map->boundkeys.AddToTail( kb ); \ + } \ + \ + class className##_RegisterKBMap; \ + friend class className##_RegisterKBMap; \ + class className##_RegisterKBMap \ + { \ + public: \ + className##_RegisterKBMap() \ + { \ + className::KB_ChainToMap(); \ + } \ + }; \ + className##_RegisterKBMap m_RegisterClassKB; \ + \ + virtual vgui::PanelKeyBindingMap *GetKBMap() \ + { \ + static vgui::PanelKeyBindingMap *s_pMap = vgui::FindOrAddPanelKeyBindingMap( GetPanelClassName() ); \ + return s_pMap; \ + } + +#define _KBMapFuncCommonFunc( name, keycode, modifiers, function, help, doc, passive ) \ + class PanelKBMapFunc_##name; \ + friend class PanelKBMapFunc_##name; \ + class PanelKBMapFunc_##name \ + { \ + public: \ + static void InitVar() \ + { \ + static bool bAdded = false; \ + if ( !bAdded ) \ + { \ + bAdded = true; \ + KB_AddToMap( #name, keycode, modifiers, (vgui::MessageFunc_t)&ThisClass::function, help, doc, passive ); \ + } \ + } \ + PanelKBMapFunc_##name() \ + { \ + PanelKBMapFunc_##name::InitVar(); \ + } \ + }; \ + PanelKBMapFunc_##name m_##name##_register; + +#define _KBBindKeyCommon( name, keycode, modifiers, _classname ) \ + class PanelKBBindFunc_##_classname; \ + friend class PanelKBBindFunc_##_classname; \ + class PanelKBBindFunc_##_classname \ + { \ + public: \ + static void InitVar() \ + { \ + static bool bAdded = false; \ + if ( !bAdded ) \ + { \ + bAdded = true; \ + KB_AddBoundKey( #name, keycode, modifiers ); \ + } \ + } \ + PanelKBBindFunc_##_classname() \ + { \ + PanelKBBindFunc_##_classname::InitVar(); \ + } \ + }; \ + PanelKBBindFunc_##_classname m_##_classname##_bindkey_register; + +#define KEYBINDING_FUNC( name, keycode, modifiers, function, help, doc ) _KBMapFuncCommonFunc( name, keycode, modifiers, function, help, doc, false ); virtual void function() +#define KEYBINDING_FUNC_NODECLARE( name, keycode, modifiers, function, help, doc ) _KBMapFuncCommonFunc( name, keycode, modifiers, function, help, doc, false ); +#define KEYBINDING_FUNC_PASSIVE( name, keycode, modifiers, function, help, doc ) _KBMapFuncCommonFunc( name, keycode, modifiers, function, help, doc, true ); virtual void function() +#define KEYBINDING_FUNC_PASSIVE_NODECLARE( name, keycode, modifiers, function, help, doc ) _KBMapFuncCommonFunc( name, keycode, modifiers, function, help, doc, true ); + +// For definding additional (non-default) keybindings +#define KEYBINDING_ADDBINDING( name, keycode, modifiers ) _KBBindKeyCommon( name, keycode, modifiers, name ); +#define KEYBINDING_ADDBINDING_MULTIPLE( name, keycode, modifiers, _classname ) _KBBindKeyCommon( name, keycode, modifiers, _classname ); + +// mapping, one per class +struct PanelKeyBindingMap +{ + PanelKeyBindingMap() + { + baseMap = NULL; + pfnClassName = NULL; + processed = false; + } + + CUtlVector< KeyBindingMap_t > entries; + bool processed; + PanelKeyBindingMap *baseMap; + CUtlVector< BoundKey_t > defaultkeys; + CUtlVector< BoundKey_t > boundkeys; + char const *(*pfnClassName)( void ); +}; + +PanelKeyBindingMap *FindPanelKeyBindingMap( char const *className ); +PanelKeyBindingMap *FindOrAddPanelKeyBindingMap( char const *className ); + +} // namespace vgui + +#endif // KEYBINDINGMAP_H diff --git a/public/vgui_controls/KeyBoardEditorDialog.h b/public/vgui_controls/KeyBoardEditorDialog.h new file mode 100644 index 0000000..ce61890 --- /dev/null +++ b/public/vgui_controls/KeyBoardEditorDialog.h @@ -0,0 +1,138 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef KEYBOARDEDITORDIALOG_H +#define KEYBOARDEDITORDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Frame.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" + +class VControlsListPanel; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Dialog for use in editing keybindings +//----------------------------------------------------------------------------- +class CKeyBoardEditorPage : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CKeyBoardEditorPage, EditablePanel ); + +public: + CKeyBoardEditorPage( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ); + ~CKeyBoardEditorPage(); + + void SetKeybindingsSaveFile( char const *filename, char const *pathID = 0 ); + + virtual void OnKeyCodeTyped(vgui::KeyCode code); + + virtual void ApplySchemeSettings( IScheme *scheme ); + + void OnSaveChanges(); + void OnRevert(); + void OnUseDefaults(); + +protected: + + virtual void OnPageHide(); + + virtual void OnCommand( char const *cmd ); + + void PopulateList(); + + void GetMappingList( Panel *panel, CUtlVector< PanelKeyBindingMap * >& maps ); + int GetMappingCount( Panel *panel ); + + void BindKey( vgui::KeyCode code ); + + // Trap row selection message + MESSAGE_FUNC( ItemSelected, "ItemSelected" ); + MESSAGE_FUNC_INT( OnClearBinding, "ClearBinding", item ); + + void SaveMappings(); + void UpdateCurrentMappings(); + void RestoreMappings(); + void ApplyMappings(); + +protected: + void AnsiText( char const *token, char *out, size_t buflen ); + + Panel *m_pPanel; + KeyBindingContextHandle_t m_Handle; + + VControlsListPanel *m_pList; + + struct SaveMapping_t + { + SaveMapping_t(); + SaveMapping_t( const SaveMapping_t& src ); + + PanelKeyBindingMap *map; + CUtlVector< BoundKey_t > current; + CUtlVector< BoundKey_t > original; + }; + + CUtlVector< SaveMapping_t * > m_Save; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Dialog for use in editing keybindings +//----------------------------------------------------------------------------- +class CKeyBoardEditorSheet : public PropertySheet +{ + DECLARE_CLASS_SIMPLE( CKeyBoardEditorSheet, PropertySheet ); + +public: + CKeyBoardEditorSheet( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ); + + void SetKeybindingsSaveFile( char const *filename, char const *pathID = 0 ); + + void OnSaveChanges(); + void OnRevert(); + void OnUseDefaults(); + +protected: + + vgui::PHandle m_hPanel; + KeyBindingContextHandle_t m_Handle; + bool m_bSaveToExternalFile; + CUtlSymbol m_SaveFileName; + CUtlSymbol m_SaveFilePathID; + Color m_clrAlteredItem; +}; + +//----------------------------------------------------------------------------- +// Purpose: Dialog for use in editing keybindings +//----------------------------------------------------------------------------- +class CKeyBoardEditorDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( CKeyBoardEditorDialog, Frame ); + +public: + CKeyBoardEditorDialog( Panel *parent, Panel *panelToEdit, KeyBindingContextHandle_t handle ); + + void SetKeybindingsSaveFile( char const *filename, char const *pathID = 0 ); + + virtual void OnCommand( char const *cmd ); + +private: + CKeyBoardEditorSheet *m_pKBEditor; + + Button *m_pSave; + Button *m_pCancel; + Button *m_pRevert; + Button *m_pUseDefaults; +}; + +} + +#endif // KEYBOARDEDITORDIALOG_H diff --git a/public/vgui_controls/Label.h b/public/vgui_controls/Label.h new file mode 100644 index 0000000..0066033 --- /dev/null +++ b/public/vgui_controls/Label.h @@ -0,0 +1,237 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LABEL_H +#define LABEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "utlvector.h" +#include "vgui/VGUI.h" +#include "vgui_controls/Panel.h" +#include "vgui_controls/PHandle.h" +#include "dmxloader/dmxelement.h" + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Contains and displays a set of images +// By default starts with one TextImage +//----------------------------------------------------------------------------- +class Label : public Panel +{ + DECLARE_CLASS_SIMPLE( Label, Panel ); + DECLARE_DMXELEMENT_UNPACK_NAMESPACE(vgui); + +public: + + // Constructors + Label(Panel *parent, const char *panelName, const char *text); + Label(Panel *parent, const char *panelName, const wchar_t *wszText); + ~Label(); + +public: + // Take the string and looks it up in the localization file to convert it to unicode + virtual void SetText(const char *tokenName); + + // Set unicode text directly + virtual void SetText(const wchar_t *unicodeString, bool bClearUnlocalizedSymbol = false ); + + // Get the current text + virtual void GetText(char *textOut, int bufferLen); + virtual void GetText(wchar_t *textOut, int bufLenInBytes); + + // Content alignment + // Get the size of the content within the label + virtual void GetContentSize(int &wide, int &tall); + + // Set how the content aligns itself within the label + // alignment code, used to determine how the images are layed out within the Label + enum Alignment + { + a_northwest = 0, + a_north, + a_northeast, + a_west, + a_center, + a_east, + a_southwest, + a_south, + a_southeast, + }; + + virtual void SetContentAlignment(Alignment alignment); + virtual void SetEnabled(bool state); + // Additional offset at the Start of the text (from whichever sides it is aligned) + virtual void SetTextInset(int xInset, int yInset); + + // Text colors + virtual void SetFgColor(Color color); + virtual Color GetFgColor(); + + // colors to use when the label is disabled + virtual void SetDisabledFgColor1(Color color); + virtual void SetDisabledFgColor2(Color color); + virtual Color GetDisabledFgColor1(); + virtual Color GetDisabledFgColor2(); + + // Set whether the text is displayed bright or dull + enum EColorState + { + CS_NORMAL, + CS_DULL, + CS_BRIGHT, + }; + virtual void SetTextColorState(EColorState state); + + // Font + virtual void SetFont(HFont font); + virtual HFont GetFont(); + + // Hotkey + virtual Panel *HasHotkey(wchar_t key); + virtual void SetHotkey(wchar_t key); + virtual wchar_t GetHotKey(); + + // Labels can be associated with controls, and alter behaviour based on the associates behaviour + // If the associate is disabled, so are we + // If the associate has focus, we may alter how we draw + // If we get a hotkey press or focus message, we forward the focus to the associate + virtual void SetAssociatedControl(Panel *control); + + // Multiple image handling + // Images are drawn from left to right across the label, ordered by index + // By default there is a TextImage in position 0 (see GetTextImage()/SetTextImageIndex()) + virtual int AddImage(IImage *image, int preOffset); // Return the index the image was placed in + virtual void SetImage(IImage *image, int preOffset ); // Clears all images and sets the only remaining image to the passed in image + virtual void SetImageAtIndex(int index, IImage *image, int preOffset); + virtual void SetImagePreOffset(int index, int preOffset); // Set the offset in pixels before the image + virtual IImage *GetImageAtIndex(int index); + virtual int GetImageCount(); + virtual void ClearImages(); + virtual void ResetToSimpleTextImage(); + // fixes the layout bounds of the image within the label + virtual void SetImageBounds(int index, int x, int width); + + // Teturns a pointer to the default text image + virtual TextImage *GetTextImage(); + + // Moves where the default text image is within the image array (it starts in position 0) + // Setting it to -1 removes it from the image list + // Returns the index the default text image was previously in + virtual int SetTextImageIndex(int newIndex); + + // Message handling + // outputData - keyName is the name of the attribute requested. + // for Labels "text" is an option that returns the default text image text + // returns true on success in finding the requested value. false on failure. + virtual bool RequestInfo(KeyValues *outputData); + /* INFO HANDLING + "GetText" + returns: + "text" - text contained in the label + */ + + /* CUSTOM MESSAGE HANDLING + "SetText" + input: "text" - label text is set to be this string + */ + + virtual void SizeToContents(); + + // the +8 is padding to the content size + // the code which uses it should really set that itself; + // however a lot of existing code relies on this + enum Padding + { + Content = 8, + }; + + void SetWrap( bool bWrap ); + void SetCenterWrap( bool bWrap ); + + void SetAllCaps( bool bAllCaps ); + + virtual void GetSizerMinimumSize(int &wide, int &tall); + +protected: + virtual void PerformLayout(); + virtual wchar_t CalculateHotkey(const char *text); + virtual wchar_t CalculateHotkey(const wchar_t *text); + virtual void ComputeAlignment(int &tx0, int &ty0, int &tx1, int &ty1); + virtual void Paint(); + MESSAGE_FUNC_PARAMS( OnSetText, "SetText", params ); + virtual void DrawDashedLine(int x0, int y0, int x1, int y1, int dashLen, int gapLen); + virtual void OnRequestFocus(VPANEL subFocus, VPANEL defaultPanel); + MESSAGE_FUNC( OnHotkeyPressed, "Hotkey" ); + virtual void OnMousePressed(MouseCode code); + virtual void OnSizeChanged(int wide, int tall); + + // makes sure that the maxIndex will be a valid index + virtual void EnsureImageCapacity(int maxIndex); + + // editing + virtual void ApplySchemeSettings(IScheme *pScheme); + +public: + virtual void GetSettings( KeyValues *outResourceData ); + virtual void ApplySettings( KeyValues *inResourceData ); + +protected: + virtual const char *GetDescription( void ); + + MESSAGE_FUNC_PARAMS( OnDialogVariablesChanged, "DialogVariables", dialogVariables ); + + void HandleAutoSizing( void ); + + // Derived can override to, e.g., recenter text image text if there is space. + virtual void RepositionTextImage( int &x, int &y, TextImage *pTextImage ) {} + + Alignment _contentAlignment; + +private: + + void Init(); + + TextImage *_textImage; // this is the textImage, if the full text will not + // fit we put as much as we can and add an elipsis (...) + struct TImageInfo + { + IImage *image; + short offset; + short xpos; + short width; + }; + CUtlVector _imageDar; + + int _textInset[2]; + Color _disabledFgColor1; + Color _disabledFgColor2; + Color _associateColor; + int _textImageIndex; // index in the image array that the default _textimage resides + EColorState _textColorState; + + PHandle _associate; + char *_associateName; + + char *_fontOverrideName; + + wchar_t _hotkey; // the hotkey contained in the text + + bool m_bWrap; + bool m_bCenterWrap; + bool m_bAllCaps; + bool m_bAutoWideToContents; + bool m_bAutoWideDirty; +}; + +} // namespace vgui + +#endif // LABEL_H diff --git a/public/vgui_controls/ListPanel.h b/public/vgui_controls/ListPanel.h new file mode 100644 index 0000000..b96c929 --- /dev/null +++ b/public/vgui_controls/ListPanel.h @@ -0,0 +1,374 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LISTPANEL_H +#define LISTPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include + +class KeyValues; + +namespace vgui +{ + +class ScrollBar; +class TextImage; +class ImagePanel; +class Label; +class Button; +class IDraggerEvent; +class FastSortListPanelItem; + +//----------------------------------------------------------------------------- +// Purpose: Generic class for ListPanel items +//----------------------------------------------------------------------------- +class ListPanelItem +{ +public: + ListPanelItem() : + kv( 0 ), + userData( 0 ), + m_pDragData( 0 ), + m_bImage( false ), + m_nImageIndex( -1 ), + m_nImageIndexSelected( -1 ), + m_pIcon( 0 ) + { + } + + KeyValues *kv; + unsigned int userData; + KeyValues *m_pDragData; + bool m_bImage; + int m_nImageIndex; + int m_nImageIndexSelected; + IImage *m_pIcon; +}; + +typedef int __cdecl SortFunc( + ListPanel *pPanel, + const ListPanelItem &item1, + const ListPanelItem &item2 ); + +//----------------------------------------------------------------------------- +// Purpose: A spread-sheet type data view, similar to MFC's +//----------------------------------------------------------------------------- +class ListPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( ListPanel, Panel ); + +public: + ListPanel(Panel *parent, const char *panelName); + ~ListPanel(); + + // COLUMN HANDLING + // all indices are 0 based, limit of 255 columns + // columns are resizable by default + enum ColumnFlags_e + { + COLUMN_FIXEDSIZE = 0x01, // set to have the column be a fixed size + COLUMN_RESIZEWITHWINDOW = 0x02, // set to have the column grow with the parent dialog growing + COLUMN_IMAGE = 0x04, // set if the column data is not text, but instead the index of the image to display + COLUMN_HIDDEN = 0x08, // column is hidden by default + COLUMN_UNHIDABLE = 0x10, // column is unhidable + }; + + // adds a column header + virtual void AddColumnHeader(int index, const char *columnName, const char *columnText, int startingWidth, int minWidth, int maxWidth, int columnFlags = 0); + virtual void AddColumnHeader(int index, const char *columnName, const char *columnText, int width, int columnFlags = 0); + + virtual void RemoveColumn(int column); // removes a column + virtual int FindColumn(const char *columnName); + virtual void SetColumnHeaderHeight( int height ); + virtual void SetColumnHeaderText(int column, const char *text); + virtual void SetColumnHeaderText(int column, wchar_t *text); + virtual void SetColumnHeaderImage(int column, int imageListIndex); + virtual void SetColumnHeaderTooltip(int column, const char *tooltipText); + virtual void SetColumnTextAlignment( int column, int align ); + + // Get information about the column headers. + virtual int GetNumColumnHeaders() const; + virtual bool GetColumnHeaderText( int index, char *pOut, int maxLen ); + + virtual void SetSortFunc(int column, SortFunc *func); + virtual void SetSortColumn(int column); + virtual void SortList( void ); + virtual void SetColumnSortable(int column, bool sortable); + virtual void SetColumnVisible(int column, bool visible); + int GetSortColumn() const; + + // sets whether the user can add/remove columns (defaults to off) + virtual void SetAllowUserModificationOfColumns(bool allowed); + + // DATA HANDLING + // data->GetName() is used to uniquely identify an item + // data sub items are matched against column header name to be used in the table + // Makes a copy of the data for use in the table. Returns the index the item is at. + virtual int AddItem(const KeyValues *data, unsigned int userData, bool bScrollToItem, bool bSortOnAdd); + // Unlike AddItem, this takes ownership of the KeyValues * and stores it in the list. Used when dragging from the table. Only used if the caller enables drag support + void SetItemDragData( int itemID, const KeyValues *data ); + virtual int GetItemCount( void ); // returns the number of VISIBLE items + virtual int GetItem(const char *itemName); // gets the row index of an item by name (data->GetName()) + virtual KeyValues *GetItem(int itemID); // returns pointer to data the row holds + virtual int GetItemCurrentRow(int itemID); // returns -1 if invalid index or item not visible + virtual int GetItemIDFromRow(int currentRow); // returns -1 if invalid row + virtual unsigned int GetItemUserData(int itemID); + virtual ListPanelItem *GetItemData(int itemID); + virtual void SetUserData( int itemID, unsigned int userData ); + virtual int GetItemIDFromUserData( unsigned int userData ); + virtual void ApplyItemChanges(int itemID); // applies any changes to the data, performed by modifying the return of GetItem() above + virtual void RemoveItem(int itemID); // removes an item from the table (changing the indices of all following items) + virtual void RereadAllItems(); // updates the view with the new data + + virtual void RemoveAll(); // clears and deletes all the memory used by the data items + virtual void DeleteAllItems(); // obselete, use RemoveAll(); + + virtual void GetCellText(int itemID, int column, wchar_t *buffer, int bufferSize); // returns the data held by a specific cell + virtual IImage *GetCellImage(int itemID, int column); //, ImagePanel *&buffer); // returns the image held by a specific cell + + // Use these until they return InvalidItemID to iterate all the items. + virtual int FirstItem() const; + virtual int NextItem( int iItem ) const; + + virtual int InvalidItemID() const; + virtual bool IsValidItemID(int itemID); + + // sets whether the dataitem is visible or not + // it is removed from the row list when it becomes invisible, but stays in the indexes + // this is much faster than a normal remove + virtual void SetItemVisible(int itemID, bool state); + virtual void SetItemDisabled(int itemID, bool state ); + bool IsItemVisible( int itemID ); + void SetAllVisible( bool state ); + + virtual void SetFont(HFont font); + + // image handling + virtual void SetImageList(ImageList *imageList, bool deleteImageListWhenDone); + + // SELECTION + + // returns the count of selected items + virtual int GetSelectedItemsCount(); + + // returns the selected item by selection index, valid in range [0, GetNumSelectedRows) + virtual int GetSelectedItem(int selectionIndex); + + // sets no item as selected + virtual void ClearSelectedItems(); + + virtual bool IsItemSelected( int itemID ); + + // adds a item to the select list + virtual void AddSelectedItem( int itemID ); + + // sets this single item as the only selected item + virtual void SetSingleSelectedItem( int itemID ); + + // returns the selected column, -1 for particular column selected + virtual int GetSelectedColumn(); + + // whether or not to select specific cells (off by default) + virtual void SetSelectIndividualCells(bool state); + + // whether or not multiple cells/rows can be selected + void SetMultiselectEnabled( bool bState ); + bool IsMultiselectEnabled() const; + + // sets a single cell - all other previous rows are cleared + virtual void SetSelectedCell(int row, int column); + + virtual bool GetCellAtPos(int x, int y, int &row, int &column); // returns true if any found, row and column are filled out. x, y are in screen space + virtual bool GetCellBounds( int row, int column, int& x, int& y, int& wide, int& tall ); + + // sets the text which is displayed when the list is empty + virtual void SetEmptyListText(const char *text); + virtual void SetEmptyListText(const wchar_t *text); + + // Move the scroll bar to a point where this item is visible + void ScrollToItem( int itemID ); + + // relayout the scroll bar in response to changing the items in the list panel + // do this if you RemoveAll() + void ResetScrollBar(); + + // Attaches drag data to a particular item + virtual void OnCreateDragData( KeyValues *msg ); + + void SetIgnoreDoubleClick( bool state ); + + // set up a field for editing + virtual void EnterEditMode(int itemID, int column, vgui::Panel *editPanel); + + // leaves editing mode + virtual void LeaveEditMode(); + + // returns true if we are currently in inline editing mode + virtual bool IsInEditMode(); + + MESSAGE_FUNC_INT( ResizeColumnToContents, "ResizeColumnToContents", column ); + +#ifdef _X360 + virtual void NavigateTo(); +#endif + +protected: + // PAINTING + virtual Panel *GetCellRenderer(int row, int column); + + // overrides + virtual void OnMouseWheeled(int delta); + virtual void OnSizeChanged(int wide, int tall); + virtual void PerformLayout(); + virtual void Paint(); + virtual void PaintBackground(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnMousePressed( MouseCode code ); + virtual void OnMouseDoublePressed( MouseCode code ); +#ifdef _X360 + virtual void OnKeyCodePressed(KeyCode code); +#endif + virtual void OnKeyCodeTyped( KeyCode code ); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); + MESSAGE_FUNC_INT_INT( OnColumnResized, "ColumnResized", column, delta ); + MESSAGE_FUNC_INT( OnSetSortColumn, "SetSortColumn", column ); + MESSAGE_FUNC( OpenColumnChoiceMenu, "OpenColumnChoiceMenu" ); + MESSAGE_FUNC_INT( OnToggleColumnVisible, "ToggleColumnVisible", col ); + virtual float GetRowsPerPage(); + virtual int GetStartItem(); + + // user configuration + virtual void ApplyUserConfigSettings(KeyValues *userConfig); + virtual void GetUserConfigSettings(KeyValues *userConfig); + virtual bool HasUserConfigSettings(); + + /* MESSAGES SENT + "ItemSelected" - query which items are selected + "ItemDeselected" - query which items are selected + */ + +public: + virtual void SetSortColumnEx( int iPrimarySortColumn, int iSecondarySortColumn, bool bSortAscending ); + void GetSortColumnEx( int &iPrimarySortColumn, int &iSecondarySortColumn, bool &bSortAscending ) const; + + void SetVScrollBarTextures( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox ); + void SetHScrollBarTextures( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox ); +private: + // Cleans up allocations associated with a particular item + void CleanupItem( FastSortListPanelItem *data ); + + // adds the item into the column indexes + void IndexItem(int itemID); + + // Purpose: + void UpdateSelection( vgui::MouseCode code, int x, int y, int row, int column ); + + // Handles multiselect + void HandleMultiSelection( int itemID, int row, int column ); + + // Handles addselect + void HandleAddSelection( int itemID, int row, int column ); + + // pre-sorted columns + struct IndexItem_t + { + ListPanelItem *dataItem; + int duplicateIndex; + }; + typedef CUtlRBTree IndexRBTree_t; + + struct column_t + { + Button *m_pHeader; + int m_iMinWidth; + int m_iMaxWidth; + bool m_bResizesWithWindow; + Panel *m_pResizer; + SortFunc *m_pSortFunc; + bool m_bTypeIsText; + bool m_bHidden; + bool m_bUnhidable; + IndexRBTree_t m_SortedTree; + int m_nContentAlignment; + }; + + // list of the column headers + CUtlLinkedList m_ColumnsData; + + // persistent list of all columns ever created, indexes into m_ColumnsData - used for matching up DATAITEM m_SortedTreeIndexes + CUtlVector m_ColumnsHistory; + + // current list of columns, indexes into m_ColumnsData + CUtlVector m_CurrentColumns; + + int m_iColumnDraggerMoved; // which column dragger was moved->which header to resize + int m_lastBarWidth; + + CUtlLinkedList m_DataItems; + CUtlVector m_VisibleItems; + + // set to true if the table needs to be sorted before it's drawn next + int m_iSortColumn; + int m_iSortColumnSecondary; + + void ResortColumnRBTree(int col); + static bool RBTreeLessFunc(vgui::ListPanel::IndexItem_t &item1, vgui::ListPanel::IndexItem_t &item2); + + TextImage *m_pTextImage; // used in rendering + ImagePanel *m_pImagePanel; // used in rendering + Label *m_pLabel; // used in rendering + ScrollBar *m_hbar; + ScrollBar *m_vbar; + + int m_iSelectedColumn; + + bool m_bNeedsSort : 1; + bool m_bSortAscending : 1; + bool m_bSortAscendingSecondary : 1; + bool m_bCanSelectIndividualCells : 1; + bool m_bShiftHeldDown : 1; + bool m_bMultiselectEnabled : 1; + bool m_bAllowUserAddDeleteColumns : 1; + bool m_bDeleteImageListWhenDone : 1; + bool m_bIgnoreDoubleClick : 1; + + int m_iHeaderHeight; + int m_iRowHeight; + + // selection data + CUtlVector m_SelectedItems; // array of selected rows + int m_LastItemSelected; // remember the last row selected for future shift clicks + + int m_iTableStartX; + int m_iTableStartY; + + Color m_LabelFgColor; + Color m_DisabledColor; + Color m_SelectionFgColor; + Color m_DisabledSelectionFgColor; + + ImageList *m_pImageList; + TextImage *m_pEmptyListText; + + PHandle m_hEditModePanel; + int m_iEditModeItemID; + int m_iEditModeColumn; + + void ResetColumnHeaderCommands(); +}; + +} + +#endif // LISTPANEL_H diff --git a/public/vgui_controls/ListViewPanel.h b/public/vgui_controls/ListViewPanel.h new file mode 100644 index 0000000..8eb0253 --- /dev/null +++ b/public/vgui_controls/ListViewPanel.h @@ -0,0 +1,121 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef LISTVIEWPANEL_H +#define LISTVIEWPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class ListViewPanel; +typedef bool (*ListViewSortFunc_t)(KeyValues *kv1, KeyValues *kv2); + +class ListViewItem; + +//----------------------------------------------------------------------------- +// Purpose: List Ctrl Panel with each item having an icon and text after it +//----------------------------------------------------------------------------- +class ListViewPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( ListViewPanel, Panel ); + +public: + ListViewPanel(Panel *parent, const char *panelName); + ~ListViewPanel(); + + virtual int AddItem(const KeyValues *data, bool bScrollToItem, bool bSortOnAdd); + virtual int GetItemCount(); + virtual KeyValues *GetItem(int itemID); + virtual void ApplyItemChanges(int itemID); + virtual void RemoveItem(int itemID); + virtual void DeleteAllItems(); + virtual int GetItemIDFromPos(int iPos); // valid from [0, GetItemCount) + + virtual int InvalidItemID(); + virtual bool IsValidItemID(int itemID); + + virtual void ScrollToItem(int itemID); + + virtual void SetSortFunc(ListViewSortFunc_t func); + virtual void SortList(); + + // image handling + virtual void SetImageList(ImageList *imageList, bool deleteImageListWhenDone); + + virtual void SetFont(HFont font); + + // returns the count of selected items + virtual int GetSelectedItemsCount(); + + // returns the selected item by selection index, valid in range [0, GetNumSelectedRows) + virtual int GetSelectedItem(int selectionIndex); + + // sets no item as selected + virtual void ClearSelectedItems(); + + // adds a item to the select list + virtual void AddSelectedItem(int itemID); + + // sets this single item as the only selected item + virtual void SetSingleSelectedItem(int itemID); + +protected: + // overrides + virtual void OnMouseWheeled(int delta); + virtual void OnSizeChanged(int wide, int tall); + virtual void PerformLayout(); + virtual void Paint(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnMousePressed( MouseCode code); + virtual void OnMouseDoublePressed( MouseCode code); + virtual void OnKeyCodeTyped( KeyCode code); + virtual void OnKeyTyped(wchar_t unichar); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); + virtual int GetItemsPerColumn(); + +private: + ScrollBar *m_hbar; + + friend class ListViewItem; + void OnItemMousePressed(ListViewItem* pItem, MouseCode code); + void OnItemMouseDoublePressed(ListViewItem* pItem, MouseCode code); + int GetItemsMaxWidth(); + int GetItemIndex(int itemID); + void OnShiftSelect(int itemID); + void FinishKeyPress(int itemID); + + CUtlLinkedList m_DataItems; + CUtlVector m_SortedItems; + ListViewSortFunc_t m_pSortFunc; + + int m_iRowHeight; + HFont m_hFont; + + Color m_LabelFgColor; + Color m_SelectionFgColor; + + // selection data + CUtlVector m_SelectedItems; + int m_LastSelectedItemID; + int m_ShiftStartItemID; + + bool m_bNeedsSort; + bool m_bDeleteImageListWhenDone; + ImageList *m_pImageList; +}; + + +} + +#endif // LISTVIEWPANEL_H diff --git a/public/vgui_controls/Menu.h b/public/vgui_controls/Menu.h new file mode 100644 index 0000000..5c73e14 --- /dev/null +++ b/public/vgui_controls/Menu.h @@ -0,0 +1,351 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef MENU_H +#define MENU_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include + +namespace vgui +{ + +class MenuItem; +class ScrollBar; +class MenuSeparator; + +//----------------------------------------------------------------------------- +// Purpose: A menu is a list of items that can be selected with one click, navigated +// with arrow keys and/or hot keys, and have a lit behavior when mouse over. +// It is NOT the button which opens the menu, but only the menu itself. +// +// Behaviour spec: +// Menu navigation can be done in 2 modes, via keyboard keys and via mouse. +// Clicking on menu button opens menu. +// Only one item in a menu is highlighted at a time. +// Only one submenu in a menu is open at a time. +// Disabled menuitems get highlighted via mouse and keys but will not activate. +// +// Mouse: +// Moving mouse into a menuitem highlights it. +// If the menuitem has a cascading menu, the menu opens when the mouse enters +// the menuitem. The cascading menuitem stays highlighted while its menu is open. +// No submenu items are highlighted by default. +// Moving the mouse into another menuitem closes any previously open submenus in the list. +// Clicking once in the menu item activates the menu item and closes all menus. +// Moving the mouse off a menuitem unhighlights it. +// The scroll bar arrows can be used to move up/down the menu one item at a time. +// The clicking and dragging on the scroll bar nob also scrolls the menu items. +// If a highlighed menuitem scrolls off, and the user then begins navigating via keys, +// the menu will snap the scroll bar so the highlighted item is visible. +// If user has been navigating via keys, moving the mouse over a menu item +// highlights it. +// Mousewheel: +// You must have the mouse inside the menu/scroll bar to use the wheel. +// The mouse wheel moves the highlighted menuitem up or down the list. +// If the list has no scroll bar the wheel will cycle from the bottom of the list +// to the top of the list and vice versa. +// If the list has a scrollbar the mouse wheel will stop at the top or bottom +// of the list. +// If the mouse is over the scroll bar no items are highlighted. +// Keyboard: +// When a menu is opened, no items are highlighted. +// If a menuitem has a cascading menu it does not open when the item is highlighted. +// The down arrow selects the next item in the list. +// (first item if none are highlighted and there is a scrollbar). +// The up arrow selects the previous item in the list +// (first item if none are highlighted and there is a scrollbar, last item if none are +// highlighted and there is no scrollbar). +// Selecting a new menuitem closes any previously open submenus in the list. +// The enter key activates the selected item and closes all menus. +// If the selected item has a cascading menu, activating it opens its submenu. +// These may also be activated by pressing the right arrow. +// Pressing the left arrow closes the submenu. +// When the submenu is opened the cascading menuitem stays highlighted. +// No items in the submenu are highlighted when it is opened. +// +// Note: Cascading menuitems in menus with a scrollbar is not supported. +// Its a clunky UI and if we want this we should design a better solution, +// perhaps along the lines of how explorer's bookmarks does it. +// It currently functions, but there are some arm/disarm bugs. +// +// +//----------------------------------------------------------------------------- +class Menu : public Panel +{ + DECLARE_CLASS_SIMPLE( Menu, Panel ); + friend class MenuItem; +public: + enum MenuDirection_e + { + LEFT, + RIGHT, + UP, + DOWN, + CURSOR, // make the menu appear under the mouse cursor + ALIGN_WITH_PARENT, // make the menu appear under the parent + }; + + Menu(Panel *parent, const char *panelName); + ~Menu(); + + static void PlaceContextMenu( Panel *parent, Menu *menu ); + static void OnInternalMousePressed( Panel *other, MouseCode code ); + + virtual void PositionRelativeToPanel( Panel *reference, MenuDirection_e direction, int nAdditionalYOffset = 0, bool showMenu = false ); + + // the menu. For combo boxes, it's the edit/field, etc. etc. + + // Add a simple text item to the menu + virtual int AddMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData = NULL ); + virtual int AddMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData = NULL ); + + virtual int AddMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target , const KeyValues *userData = NULL); + virtual int AddMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target , const KeyValues *userData = NULL); + + virtual int AddMenuItem( const char *itemText, const char *command, Panel *target , const KeyValues *userData = NULL); + virtual int AddMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL ); + virtual int AddMenuItem( const char *itemText, Panel *target, const KeyValues *userData = NULL ); + + // Add a checkable item to the menu + virtual int AddCheckableMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData = NULL ); + virtual int AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData = NULL ); + + virtual int AddCheckableMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL ); + virtual int AddCheckableMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL ); + + virtual int AddCheckableMenuItem( const char *itemText, const char *command, Panel *target , const KeyValues *userData = NULL); + virtual int AddCheckableMenuItem( const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData = NULL ); + virtual int AddCheckableMenuItem( const char *itemText, Panel *target, const KeyValues *userData = NULL ); + + // Add a cascading menu item to the menu + virtual int AddCascadingMenuItem( const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + virtual int AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + + virtual int AddCascadingMenuItem( const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + virtual int AddCascadingMenuItem( const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + + virtual int AddCascadingMenuItem( const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + virtual int AddCascadingMenuItem( const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + virtual int AddCascadingMenuItem( const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData = NULL ); + + // Add a custom panel to the menu + virtual int AddMenuItem( MenuItem *panel ); + + virtual void AddSeparator(); + virtual void AddSeparatorAfterItem( int itemID ); + + // Sets the values of a menu item at the specified index + virtual void UpdateMenuItem(int itemID, const char *itemText,KeyValues *message, const KeyValues *userData = NULL); + virtual void UpdateMenuItem(int itemID, const wchar_t *wszItemText,KeyValues *message, const KeyValues *userData = NULL); + + virtual void MoveMenuItem( int itemID, int moveBeforeThisItemID ); + + virtual bool IsValidMenuID(int itemID); + virtual int GetInvalidMenuID(); + + KeyValues *GetItemUserData(int itemID); + void GetItemText(int itemID, wchar_t *text, int bufLenInBytes); + void GetItemText(int itemID, char *text, int bufLenInBytes); + + virtual void SetItemEnabled(const char *itemName, bool state); + virtual void SetItemEnabled(int itemID, bool state); + virtual void SetItemVisible(const char *itemName, bool visible); + virtual void SetItemVisible(int itemID, bool visible); + + // Remove a single item + void DeleteItem( int itemID ); + + // Clear the menu, deleting all the menu items within + void DeleteAllItems(); + + // Override the auto-width setting with a single fixed width + virtual void SetFixedWidth( int width ); + + // Sets the content alignment of all items in the menu + void SetContentAlignment( Label::Alignment alignment ); + + // sets the height of each menu item + virtual void SetMenuItemHeight(int itemHeight); + virtual int GetMenuItemHeight() const; + + // Set the max number of items visible (scrollbar appears with more) + virtual void SetNumberOfVisibleItems( int numItems ); + + // Set up the menu items layout + virtual void PerformLayout( void ); + + virtual void SetBorder(class IBorder *border); + virtual void ApplySchemeSettings(IScheme *pScheme); + + // Set type ahead behaviour + enum MenuTypeAheadMode + { + COMPAT_MODE = 0, + HOT_KEY_MODE, + TYPE_AHEAD_MODE, + }; + virtual void SetTypeAheadMode(MenuTypeAheadMode mode); + virtual int GetTypeAheadMode(); + + // Hotkey handling + virtual void OnKeyTyped(wchar_t unichar); + // Menu nagivation etc. + virtual void OnKeyCodeTyped( KeyCode code ); + + // Visibility + virtual void SetVisible(bool state); + + // Activates item in the menu list, as if that menu item had been selected by the user + virtual void ActivateItem(int itemID); + virtual void ActivateItemByRow(int row); + virtual int GetActiveItem(); // returns the itemID (not the row) of the active item + + // Return the number of items currently in the menu list + virtual int GetItemCount(); + + // return the menuID of the n'th item in the menu list, valid from [0, GetItemCount) + virtual int GetMenuID(int index); + + // Return the number of items currently visible in the menu list + int GetCurrentlyVisibleItemsCount(); + + MenuItem *GetMenuItem(int itemID); + void CloseOtherMenus(MenuItem *item); + virtual void OnKillFocus(); + + int GetMenuMode(); + enum MenuMode + { + MOUSE = 0, + KEYBOARD, + }; + + void SetCurrentlyHighlightedItem(int itemID); + int GetCurrentlyHighlightedItem(); + void ClearCurrentlyHighlightedItem(); + + // Set the checked state of a checkable menuItem + void SetMenuItemChecked(int itemID, bool state); + bool IsChecked(int index); // check if item is checked. + + + void SetMinimumWidth(int width); + int GetMinimumWidth(); + + // baseclass overrides to chain colors through to cascade menus + virtual void SetFgColor( Color newColor ); + virtual void SetBgColor( Color newColor ); + + virtual void SetFont( HFont font ); + + // Pass in NULL hotkey to remove hotkey + void SetCurrentKeyBinding( int itemID, char const *hotkey ); + + void ForceCalculateWidth(); + + void SetUseFallbackFont( bool bState, HFont hFallback ); + +protected: + // helper functions + int AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData); + int AddMenuItemKeyValuesCommand(MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData); + + // vgui result reporting + virtual void OnCommand( const char *command ); + MESSAGE_FUNC_PTR( OnMenuItemSelected, "MenuItemSelected", panel ); + virtual void AddScrollBar(); + virtual void RemoveScrollBar(); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); + virtual void Paint(); + virtual void LayoutMenuBorder(); + virtual void MakeItemsVisibleInScrollRange( int maxVisibleItems, int nNumPixelsAvailable ); + virtual void OnMouseWheeled(int delta); + // Alternate OnKeyTyped behaviors + virtual void OnHotKey(wchar_t unichar); + virtual void OnTypeAhead(wchar_t unichar); + + int CountVisibleItems(); + void ComputeWorkspaceSize( int& workWide, int& workTall ); + int ComputeFullMenuHeightWithInsets(); + + void CalculateWidth(); + + void LayoutScrollBar(); + void PositionCascadingMenu(); + void SizeMenuItems(); + void OnCursorMoved(int x, int y); + void OnKeyCodePressed(KeyCode code); + void OnMenuClose(); + MESSAGE_FUNC( OnKeyModeSet, "KeyModeSet" ); + + void SetCurrentlySelectedItem(MenuItem *item); + void SetCurrentlySelectedItem(int itemID); + MESSAGE_FUNC_INT( OnCursorEnteredMenuItem, "CursorEnteredMenuItem", VPanel); + MESSAGE_FUNC_INT( OnCursorExitedMenuItem, "CursorExitedMenuItem", VPanel); + + void MoveAlongMenuItemList(int direction, int loopCount); + + enum + { + DEFAULT_MENU_ITEM_HEIGHT = 22, // height of items in the menu + MENU_UP = -1, // used for moving up/down list of menu items in the menu + MENU_DOWN = 1 + }; + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, char *pchName ); +#endif // DBGFLAG_VALIDATE + +private: + MenuItem *GetParentMenuItem(); + + int m_iMenuItemHeight; + int m_iFixedWidth; + int m_iMinimumWidth; // a minimum width the menu has to be if it is not fixed width + int m_iNumVisibleLines; // number of items in menu before scroll bar adds on + ScrollBar *m_pScroller; + + CUtlLinkedList m_MenuItems; + + CUtlVector m_VisibleSortedItems; + CUtlVector m_SortedItems; // used for visual + CUtlVector m_Separators; // menu item ids after which separators should be shown + CUtlVector m_SeparatorPanels; + + bool _sizedForScrollBar: 1 ; // whether menu has been sized for a scrollbar + bool m_bUseFallbackFont : 1; + bool _recalculateWidth : 1; + + int _menuWide; + int m_iCurrentlySelectedItemID; + int m_iInputMode; + int m_iCheckImageWidth; // the size of the check box spot on a checkable menu. + int m_iProportionalScrollBarSize; + Label::Alignment m_Alignment; + Color _borderDark; + int m_iActivatedItem; + HFont m_hItemFont; + HFont m_hFallbackItemFont; + + // for managing type ahead + #define TYPEAHEAD_BUFSIZE 256 + MenuTypeAheadMode m_eTypeAheadMode; + wchar_t m_szTypeAheadBuf[TYPEAHEAD_BUFSIZE]; + int m_iNumTypeAheadChars; + double m_fLastTypeAheadTime; +}; + +} // namespace vgui + +#endif // MENU_H diff --git a/public/vgui_controls/MenuBar.h b/public/vgui_controls/MenuBar.h new file mode 100644 index 0000000..b1f780c --- /dev/null +++ b/public/vgui_controls/MenuBar.h @@ -0,0 +1,54 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MENUBAR_H +#define MENUBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class MenuBar : public Panel +{ + DECLARE_CLASS_SIMPLE( MenuBar, Panel ); + +public: + MenuBar(Panel *parent, const char *panelName); + ~MenuBar(); + + virtual void AddButton(MenuButton *button); // add button to end of menu list + virtual void AddMenu( const char *pButtonName, Menu *pMenu ); + + virtual void GetContentSize( int& w, int&h ); + +protected: + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnKeyTyped(wchar_t unichar); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void PerformLayout(); + virtual void Paint(); + MESSAGE_FUNC( OnMenuClose, "MenuClose" ); + MESSAGE_FUNC_INT( OnCursorEnteredMenuButton, "CursorEnteredMenuButton", VPanel); + +private: + CUtlVector m_pMenuButtons; + int m_nRightEdge; +}; + +} // namespace vgui + +#endif // MENUBAR_H + diff --git a/public/vgui_controls/MenuButton.h b/public/vgui_controls/MenuButton.h new file mode 100644 index 0000000..f7be3bc --- /dev/null +++ b/public/vgui_controls/MenuButton.h @@ -0,0 +1,82 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MENUBUTTON_H +#define MENUBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "vgui_controls/Menu.h" + +namespace vgui +{ + +class Menu; +class TextImage; + +//----------------------------------------------------------------------------- +// Purpose: Button that displays a menu when pressed +//----------------------------------------------------------------------------- +class MenuButton : public Button +{ + DECLARE_CLASS_SIMPLE( MenuButton, Button ); + +public: + MenuButton(Panel *parent, const char *panelName, const char *text); + ~MenuButton(); + + // functions designed to be overriden + virtual void OnShowMenu(Menu *menu) {} + virtual void OnHideMenu(Menu *menu) {} + virtual int OnCheckMenuItemCount() { return 0; } + + virtual void SetMenu(Menu *menu); + virtual void HideMenu(void); + virtual void DrawFocusBorder(int tx0, int ty0, int tx1, int ty1); + MESSAGE_FUNC( OnMenuClose, "MenuClose" ); + MESSAGE_FUNC_PARAMS( OnKillFocus, "KillFocus", kv ); // called after the panel loses the keyboard focus + virtual void DoClick(); + virtual void SetOpenOffsetY(int yOffset); + + virtual bool CanBeDefaultButton(void); + + // sets the direction in which the menu opens from the button, defaults to down + virtual void SetOpenDirection(Menu::MenuDirection_e direction); + + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnCursorEntered(); + + virtual void Paint(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void OnCursorMoved( int x, int y ); + + // This style is like the IE "back" button where the left side acts like a regular button, the the right side has a little + // combo box dropdown indicator and presents and submenu + void SetDropMenuButtonStyle( bool state ); + bool IsDropMenuButtonStyle() const; + + Menu *GetMenu(); + +private: + + Menu *m_pMenu; + Menu::MenuDirection_e m_iDirection; + + int _openOffsetY; // vertical offset of menu from the menu button + + bool m_bDropMenuButtonStyle : 1; + TextImage *m_pDropMenuImage; + int m_nImageIndex; +}; + +}; // namespace vgui + +#endif // MENUBUTTON_H diff --git a/public/vgui_controls/MenuItem.h b/public/vgui_controls/MenuItem.h new file mode 100644 index 0000000..d2f03c5 --- /dev/null +++ b/public/vgui_controls/MenuItem.h @@ -0,0 +1,138 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MENUITEM_H +#define MENUITEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +class IBorder; +class TextImage; +class Menu; +class Image; + +//----------------------------------------------------------------------------- +// Purpose: The items in a menu +// MenuItems MUST have the Menu class as parents. +//----------------------------------------------------------------------------- +class MenuItem : public Button +{ + DECLARE_CLASS_SIMPLE( MenuItem, Button ); + +public: + MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu = NULL, bool checkable = false); + MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu = NULL, bool checkable = false); + ~MenuItem(); + + virtual void Paint(); + + // Activate the menu item as if it had been selected by the user + virtual void FireActionSignal(); + + virtual bool CanBeDefaultButton(void); + + // Handle mouse cursor entering a MenuItem. + void OnCursorEntered(); + // Handle mouse cursor exiting a MenuItem. + void OnCursorExited(); + + // Close the cascading menu if we have one. + void CloseCascadeMenu(); + + // Pass kill focus events up to parent on loss of focus + MESSAGE_FUNC( OnKillFocus, "MenuClose" ); + + // Return true if this item triggers a cascading menu + bool HasMenu(); + + // Set the size of the text portion of the label. + void SetTextImageSize(int wide, int tall); + + //Return the size of the text portion of the label. + void GetTextImageSize(int &wide, int &tall); + + // Return the size of the arrow portion of the label. + void GetArrowImageSize(int &wide, int &tall); + + // Return the size of the check portion of the label. + void GetCheckImageSize(int &wide, int &tall); + + // Return the menu that this menuItem contains + Menu *GetMenu(); + + virtual void PerformLayout(); + + // Respond to cursor movement + void OnCursorMoved(int x, int y); + + // Highlight item + MESSAGE_FUNC( ArmItem, "ArmItem" ); + // Unhighlight item. + MESSAGE_FUNC( DisarmItem, "DisarmItem" ); + + // is the item highlighted? + bool IsItemArmed(); + + // Open cascading menu if there is one. + void OpenCascadeMenu(); + + bool IsCheckable(); + bool IsChecked(); + + // Set a checkable menuItem checked or unchecked. + void SetChecked(bool state); + + KeyValues *GetUserData(); + void SetUserData(const KeyValues *kv); + + int GetActiveItem() { if ( m_pCascadeMenu ) { return m_pCascadeMenu->GetActiveItem(); } else { return 0; }} + + Menu *GetParentMenu(); + + void SetCurrentKeyBinding( char const *keyName ); + + virtual void GetContentSize( int& cw, int &ch ); + +protected: + void OnKeyCodeReleased(KeyCode code); + void OnMenuClose(); + MESSAGE_FUNC( OnKeyModeSet, "KeyModeSet" ); + + // vgui overrides + virtual void Init( void ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + virtual void RepositionTextImage( int &x, int &y, TextImage *pTextImage ); + +private: + enum { CHECK_INSET = 6 }; + Menu *m_pCascadeMenu; // menu triggered to open upon selecting this menu item + bool m_bCheckable; // can this menu item have a little check to the left of it when you select it? + bool m_bChecked; // whether item is checked or not. + TextImage *m_pCascadeArrow; // little arrow that appears to the right of menuitems that open a menu + TextImage *m_pCheck; // the check that appears to the left of checked menu items + TextImage *m_pBlankCheck; // a blank image same size as the check for when items are not checked. + + TextImage *m_pCurrentKeyBinding; // An optional indicator for the key currently bound to this menu item + + KeyValues *m_pUserData; + +}; + +} // namespace vgui + +#endif // MENUITEM_H diff --git a/public/vgui_controls/MessageBox.h b/public/vgui_controls/MessageBox.h new file mode 100644 index 0000000..c464642 --- /dev/null +++ b/public/vgui_controls/MessageBox.h @@ -0,0 +1,98 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MESSAGEBOX_H +#define MESSAGEBOX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// prevent windows macros from messing with the class +#ifdef MessageBox +#undef MessageBox +#endif + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Popup discardable message box +//----------------------------------------------------------------------------- +class MessageBox : public Frame +{ + DECLARE_CLASS_SIMPLE( MessageBox, Frame ); + +public: + // title - Text to be displayed in the title bar of the window + // text - Text message in the message box + // startMinimized - wether message box starts minimized. Starts invisible by default + // parent - parent panel of the message box, by default it has no parent. This will keep the box visible until the OK button is pressed. + MessageBox(const char *title, const char *text, Panel *parent = NULL); + MessageBox(const wchar_t *wszTitle, const wchar_t *wszText, Panel *parent = NULL); + ~MessageBox(); + + // Put the message box into a modal state + virtual void DoModal(Frame *pFrameOver = NULL); + + // make the message box appear and in a modeless state + virtual void ShowWindow(Frame *pFrameOver = NULL); + + // Set a string command to be sent when the OK button is pressed + // Use AddActionSignalTarget() to mark yourself as a recipient of the command + virtual void SetCommand(const char *command); + virtual void SetCommand(KeyValues *command); + + // Set the visibility of the OK button. + virtual void SetOKButtonVisible(bool state); + + // Set the text on the OK button + virtual void SetOKButtonText(const char *buttonText); + virtual void SetOKButtonText(const wchar_t *wszButtonText); + + // Cancel button (off by default) + void SetCancelButtonVisible(bool state); + void SetCancelButtonText(const char *buttonText); + void SetCancelButtonText(const wchar_t *wszButtonText); + void SetCancelCommand( KeyValues *command ); + + // Toggles visibility of the close box. + virtual void DisableCloseButton(bool state); + + virtual void OnCommand( const char *pCommand ); + + // Shows the message box over the cursor + void ShowMessageBoxOverCursor( bool bEnable ); + +protected: + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + +protected: + Button *m_pOkButton; + Button *m_pCancelButton; + Label *m_pMessageLabel; + +private: + MESSAGE_FUNC( OnShutdownRequest, "ShutdownRequest" ); + + void Init(); + + KeyValues *m_OkCommand; + KeyValues *m_CancelCommand; + vgui::Frame *m_pFrameOver; + bool m_bNoAutoClose : 1; + bool m_bShowMessageBoxOverCursor : 1; +}; + +} // namespace vgui + + +#endif // MESSAGEBOX_H diff --git a/public/vgui_controls/MessageDialog.h b/public/vgui_controls/MessageDialog.h new file mode 100644 index 0000000..ec800b9 --- /dev/null +++ b/public/vgui_controls/MessageDialog.h @@ -0,0 +1,154 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Contains the CMessageDialog declaration +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MESSAGEDIALOG_H +#define MESSAGEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +// styles +#define MD_WARNING 0x0001 +#define MD_ERROR 0x0002 + +// button configurations +#define MD_OK 0x0004 // 1 button - OK +#define MD_CANCEL 0x0008 // 1 button - CANCEL +#define MD_OKCANCEL 0x0010 // 2 buttons - OK and CANCEL +#define MD_YESNO 0x0020 // 2 buttons - YES and NO + +// behavior +#define MD_SIMPLEFRAME 0x0100 // legacy corners +#define MD_COMMANDAFTERCLOSE 0x0200 // send command at dialog termination (i.e. after fade) +#define MD_RESTRICTPAINT 0x0400 // only paint this dialog (hide any other ui elements) +#define MD_COMMANDONFORCECLOSE 0x0800 // send command when the dialog is closed assuming A input + +// dialog type +enum EDialogType +{ + MD_SAVE_BEFORE_QUIT, + MD_QUIT_CONFIRMATION, + MD_QUIT_CONFIRMATION_TF, + MD_KICK_CONFIRMATION, + MD_CLIENT_KICKED, + MD_LOST_HOST, + MD_LOST_SERVER, + MD_SEARCHING_FOR_GAMES, + MD_CREATING_GAME, + MD_MODIFYING_SESSION, + MD_SESSION_SEARCH_FAILED, + MD_SESSION_CREATE_FAILED, + MD_SESSION_CONNECTING, + MD_SESSION_CONNECT_NOTAVAILABLE, + MD_SESSION_CONNECT_SESSIONFULL, + MD_SESSION_CONNECT_FAILED, + MD_EXIT_SESSION_CONFIRMATION, + MD_STORAGE_DEVICES_NEEDED, + MD_STORAGE_DEVICES_CHANGED, + MD_STORAGE_DEVICES_TOO_FULL, + MD_NOT_ONLINE_ENABLED, + MD_NOT_ONLINE_SIGNEDIN, + MD_DEFAULT_CONTROLS_CONFIRM, + MD_AUTOSAVE_EXPLANATION, + MD_COMMENTARY_EXPLANATION, + MD_COMMENTARY_EXPLANATION_MULTI, + MD_COMMENTARY_CHAPTER_UNLOCK_EXPLANATION, + MD_SAVE_BEFORE_LANGUAGE_CHANGE, + MD_SAVE_BEFORE_NEW_GAME, + MD_SAVE_BEFORE_LOAD, + MD_DELETE_SAVE_CONFIRM, + MD_SAVE_OVERWRITE, + MD_SAVING_WARNING, + MD_SAVE_COMPLETE, + MD_STANDARD_SAMPLE, + MD_WARNING_SAMPLE, + MD_ERROR_SAMPLE, + MD_PROMPT_SIGNIN, + MD_PROMPT_SIGNIN_REQUIRED, + MD_PROMPT_STORAGE_DEVICE, + MD_PROMPT_STORAGE_DEVICE_REQUIRED, + MD_DISCONNECT_CONFIRMATION, + MD_DISCONNECT_CONFIRMATION_HOST, + MD_LOAD_FAILED_WARNING, + MD_OPTION_CHANGE_FROM_X360_DASHBOARD, + MD_STORAGE_DEVICES_CORRUPT, + MD_CHECKING_STORAGE_DEVICE +}; + +#include "vgui_controls/Frame.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/AnimatingImagePanel.h" +#include "vgui_controls/ImagePanel.h" + +//----------------------------------------------------------------------------- +// Purpose: Simple modal dialog box for Xbox 360 warnings and messages +//----------------------------------------------------------------------------- +class CMessageDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CMessageDialog, vgui::Frame ); + +public: + CMessageDialog( vgui::Panel *parent, const uint nType, const char *pTitle, const char *pMsg, const char *pCmdA, const char *pCmdB, vgui::Panel *pParent, bool bShowActivity ); + ~CMessageDialog(); + + enum + { + BTN_INVALID = -1, + BTN_B, + BTN_A, + MAX_BUTTONS, + }; + + struct ButtonLabel_s + { + vgui::Label *pIcon; + vgui::Label *pText; + int nWide; + bool bCreated; + }; + + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void PaintBackground(); + uint GetType( void ); + void SetControlSettingsKeys( KeyValues *pKeys ); + +private: + void CreateButtonLabel( ButtonLabel_s *pButton, const char *pIcon, const char *pText ); + void DoCommand( int button ); + + vgui::Panel *m_pCreator; + + vgui::Label *m_pTitle; + vgui::Label *m_pMsg; + vgui::ImagePanel *m_pBackground; + + vgui::AnimatingImagePanel *m_pAnimatingPanel; + + vgui::HFont m_hButtonFont; + vgui::HFont m_hTextFont; + uint m_nType; + Color m_ButtonTextColor; + int m_ButtonPressed; + KeyValues *m_pControlSettings; + + int m_FooterTall; + int m_ButtonMargin; + Color m_clrNotSimpleBG; + Color m_clrNotSimpleBGBlack; + int m_ButtonIconLabelSpace; + + int m_ActivityIndent; + + bool m_bShowActivity; // should we show an animating image panel? + + ButtonLabel_s m_Buttons[MAX_BUTTONS]; + char *m_pCommands[MAX_BUTTONS]; +}; + +#endif // MESSAGEDIALOG_H diff --git a/public/vgui_controls/MessageMap.h b/public/vgui_controls/MessageMap.h new file mode 100644 index 0000000..709d424 --- /dev/null +++ b/public/vgui_controls/MessageMap.h @@ -0,0 +1,393 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef MESSAGEMAP_H +#define MESSAGEMAP_H + +#ifdef _WIN32 +#pragma once +#endif + +//#include "tier1/utlvector.h" +#include "dmxloader/dmxelement.h" + +// more flexible than default pointers to members code required for casting member function pointers +#pragma pointers_to_members( full_generality, virtual_inheritance ) + +namespace vgui +{ + +////////////// MESSAGEMAP DEFINITIONS ////////////// + + +#ifndef ARRAYSIZE +#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) +#endif + + +//----------------------------------------------------------------------------- +// Purpose: parameter data type enumeration +// used internal but the shortcut macros require this to be exposed +//----------------------------------------------------------------------------- +enum DataType_t +{ + DATATYPE_VOID, + DATATYPE_CONSTCHARPTR, + DATATYPE_INT, + DATATYPE_FLOAT, + DATATYPE_PTR, + DATATYPE_BOOL, + DATATYPE_KEYVALUES, + DATATYPE_CONSTWCHARPTR, + DATATYPE_UINT64, + DATATYPE_HANDLE, // It's an int, really +}; + +class Panel; +typedef unsigned int VPANEL; + +typedef void (Panel::*MessageFunc_t)(void); + +//----------------------------------------------------------------------------- +// Purpose: Single item in a message map +// Contains the information to map a string message name with parameters +// to a function call +//----------------------------------------------------------------------------- +#pragma warning(disable:4121) +struct MessageMapItem_t +{ + const char *name; + // VC6 aligns this to 16-bytes. Since some of the code has been compiled with VC6, + // we need to enforce the alignment on later compilers to remain compatible. + ALIGN16 MessageFunc_t func; + + int numParams; + + DataType_t firstParamType; + const char *firstParamName; + + DataType_t secondParamType; + const char *secondParamName; + + int nameSymbol; + int firstParamSymbol; + int secondParamSymbol; +}; + +#define DECLARE_PANELMESSAGEMAP( className ) \ + static void AddToMap( char const *scriptname, vgui::MessageFunc_t function, int paramCount, int p1type, const char *p1name, int p2type, const char *p2name ) \ + { \ + vgui::PanelMessageMap *map = vgui::FindOrAddPanelMessageMap( GetPanelClassName() ); \ + \ + vgui::MessageMapItem_t entry; \ + entry.name = scriptname; \ + entry.func = function; \ + entry.numParams = paramCount; \ + entry.firstParamType = (vgui::DataType_t)p1type; \ + entry.firstParamName = p1name; \ + entry.secondParamType = (vgui::DataType_t)p2type; \ + entry.secondParamName = p2name; \ + entry.nameSymbol = 0; \ + entry.firstParamSymbol = 0; \ + entry.secondParamSymbol = 0; \ + \ + map->entries.AddToTail( entry ); \ + } \ + \ + static void ChainToMap( void ) \ + { \ + static bool chained = false; \ + if ( chained ) \ + return; \ + chained = true; \ + vgui::PanelMessageMap *map = vgui::FindOrAddPanelMessageMap( GetPanelClassName() ); \ + map->pfnClassName = &GetPanelClassName; \ + if ( map && GetPanelBaseClassName() && GetPanelBaseClassName()[0] ) \ + { \ + map->baseMap = vgui::FindOrAddPanelMessageMap( GetPanelBaseClassName() ); \ + } \ + } \ + \ + class className##_RegisterMap; \ + friend class className##_RegisterMap; \ + class className##_RegisterMap \ + { \ + public: \ + className##_RegisterMap() \ + { \ + className::ChainToMap(); \ + } \ + }; \ + className##_RegisterMap m_RegisterClass; \ + \ + virtual vgui::PanelMessageMap *GetMessageMap() \ + { \ + static vgui::PanelMessageMap *s_pMap = vgui::FindOrAddPanelMessageMap( GetPanelClassName() ); \ + return s_pMap; \ + } + +#if !defined( _XBOX ) +#define VGUI_USEKEYBINDINGMAPS 1 +#endif + +#if defined( VGUI_USEKEYBINDINGMAPS ) + +#define DECLARE_CLASS_SIMPLE( className, baseClassName ) \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; \ +public: \ + DECLARE_PANELMESSAGEMAP( className ); \ + DECLARE_PANELANIMATION( className ); \ + DECLARE_KEYBINDINGMAP( className ); \ + static char const *GetPanelClassName() { return #className; } \ + static char const *GetPanelBaseClassName() { return #baseClassName; } + +#define DECLARE_CLASS_SIMPLE_NOBASE( className ) \ + typedef className ThisClass; \ +public: \ + DECLARE_PANELMESSAGEMAP( className ); \ + DECLARE_PANELANIMATION( className ); \ + DECLARE_KEYBINDINGMAP( className ); \ + static char const *GetPanelClassName() { return #className; } \ + static char const *GetPanelBaseClassName() { return NULL; } + +#else // no keybinding maps + +#define DECLARE_CLASS_SIMPLE( className, baseClassName ) \ + typedef baseClassName BaseClass; \ + typedef className ThisClass; \ +public: \ + DECLARE_PANELMESSAGEMAP( className ); \ + DECLARE_PANELANIMATION( className ); \ + static char const *GetPanelClassName() { return #className; } \ + static char const *GetPanelBaseClassName() { return #baseClassName; } + +#define DECLARE_CLASS_SIMPLE_NOBASE( className ) \ + typedef className ThisClass; \ +public: \ + DECLARE_PANELMESSAGEMAP( className ); \ + DECLARE_PANELANIMATION( className ); \ + static char const *GetPanelClassName() { return #className; } \ + static char const *GetPanelBaseClassName() { return NULL; } + +#endif // !VGUI_USEKEYBINDINGMAPS + +#define _MessageFuncCommon( name, scriptname, paramCount, p1type, p1name, p2type, p2name ) \ + class PanelMessageFunc_##name; \ + friend class PanelMessageFunc_##name; \ + class PanelMessageFunc_##name \ + { \ + public: \ + static void InitVar() \ + { \ + static bool bAdded = false; \ + if ( !bAdded ) \ + { \ + bAdded = true; \ + AddToMap( scriptname, (vgui::MessageFunc_t)&ThisClass::name, paramCount, p1type, p1name, p2type, p2name ); \ + } \ + } \ + PanelMessageFunc_##name() \ + { \ + PanelMessageFunc_##name::InitVar(); \ + } \ + }; \ + PanelMessageFunc_##name m_##name##_register; \ + +// Use this macro to define a message mapped function +// must end with a semicolon ';', or with a function +// no parameter +#define MESSAGE_FUNC( name, scriptname ) _MessageFuncCommon( name, scriptname, 0, 0, 0, 0, 0 ); virtual void name( void ) + +// one parameter +#define MESSAGE_FUNC_INT( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_INT, #p1, 0, 0 ); virtual void name( int p1 ) +#define MESSAGE_FUNC_UINT64( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_UINT64, #p1, 0, 0 ); virtual void name( uint64 p1 ) +#define MESSAGE_FUNC_PTR( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_PTR, #p1, 0, 0 ); virtual void name( vgui::Panel *p1 ) +#define MESSAGE_FUNC_HANDLE( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_HANDLE, #p1, 0, 0 ); virtual void name( vgui::VPANEL p1 ) +#define MESSAGE_FUNC_ENUM( name, scriptname, t1, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_INT, #p1, 0, 0 ); virtual void name( t1 p1 ) +#define MESSAGE_FUNC_FLOAT( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_FLOAT, #p1, 0, 0 ); virtual void name( float p1 ) +#define MESSAGE_FUNC_CHARPTR( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_CONSTCHARPTR, #p1, 0, 0 ); virtual void name( const char *p1 ) +#define MESSAGE_FUNC_WCHARPTR( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_CONSTWCHARPTR, #p1, 0, 0 ); virtual void name( const wchar_t *p1 ) + +// two parameters +#define MESSAGE_FUNC_INT_INT( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_INT, #p1, vgui::DATATYPE_INT, #p2 ); virtual void name( int p1, int p2 ) +#define MESSAGE_FUNC_PTR_INT( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_PTR, #p1, vgui::DATATYPE_INT, #p2 ); virtual void name( vgui::Panel *p1, int p2 ) +#define MESSAGE_FUNC_HANDLE_INT( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_HANDLE, #p1, vgui::DATATYPE_INT, #p2 ); virtual void name( vgui::VPANEL p1, int p2 ) +#define MESSAGE_FUNC_ENUM_ENUM( name, scriptname, t1, p1, t2, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_INT, #p1, vgui::DATATYPE_INT, #p2 ); virtual void name( t1 p1, t2 p2 ) +#define MESSAGE_FUNC_INT_CHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_INT, #p1, vgui::DATATYPE_CONSTCHARPTR, #p2 ); virtual void name( int p1, const char *p2 ) +#define MESSAGE_FUNC_PTR_CHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_PTR, #p1, vgui::DATATYPE_CONSTCHARPTR, #p2 ); virtual void name( vgui::Panel *p1, const char *p2 ) +#define MESSAGE_FUNC_HANDLE_CHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_HANDLE, #p1, vgui::DATATYPE_CONSTCHARPTR, #p2 ); virtual void name( vgui::VPANEL p1, const char *p2 ) +#define MESSAGE_FUNC_PTR_WCHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_PTR, #p1, vgui::DATATYPE_CONSTWCHARPTR, #p2 ); virtual void name( vgui::Panel *p1, const wchar_t *p2 ) +#define MESSAGE_FUNC_HANDLE_WCHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_HANDLE, #p1, vgui::DATATYPE_CONSTWCHARPTR, #p2 ); virtual void name( vgui::VPANEL p1, const wchar_t *p2 ) +#define MESSAGE_FUNC_CHARPTR_CHARPTR( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_CONSTCHARPTR, #p1, vgui::DATATYPE_CONSTCHARPTR, #p2 ); virtual void name( const char *p1, const char *p2 ) + +// unlimited parameters (passed in the whole KeyValues) +#define MESSAGE_FUNC_PARAMS( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_KEYVALUES, NULL, 0, 0 ); virtual void name( KeyValues *p1 ) + +// no-virtual function version +#define MESSAGE_FUNC_NV( name, scriptname ) _MessageFuncCommon( name, scriptname, 0, 0, 0, 0, 0 ); void name( void ) +#define MESSAGE_FUNC_NV_INT( name, scriptname, p1 ) _MessageFuncCommon( name, scriptname, 1, vgui::DATATYPE_INT, #p1, 0, 0 ); void name( int p1 ) +#define MESSAGE_FUNC_NV_INT_INT( name, scriptname, p1, p2 ) _MessageFuncCommon( name, scriptname, 2, vgui::DATATYPE_INT, #p1, vgui::DATATYPE_INT, #p2 ); void name( int p1, int p2 ) + + +// mapping, one per class +struct PanelMessageMap +{ + PanelMessageMap() + { + baseMap = NULL; + pfnClassName = NULL; + processed = false; + } + + CUtlVector< MessageMapItem_t > entries; + bool processed; + PanelMessageMap *baseMap; + char const *(*pfnClassName)( void ); +}; + +PanelMessageMap *FindPanelMessageMap( char const *className ); +PanelMessageMap *FindOrAddPanelMessageMap( char const *className ); + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// OBSELETE MAPPING FUNCTIONS, USE ABOVE +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// no parameters +#define MAP_MESSAGE( type, name, func ) { name, (vgui::MessageFunc_t)(&type::func), 0 } + +// implicit single parameter (params is the data store) +#define MAP_MESSAGE_PARAMS( type, name, func ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_KEYVALUES, NULL } + +// single parameter +#define MAP_MESSAGE_PTR( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_PTR, param1 } +#define MAP_MESSAGE_INT( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_INT, param1 } +#define MAP_MESSAGE_BOOL( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_BOOL, param1 } +#define MAP_MESSAGE_FLOAT( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_FLOAT, param1 } +#define MAP_MESSAGE_PTR( type, name, func, param1 ) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_PTR, param1 } +#define MAP_MESSAGE_CONSTCHARPTR( type, name, func, param1) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_CONSTCHARPTR, param1 } +#define MAP_MESSAGE_CONSTWCHARPTR( type, name, func, param1) { name, (vgui::MessageFunc_t)(&type::func), 1, vgui::DATATYPE_CONSTWCHARPTR, param1 } + +// two parameters +#define MAP_MESSAGE_INT_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_INT, param2 } +#define MAP_MESSAGE_PTR_INT( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_INT, param2 } +#define MAP_MESSAGE_INT_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_INT, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } +#define MAP_MESSAGE_PTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } +#define MAP_MESSAGE_PTR_CONSTWCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_PTR, param1, vgui::DATATYPE_CONSTWCHARPTR, param2 } +#define MAP_MESSAGE_CONSTCHARPTR_CONSTCHARPTR( type, name, func, param1, param2 ) { name, (vgui::MessageFunc_t)&type::func, 2, vgui::DATATYPE_CONSTCHARPTR, param1, vgui::DATATYPE_CONSTCHARPTR, param2 } + +// if more parameters are needed, just use MAP_MESSAGE_PARAMS() and pass the keyvalue set into the function + + +//----------------------------------------------------------------------------- +// Purpose: stores the list of objects in the hierarchy +// used to iterate through an object's message maps +//----------------------------------------------------------------------------- +struct PanelMap_t +{ + MessageMapItem_t *dataDesc; + int dataNumFields; + const char *dataClassName; + PanelMap_t *baseMap; + int processed; +}; + +// for use in class declarations +// declares the static variables and functions needed for the data description iteration +#define DECLARE_PANELMAP() \ + static vgui::PanelMap_t m_PanelMap; \ + static vgui::MessageMapItem_t m_MessageMap[]; \ + virtual vgui::PanelMap_t *GetPanelMap( void ); + +// could embed typeid() into here as well? +#define IMPLEMENT_PANELMAP( derivedClass, baseClass ) \ + vgui::PanelMap_t derivedClass::m_PanelMap = { derivedClass::m_MessageMap, ARRAYSIZE(derivedClass::m_MessageMap), #derivedClass, &baseClass::m_PanelMap }; \ + vgui::PanelMap_t *derivedClass::GetPanelMap( void ) { return &m_PanelMap; } + +typedef vgui::Panel *( *PANELCREATEFUNC )( void ); + +//----------------------------------------------------------------------------- +// Purpose: Used by DECLARE_BUILD_FACTORY macro to create a linked list of +// instancing functions +//----------------------------------------------------------------------------- +class CBuildFactoryHelper +{ +public: + // Static list of helpers + static CBuildFactoryHelper *m_sHelpers; + +public: + // Construction + CBuildFactoryHelper( char const *className, PANELCREATEFUNC func ); + + // Accessors + CBuildFactoryHelper *GetNext( void ); + + char const *GetClassName() const; + + vgui::Panel *CreatePanel(); + + static vgui::Panel *InstancePanel( char const *className ); + static void GetFactoryNames( CUtlVector< char const * >& list ); + + static CDmxElement *CreatePanelDmxElement( vgui::Panel *pPanel ); + static Panel* UnserializeDmxElementPanel( CDmxElement *pElement ); + + // DMX serializer fxns + static bool Serialize( CUtlBuffer &buf, vgui::Panel *pPanel ); + static bool Unserialize( Panel **ppPanel, CUtlBuffer &buf, const char *pFileName = NULL ); + + +private: + + static bool HasFactory( char const *className ); + + // Next factory in list + CBuildFactoryHelper *m_pNext; + + int m_Type; + PANELCREATEFUNC m_CreateFunc; + char const *m_pClassName; +}; + +// This is the macro which implements creation of each type of panel +// It creates a function which instances an object of the specified type +// It them hooks that function up to the helper list so that the CHud objects can create +// the elements by name, with no header file dependency, etc. +#define DECLARE_BUILD_FACTORY( className ) \ + static vgui::Panel *Create_##className( void ) \ + { \ + return new className( NULL, NULL ); \ + }; \ + static vgui::CBuildFactoryHelper g_##className##_Helper( #className, Create_##className );\ + className *g_##className##LinkerHack = NULL; + +#define DECLARE_BUILD_FACTORY_DEFAULT_TEXT( className, defaultText ) \ + static vgui::Panel *Create_##className( void ) \ + { \ + return new className( NULL, NULL, #defaultText ); \ + }; \ + static vgui::CBuildFactoryHelper g_##className##_Helper( #className, Create_##className );\ + className *g_##className##LinkerHack = NULL; + +// This one allows passing in a special function with calls new panel( xxx ) with arbitrary default parameters +#define DECLARE_BUILD_FACTORY_CUSTOM( className, createFunc ) \ + static vgui::CBuildFactoryHelper g_##className##_Helper( #className, createFunc );\ + className *g_##className##LinkerHack = NULL; + +#define DECLARE_BUILD_FACTORY_CUSTOM_ALIAS( className, factoryName, createFunc ) \ + static vgui::CBuildFactoryHelper g_##factoryName##_Helper( #factoryName, createFunc );\ + className *g_##factoryName##LinkerHack = NULL; + +} // namespace vgui + + +#endif // MESSAGEMAP_H diff --git a/public/vgui_controls/PHandle.h b/public/vgui_controls/PHandle.h new file mode 100644 index 0000000..a51a0bb --- /dev/null +++ b/public/vgui_controls/PHandle.h @@ -0,0 +1,91 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PHANDLE_H +#define PHANDLE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +class Panel; + +//----------------------------------------------------------------------------- +// Purpose: Safe pointer class for handling Panel or derived panel classes +//----------------------------------------------------------------------------- +class PHandle +{ +public: + PHandle() : m_iPanelID(INVALID_PANEL) {} //m_iSerialNumber(0), m_pListEntry(0) {} + + Panel *Get(); + Panel *Set( Panel *pPanel ); + Panel *Set( HPanel hPanel ); + + operator Panel *() { return Get(); } + Panel * operator ->() { return Get(); } + Panel * operator = (Panel *pPanel) { return Set(pPanel); } + + bool operator == (Panel *pPanel) { return (Get() == pPanel); } + operator bool () { return Get() != 0; } + + friend inline bool operator == ( const PHandle &p1, const PHandle &p2 ) + { + return p1.m_iPanelID == p2.m_iPanelID; + } + +private: + HPanel m_iPanelID; +}; + +//----------------------------------------------------------------------------- +// Purpose: Safe pointer class to just convert between VPANEL's and PHandle +//----------------------------------------------------------------------------- +class VPanelHandle +{ +public: + VPanelHandle() : m_iPanelID(INVALID_PANEL) {} + + VPANEL Get(); + VPANEL Set( VPANEL pPanel ); + + operator VPANEL () { return Get(); } + VPANEL operator = (VPANEL pPanel) { return Set(pPanel); } + + bool operator == (VPANEL pPanel) { return (Get() == pPanel); } + operator bool () { return Get() != 0; } + +private: + HPanel m_iPanelID; +}; + +//----------------------------------------------------------------------------- +// Purpose: DHANDLE is a templated version of PHandle +//----------------------------------------------------------------------------- +template< class PanelType > +class DHANDLE : public PHandle +{ +public: + PanelType *Get() { return (PanelType *)PHandle::Get(); } + PanelType *Set( PanelType *pPanel ) { return (PanelType *)PHandle::Set(pPanel); } + PanelType *Set( HPanel hPanel ) { return (PanelType *)PHandle::Set(hPanel); } + + operator PanelType *() { return (PanelType *)PHandle::Get(); } + PanelType * operator ->() { return (PanelType *)PHandle::Get(); } + PanelType * operator = (PanelType *pPanel) { return (PanelType *)PHandle::Set(pPanel); } + bool operator == (Panel *pPanel) { return (PHandle::Get() == pPanel); } + operator bool () { return PHandle::Get() != NULL; } +}; + +}; + +#endif // PHANDLE_H diff --git a/public/vgui_controls/Panel.h b/public/vgui_controls/Panel.h new file mode 100644 index 0000000..e0c373d --- /dev/null +++ b/public/vgui_controls/Panel.h @@ -0,0 +1,1080 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PANEL_H +#define PANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlflags.h" +#include "vgui/VGUI.h" +#include "vgui/Dar.h" +#include "vgui_controls/MessageMap.h" +#if defined( VGUI_USEKEYBINDINGMAPS ) +#include "vgui_controls/KeyBindingMap.h" +#endif +#include "vgui/IClientPanel.h" +#include "vgui/IScheme.h" +#include "vgui_controls/Controls.h" +#include "vgui_controls/PHandle.h" +#include "vgui_controls/PanelAnimationVar.h" +#include "Color.h" +#include "vstdlib/IKeyValuesSystem.h" +#include "tier1/utlsymbol.h" +#include "vgui_controls/BuildGroup.h" +#include "dmxloader/dmxelement.h" + +// undefine windows function macros that overlap +#ifdef PostMessage +#undef PostMessage +#endif + +#ifdef SetCursor +#undef SetCursor +#endif + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; +struct DmxElementUnpackStructure_t; + +namespace vgui +{ + +#if !defined( _X360 ) +#define VGUI_USEDRAGDROP 1 +#endif + +#if defined( VGUI_USEKEYBINDINGMAPS ) +struct PanelKeyBindingMap; +#endif +//----------------------------------------------------------------------------- +// Purpose: Helper functions to construct vgui panels +// +// SETUP_PANEL - will make a panel ready for use right now (i.e setup its colors, borders, fonts, etc) +// +template< class T > +inline T *SETUP_PANEL(T *panel) +{ + panel->MakeReadyForUse(); + return panel; +} + +// +// CREATE_PANEL - creates a panel that is ready to use right now +// +// example of use = to set the FG Color of a panel inside of a constructor (i.e before ApplySchemeSettings() has been run on the child) +// +#define CREATE_PANEL(type, parent, name) (SETUP_PANEL(new type(parent, name))) + +//----------------------------------------------------------------------------- +// Purpose: Drag/drop support context info (could defined within Panel...) +//----------------------------------------------------------------------------- +#if defined( VGUI_USEDRAGDROP ) +struct DragDrop_t; +class Menu; +#endif + + + +class Panel; + +struct SizerAddArgs_t +{ + SizerAddArgs_t() + { + m_flExpandFactor = 0.0f; + m_nPadding = 5; + m_bMinorExpand = true; + m_nMinX = -1; + m_nMinY = -1; + m_bIgnoreMemberMin = false; + } + + SizerAddArgs_t& Expand( float flExpandFactor ) { m_flExpandFactor = flExpandFactor; return *this; } + SizerAddArgs_t& Padding( int nPadding ) { m_nPadding = nPadding; return *this; } + SizerAddArgs_t& MinorExpand( bool bMinorExpand ) { m_bMinorExpand = bMinorExpand; return *this; } + SizerAddArgs_t& MinSize( int nMinX, int nMinY ) { m_nMinX = nMinX; m_nMinY = nMinY; return *this; } + SizerAddArgs_t& MinX( int nMinX ) { m_nMinX = nMinX; return *this; } + SizerAddArgs_t& MinY( int nMinY ) { m_nMinY = nMinY; return *this; } + + // IgnoreMemberMin --> MinX and MinY (when set) are the only criteria for minimum size; member-requested min size is ignored + SizerAddArgs_t& IgnoreMemberMin( bool bIgnoreMemberMin = true ) { m_bIgnoreMemberMin = bIgnoreMemberMin; return *this; } + + SizerAddArgs_t& FixedSize( int nX, int nY ) + { + IgnoreMemberMin( true ); + MinSize( nX, nY ); + Expand( 0.f ); + MinorExpand( false ); + return *this; + } + + float m_flExpandFactor; + int m_nPadding; + bool m_bMinorExpand; + int m_nMinX; + int m_nMinY; + bool m_bIgnoreMemberMin; +}; + + +enum SizerLayoutDirection_t +{ + ESLD_HORIZONTAL, // major axis = X + ESLD_VERTICAL // major axis = Y +}; + +enum SizerElementType_t +{ + ESET_SIZER, + ESET_PANEL, + ESET_SPACER, +}; + +class CSizerBase +{ +public: + CSizerBase( ); + virtual ~CSizerBase( ); + + int GetElementCount() { return m_Members.Count(); } + SizerElementType_t GetElementType( int i ); + Panel *GetPanel( int i ); + + void SetElementArgs( int nIndex, const SizerAddArgs_t& args ) { m_Members[nIndex].Fill( args ); } + + // The containing panel's layout should be invalidated if members are added to this sizer. + + // Inserts a panel/sizer/spacer at the specified index and shifts remaining elements down + void InsertPanel( int nIndex, Panel *pPanel, const SizerAddArgs_t& args ); + void InsertSizer( int nIndex, CSizerBase *pSizer, const SizerAddArgs_t& args ); + void InsertSpacer( int nIndex, const SizerAddArgs_t& args ); + + void AddPanel( Panel *pPanel, const SizerAddArgs_t& args ) { InsertPanel( GetElementCount(), pPanel, args ); } + void AddSizer( CSizerBase *pSizer, const SizerAddArgs_t& args ) { InsertSizer( GetElementCount(), pSizer, args ); } + void AddSpacer( const SizerAddArgs_t& args ) { InsertSpacer( GetElementCount(), args ); } + + void RemoveElement( int i, bool bDelete ); + void RemoveAllMembers( bool bDelete ); + + void GetMinSize( int &OutX, int &OutY ); + + // Called by Panel on PerformLayout() so that sizer client size computations are up-to-date + void RecursiveInvalidateCachedSize(); + + virtual void DoLayout( int BaseX, int BaseY, int SizeX, int SizeY ) = 0; + virtual void CalculateSize() = 0; + +protected: + class CSizerMember + { + friend class CSizerBase; // allow CSizerBase to populate the private members directly + + public: + SizerElementType_t GetElementType() const; + Panel *GetPanel() const; + + void GetMemberMinSize( int &OutX, int &OutY ); + void RecursiveInvalidateCachedSize(); + void Place( int BaseX, int BaseY, int SizeX, int SizeY ); + + float GetExpandFactor() { return m_flExpandFactor; } + bool GetMinorExpand() { return m_bMinorExpand; } + + void DiscardOwnedSizer(); + + bool IsVisible(); + + void Fill( const SizerAddArgs_t& args ); + + private: + void RecursiveRemove( bool bDelete ); + + Panel *m_pPanel; + CSizerBase *m_pSizer; + + int m_nPadding; // if m_pPanel and m_pSizer are both NULL, this is the spacer min size + float m_flExpandFactor; + bool m_bMinorExpand; + bool m_bIgnoreMemberMin; + int m_nMinX; + int m_nMinY; + }; + + CUtlVector m_Members; + int m_nMinXSize; + int m_nMinYSize; +}; + +inline int SizerMajorAxis( SizerLayoutDirection_t Dir, int X, int Y ) { return (Dir == ESLD_HORIZONTAL) ? X : Y; } +inline int SizerMinorAxis( SizerLayoutDirection_t Dir, int X, int Y ) { return (Dir == ESLD_VERTICAL) ? X : Y; } +inline int SizerXAxis( SizerLayoutDirection_t Dir, int MajorAxis, int MinorAxis ) { return (Dir == ESLD_HORIZONTAL) ? MajorAxis : MinorAxis; } +inline int SizerYAxis( SizerLayoutDirection_t Dir, int MajorAxis, int MinorAxis ) { return (Dir == ESLD_VERTICAL) ? MajorAxis : MinorAxis; } + +class CBoxSizer: public CSizerBase +{ +public: + CBoxSizer( SizerLayoutDirection_t LayoutDirection ); + + virtual void CalculateSize(); + virtual void DoLayout( int BaseX, int BaseY, int SizeX, int SizeY ); + +protected: + SizerLayoutDirection_t m_LayoutDirection; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Macro to handle Colors that can be overridden in .res files +//----------------------------------------------------------------------------- +struct OverridableColorEntry +{ + char const *name() { return m_pszScriptName; } + + char const *m_pszScriptName; + Color *m_pColor; + Color m_colFromScript; + UtlSymId_t m_sColorNameFromScript; + bool m_bOverridden; +}; + +#define REGISTER_COLOR_AS_OVERRIDABLE( name, scriptname ) \ + AddToOverridableColors( &name, scriptname ); + + +//----------------------------------------------------------------------------- +// Macros for unpacking vgui panels +//----------------------------------------------------------------------------- +#define DECLARE_VGUI_UNPACK() \ + DECLARE_DMXELEMENT_UNPACK() \ + private: \ + static DmxElementUnpackStructure_t *s_pUnpackParams; \ + public: \ + virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const { return s_pUnpackParams; } + +#define DECLARE_VGUI_UNPACK_NAMESPACE( _namespace ) \ + template friend DmxElementUnpackStructure_t *DmxElementUnpackInit##_namespace(T *); \ + private: \ + static DmxElementUnpackStructure_t *s_pUnpackParams; \ + public: \ + virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const { return s_pUnpackParams; } + +#define BEGIN_VGUI_UNPACK( _structName ) BEGIN_DMXELEMENT_UNPACK( _structName ) +#define END_VGUI_UNPACK( _structName ) \ + END_DMXELEMENT_UNPACK( _structName, s_pUnpackParams ) \ + DmxElementUnpackStructure_t *_structName::s_pUnpackParams = _structName##_UnpackInit::s_pUnpack; + +#define BEGIN_VGUI_UNPACK_NAMESPACE( _nameSpace, _structName ) BEGIN_DMXELEMENT_UNPACK_NAMESPACE( _nameSpace, _structName ) +#define END_VGUI_UNPACK_NAMESPACE( _nameSpace, _structName ) \ + END_DMXELEMENT_UNPACK_NAMESPACE( _nameSpace, _structName, s_pUnpackParams ) \ + DmxElementUnpackStructure_t *_structName::s_pUnpackParams = _namespace##_structName##_UnpackInit::s_pUnpack; + + +//----------------------------------------------------------------------------- +// Purpose: For hudanimations.txt scripting of vars +//----------------------------------------------------------------------------- +class IPanelAnimationPropertyConverter +{ +public: + virtual void GetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) = 0; + virtual void SetData( Panel *panel, KeyValues *kv, PanelAnimationMapEntry *entry ) = 0; + virtual void InitFromDefault( Panel *panel, PanelAnimationMapEntry *entry ) = 0; +}; + +#if defined( VGUI_USEKEYBINDINGMAPS ) +enum KeyBindingContextHandle_t +{ + INVALID_KEYBINDINGCONTEXT_HANDLE = 0xffffffff, +}; +#endif +//----------------------------------------------------------------------------- +// Purpose: Base interface to all vgui windows +// All vgui controls that receive message and/or have a physical presence +// on screen are be derived from Panel. +// This is designed as an easy-access to the vgui-functionality; for more +// low-level access to vgui functions use the IPanel/IClientPanel interfaces directly +//----------------------------------------------------------------------------- +class Panel : public IClientPanel +{ + DECLARE_CLASS_SIMPLE_NOBASE( Panel ); + DECLARE_DMXELEMENT_UNPACK_NAMESPACE(vgui); + +public: + // For property mapping + static void InitPropertyConverters( void ); + static void AddPropertyConverter( char const *typeName, IPanelAnimationPropertyConverter *converter ); + + //----------------------------------------------------------------------------- + // CONSTRUCTORS + // these functions deal with the creation of the Panel + // the Panel automatically gets a handle to a vgui-internal panel, the ipanel(), upon construction + // vgui interfaces deal only with ipanel(), not Panel directly + Panel(); + Panel(Panel *parent); + Panel(Panel *parent, const char *panelName); + Panel(Panel *parent, const char *panelName, HScheme scheme); + + virtual ~Panel(); + + // returns pointer to Panel's vgui VPanel interface handle + virtual VPANEL GetVPanel() { return _vpanel; } + HPanel ToHandle() const; + + + //----------------------------------------------------------------------------- + // PANEL METHODS + // these functions all manipulate panels + // they cannot be derived from + void SetName(const char *panelName); // sets the name of the panel - used as an identifier + const char *GetName(); // returns the name of this panel... never NULL + const char *GetClassName(); // returns the class name of the panel (eg. Panel, Label, Button, etc.) + + void MakeReadyForUse(); // fully construct this panel so its ready for use right now (i.e fonts loaded, colors set, default label text set, ...) + + // panel position & size + // all units are in pixels + void SetPos(int x,int y); // sets position of panel, in local space (ie. relative to parent's position) + void GetPos(int &x,int &y); // gets local position of panel + void SetSize(int wide,int tall); // sets size of panel + void GetSize(int &wide, int &tall); // gets size of panel + void SetBounds(int x, int y, int wide, int tall); // combination of SetPos/SetSize + void GetBounds(int &x, int &y, int &wide, int &tall); // combination of GetPos/GetSize + int GetWide(); // returns width of panel + void SetWide(int wide); // sets width of panel + int GetTall(); // returns height of panel + void SetTall(int tall); // sets height of panel + void SetMinimumSize(int wide,int tall); // sets the minimum size the panel can go + void GetMinimumSize(int& wide,int& tall); // gets the minimum size + bool IsBuildModeEditable(); // editable in the buildModeDialog? + void SetBuildModeEditable(bool state); // set buildModeDialog editable + bool IsBuildModeDeletable(); // deletable in the buildModeDialog? + void SetBuildModeDeletable(bool state); // set buildModeDialog deletable + bool IsBuildModeActive(); // true if we're currently in edit mode + void SetZPos(int z); // sets Z ordering - lower numbers are always behind higher z's + int GetZPos( void ); + void SetAlpha(int alpha); // sets alpha modifier for panel and all child panels [0..255] + int GetAlpha(); // returns the current alpha + + // panel visibility + // invisible panels and their children do not drawn, updated, or receive input messages + virtual void SetVisible(bool state); + virtual bool IsVisible(); + virtual bool IsFullyVisible(); // checks parent panels are IsVisible too + + // painting + virtual VPANEL IsWithinTraverse(int x, int y, bool traversePopups); // recursive; returns a pointer to the panel at those coordinates + MESSAGE_FUNC( Repaint, "Repaint" ); // marks the panel as needing to be repainted + virtual void PostMessage(VPANEL target, KeyValues *message, float delaySeconds = 0.0f); + + bool IsWithin(int x, int y); //in screen space + void LocalToScreen(int &x, int &y); + void ScreenToLocal(int &x, int &y); + void ParentLocalToScreen(int &x, int &y); + void MakePopup(bool showTaskbarIcon = true,bool disabled = false); // turns the panel into a popup window (ie. can draw outside of it's parents space) + virtual void OnMove(); + + // panel hierarchy + virtual Panel *GetParent(); + virtual VPANEL GetVParent(); + virtual void SetParent(Panel *newParent); + virtual void SetParent(VPANEL newParent); + virtual bool HasParent(VPANEL potentialParent); + + int GetChildCount(); + Panel *GetChild(int index); + int FindChildIndexByName( const char *childName ); + Panel *FindChildByName(const char *childName, bool recurseDown = false); + Panel *FindSiblingByName(const char *siblingName); + void CallParentFunction(KeyValues *message); + + virtual bool LookupElementBounds( const char *elementName, int &x, int &y, int &wide, int &tall ) { return false; } + + virtual void SetAutoDelete(bool state); // if set to true, panel automatically frees itself when parent is deleted + virtual bool IsAutoDeleteSet(); + virtual void DeletePanel(); // simply does a { delete this; } + + // messaging + virtual void AddActionSignalTarget(Panel *messageTarget); + virtual void AddActionSignalTarget(VPANEL messageTarget); + virtual void RemoveActionSignalTarget(Panel *oldTarget); + virtual void PostActionSignal(KeyValues *message); // sends a message to the current actionSignalTarget(s) + virtual bool RequestInfoFromChild(const char *childName, KeyValues *outputData); + virtual void PostMessageToChild(const char *childName, KeyValues *messsage); + virtual void PostMessage(Panel *target, KeyValues *message, float delaySeconds = 0.0f); + virtual bool RequestInfo(KeyValues *outputData); // returns true if output is successfully written. You should always chain back to the base class if info request is not handled + virtual bool SetInfo(KeyValues *inputData); // sets a specified value in the control - inverse of the above + virtual void SetSilentMode( bool bSilent ); //change the panel's silent mode; if silent, the panel will not post any action signals + + // drawing state + virtual void SetEnabled(bool state); + virtual bool IsEnabled(); + virtual bool IsPopup(); // has a parent, but is in it's own space + virtual void GetClipRect(int &x0, int &y0, int &x1, int &y1); + virtual void MoveToFront(); + + // pin positions for auto-layout + enum PinCorner_e + { + PIN_TOPLEFT = 0, + PIN_TOPRIGHT, + PIN_BOTTOMLEFT, + PIN_BOTTOMRIGHT, + PIN_NO, + + // For sibling pinning + PIN_CENTER_TOP, + PIN_CENTER_RIGHT, + PIN_CENTER_BOTTOM, + PIN_CENTER_LEFT, + }; + + // specifies the auto-resize directions for the panel + enum AutoResize_e + { + AUTORESIZE_NO = 0, + AUTORESIZE_RIGHT, + AUTORESIZE_DOWN, + AUTORESIZE_DOWNANDRIGHT, + }; + + // Sets the pin corner for non-resizing panels + void SetPinCorner( PinCorner_e pinCorner, int nOffsetX, int nOffsetY ); + + // Sets the pin corner + resize mode for resizing panels + void SetAutoResize( PinCorner_e pinCorner, AutoResize_e resizeDir, int nPinOffsetX, int nPinOffsetY, int nUnpinnedCornerOffsetX, int nUnpinnedCornerOffsetY ); + + AutoResize_e GetAutoResize(); + PinCorner_e GetPinCorner(); + + // Gets the relative offset of the control from the pinned + non-pinned corner (for resizing) + void GetPinOffset( int &dx, int &dy ); + void GetResizeOffset( int &dx, int &dy ); + + void PinToSibling( const char *pszSibling, PinCorner_e pinOurCorner, PinCorner_e pinSibling ); + void UpdateSiblingPin( void ); + + // colors + virtual void SetBgColor(Color color); + virtual void SetFgColor(Color color); + virtual Color GetBgColor(); + virtual Color GetFgColor(); + + virtual void SetCursor(HCursor cursor); + virtual HCursor GetCursor(); + virtual void RequestFocus(int direction = 0); + virtual bool HasFocus(); + virtual void InvalidateLayout(bool layoutNow = false, bool reloadScheme = false); + virtual bool RequestFocusPrev(VPANEL panel = NULL); + virtual bool RequestFocusNext(VPANEL panel = NULL); + // tab positioning + virtual void SetTabPosition(int position); + virtual int GetTabPosition(); + // border + virtual void SetBorder(IBorder *border); + virtual IBorder *GetBorder(); + virtual void SetPaintBorderEnabled(bool state); + virtual void SetPaintBackgroundEnabled(bool state); + virtual void SetPaintEnabled(bool state); + virtual void SetPostChildPaintEnabled(bool state); + virtual void SetPaintBackgroundType(int type); // 0 for normal(opaque), 1 for single texture from Texture1, and 2 for rounded box w/ four corner textures + virtual void GetInset(int &left, int &top, int &right, int &bottom); + virtual void GetPaintSize(int &wide, int &tall); + virtual void SetBuildGroup(BuildGroup *buildGroup); + virtual bool IsBuildGroupEnabled(); + virtual bool IsCursorNone(); + virtual bool IsCursorOver(); // returns true if the cursor is currently over the panel + virtual void MarkForDeletion(); // object will free it's memory next tick + virtual bool IsLayoutInvalid(); // does this object require a perform layout? + virtual Panel *HasHotkey(wchar_t key); // returns the panel that has this hotkey + virtual bool IsOpaque(); + bool IsRightAligned(); // returns true if the settings are aligned to the right of the screen + bool IsBottomAligned(); // returns true if the settings are aligned to the bottom of the screen + bool IsPercentage(); // returns true if the settings are a percentage of screen size + + // scheme access functions + virtual HScheme GetScheme(); + virtual void SetScheme(const char *tag); + virtual void SetScheme(HScheme scheme); + virtual Color GetSchemeColor(const char *keyName,IScheme *pScheme); + virtual Color GetSchemeColor(const char *keyName, Color defaultColor,IScheme *pScheme); + + // called when scheme settings need to be applied; called the first time before the panel is painted + virtual void ApplySchemeSettings(IScheme *pScheme); + + // interface to build settings + // takes a group of settings and applies them to the control + virtual void ApplySettings(KeyValues *inResourceData); + virtual void OnUnserialized( CDmxElement *pElement ); + + // records the settings into the resource data + virtual void GetSettings(KeyValues *outResourceData); + + // gets a description of the resource for use in the UI + // format: ... + // unknown types as just displayed as strings in the UI (for future UI expansion) + virtual const char *GetDescription(); + + // returns the name of the module that this instance of panel was compiled into + virtual const char *GetModuleName(); + + // user configuration settings + // this is used for any control details the user wants saved between sessions + // eg. dialog positions, last directory opened, list column width + virtual void ApplyUserConfigSettings(KeyValues *userConfig); + + // returns user config settings for this control + virtual void GetUserConfigSettings(KeyValues *userConfig); + + // optimization, return true if this control has any user config settings + virtual bool HasUserConfigSettings(); + + // message handlers + // override to get access to the message + // override to get access to the message + virtual void OnMessage(const KeyValues *params, VPANEL fromPanel); // called when panel receives message; must chain back + MESSAGE_FUNC_CHARPTR( OnCommand, "Command", command ); // called when a panel receives a command + MESSAGE_FUNC( OnMouseCaptureLost, "MouseCaptureLost" ); // called after the panel loses mouse capture + MESSAGE_FUNC( OnSetFocus, "SetFocus" ); // called after the panel receives the keyboard focus + MESSAGE_FUNC( OnKillFocus, "KillFocus" ); // called after the panel loses the keyboard focus + MESSAGE_FUNC( OnDelete, "Delete" ); // called to delete the panel; Panel::OnDelete() does simply { delete this; } + virtual void OnThink(); // called every frame before painting, but only if panel is visible + virtual void OnChildAdded(VPANEL child); // called when a child has been added to this panel + virtual void OnSizeChanged(int newWide, int newTall); // called after the size of a panel has been changed + + // called every frame if ivgui()->AddTickSignal() is called + virtual void OnTick(); + + // input messages + MESSAGE_FUNC_INT_INT( OnCursorMoved, "OnCursorMoved", x, y ); + virtual void OnCursorEntered(); + virtual void OnCursorExited(); + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void OnMouseWheeled(int delta); + + // Trip pressing (e.g., select all text in a TextEntry) requires this to be enabled + virtual void SetTriplePressAllowed( bool state ); + virtual bool IsTriplePressAllowed() const; + virtual void OnMouseTriplePressed( MouseCode code ); + + static char const *KeyCodeToString( KeyCode code ); + static wchar_t const *KeyCodeToDisplayString( KeyCode code ); + static wchar_t const *KeyCodeModifiersToDisplayString( KeyCode code, int modifiers ); // L"Ctrl+Alt+Shift+Backspace" + + static KeyCode StringToKeyCode( char const *str ); +#if defined( VGUI_USEKEYBINDINGMAPS ) + static KeyBindingContextHandle_t CreateKeyBindingsContext( char const *filename, char const *pathID = 0 ); + virtual void SetKeyBindingsContext( KeyBindingContextHandle_t handle ); + virtual KeyBindingContextHandle_t GetKeyBindingsContext() const; + virtual bool IsValidKeyBindingsContext() const; + + static int GetPanelsWithKeyBindingsCount( KeyBindingContextHandle_t handle ); + static Panel *GetPanelWithKeyBindings( KeyBindingContextHandle_t handle, int index ); + + static void RevertKeyBindings( KeyBindingContextHandle_t handle ); + + static void ReloadKeyBindings( KeyBindingContextHandle_t handle ); + static void SaveKeyBindings( KeyBindingContextHandle_t handle ); + static void SaveKeyBindingsToFile( KeyBindingContextHandle_t handle, char const *filename, char const *pathID = 0 ); + static void LoadKeyBindings( KeyBindingContextHandle_t handle ); + static void LoadKeyBindingsForOnePanel( KeyBindingContextHandle_t handle, Panel *panelOfInterest ); + + // OnKeyCodeTyped hooks into here for action + virtual bool IsKeyRebound( KeyCode code, int modifiers ); + // If a panel implements this and returns true, then the IsKeyRebound check will fail and OnKeyCodeTyped messages will pass through.. + // sort of like setting the SetAllowKeyBindingChainToParent flag to false for specific keys + virtual bool IsKeyOverridden( KeyCode code, int modifiers ); + + virtual void AddKeyBinding( char const *bindingName, int keycode, int modifiers ); + + KeyBindingMap_t *LookupBinding( char const *bindingName ); + KeyBindingMap_t *LookupBindingByKeyCode( KeyCode code, int modifiers ); + void LookupBoundKeys( char const *bindingName, CUtlVector< BoundKey_t * >& list ); + BoundKey_t *LookupDefaultKey( char const *bindingName ); + PanelKeyBindingMap *LookupMapForBinding( char const *bindingName ); + + // Returns the number of keybindings + int GetKeyMappingCount( ); + + void RevertKeyBindingsToDefault(); + void RemoveAllKeyBindings(); + void ReloadKeyBindings(); + virtual void EditKeyBindings(); + + // calls RevertKeyBindingsToDefault() and then LoadKeyBindingsForOnePanel( GetKeyBindingsContext(), this ); + void SaveKeyBindingsToBuffer( int level, CUtlBuffer& buf ); + bool ParseKeyBindings( KeyValues *kv ); + + virtual char const *GetKeyBindingsFile() const; + virtual char const *GetKeyBindingsFilePathID() const; + + // Set this to false to disallow IsKeyRebound chaining to GetParent() Panels... + void SetAllowKeyBindingChainToParent( bool state ); + bool IsKeyBindingChainToParentAllowed() const; +#endif // VGUI_USEKEYBINDINGMAPS + + // base implementation forwards Key messages to the Panel's parent + // - override to 'swallow' the input + virtual void OnKeyCodePressed(KeyCode code); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnKeyTyped(wchar_t unichar); + virtual void OnKeyCodeReleased(KeyCode code); + virtual void OnKeyFocusTicked(); // every window gets key ticked events + + // forwards mouse messages to the panel's parent + MESSAGE_FUNC( OnMouseFocusTicked, "OnMouseFocusTicked" ); + + // message handlers that don't go through the message pump + virtual void PaintBackground(); + virtual void Paint(); + virtual void PaintBorder(); + virtual void PaintBuildOverlay(); // the extra drawing for when in build mode + virtual void PostChildPaint(); + virtual void PerformLayout(); + + // this enables message mapping for this class - requires matching IMPLEMENT_PANELDESC() in the .cpp file + DECLARE_PANELMAP(); + + virtual VPANEL GetCurrentKeyFocus(); + + // returns a pointer to the tooltip object associated with the panel + // creates a new one if none yet exists + Tooltip *GetTooltip(); + + // proportional mode settings + virtual bool IsProportional() { return _flags.IsFlagSet( IS_PROPORTIONAL ); } + virtual void SetProportional(bool state); + + // input interest + virtual void SetMouseInputEnabled( bool state ); + virtual void SetKeyBoardInputEnabled( bool state ); + virtual bool IsMouseInputEnabled(); + virtual bool IsKeyBoardInputEnabled(); + virtual bool HandleMouseCode( MouseCode code ); + + // allows you to disable for this panel but not children + void DisableMouseInputForThisPanel( bool bDisable ); + bool IsMouseInputDisabledForThisPanel() const; + + virtual void DrawTexturedBox( int x, int y, int wide, int tall, Color color, float normalizedAlpha ); + virtual void DrawBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha, bool hollow = false ); + virtual void DrawBoxFade(int x, int y, int wide, int tall, Color color, float normalizedAlpha, unsigned int alpha0, unsigned int alpha1, bool bHorizontal, bool hollow = false ); + virtual void DrawHollowBox(int x, int y, int wide, int tall, Color color, float normalizedAlpha ); + +// Drag Drop Public interface + + virtual void SetDragEnabled( bool enabled ); + virtual bool IsDragEnabled() const; + + virtual void SetShowDragHelper( bool enabled ); + + // Called if drag drop is started but not dropped on top of droppable panel... + virtual void OnDragFailed( CUtlVector< KeyValues * >& msglist ); + + // Use this to prevent chaining up from a parent which can mess with mouse functionality if you don't want to chain up from a child panel to the best + // draggable parent. + virtual void SetBlockDragChaining( bool block ); + virtual bool IsBlockingDragChaining() const; + + virtual int GetDragStartTolerance() const; + virtual void SetDragSTartTolerance( int nTolerance ); + + // If hover context time is non-zero, then after the drop cursor is hovering over the panel for that amount of time + // the Show hover context menu function will be invoked + virtual void SetDropEnabled( bool enabled, float m_flHoverContextTime = 0.0f ); + virtual bool IsDropEnabled() const; + + // Called if m_flHoverContextTime was non-zero, allows droppee to preview the drop data and show an appropriate menu + // Return false if not using context menu + virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ); + virtual void OnDropContextHoverShow( CUtlVector< KeyValues * >& msglist ); + virtual void OnDropContextHoverHide( CUtlVector< KeyValues * >& msglist ); + +#if defined( VGUI_USEDRAGDROP ) + virtual DragDrop_t *GetDragDropInfo(); +#endif + // For handling multiple selections... + virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ); + + virtual void OnCreateDragData( KeyValues *msg ); + // Called to see if a drop enabled panel can accept the specified data blob + virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); + + // Mouse is on draggable panel and has started moving, but is not over a droppable panel yet + virtual void OnDraggablePanelPaint(); + // Mouse is now over a droppable panel + virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ); + + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); + + // called on droptarget when draggable panel entered/exited droptarget + virtual void OnPanelEnteredDroppablePanel( CUtlVector< KeyValues * >& msglist ); + virtual void OnPanelExitedDroppablePanel ( CUtlVector< KeyValues * >& msglist ); + + // Chains up to any parent marked DropEnabled + virtual Panel *GetDropTarget( CUtlVector< KeyValues * >& msglist ); + // Chains up to first parent marked DragEnabled + virtual Panel *GetDragPanel(); + virtual bool IsBeingDragged(); + virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist ); + virtual HCursor GetDragFailCursor( CUtlVector< KeyValues * >& msglist ) { return dc_no; } + + Color GetDropFrameColor(); + Color GetDragFrameColor(); + + // Can override to require custom behavior to start the drag state + virtual bool CanStartDragging( int startx, int starty, int mx, int my ); + + // Draws a filled rect of specified bounds, but omits the bounds of the skip panel from those bounds + virtual void FillRectSkippingPanel( const Color clr, int x, int y, int w, int h, Panel *skipPanel ); + + virtual int GetPaintBackgroundType(); + virtual void GetCornerTextureSize( int& w, int& h ); + + bool IsChildOfModalSubTree(); + bool IsChildOfSurfaceModalPanel(); + + bool ShouldHandleInputMessage(); + + virtual void SetSkipChildDuringPainting( Panel *child ); + + // If this is set, then the drag drop won't occur until the mouse leaves the drag panels current rectangle + void SetStartDragWhenMouseExitsPanel( bool state ); + bool IsStartDragWhenMouseExitsPanel() const; + + // Forces context ID for this panel and all children below it + void SetMessageContextId_R( int nContextID ); + + void PostMessageToAllSiblings( KeyValues *msg, float delaySeconds = 0.0f ); + template< class S > + void PostMessageToAllSiblingsOfType( KeyValues *msg, float delaySeconds = 0.0f ); + + void SetConsoleStylePanel( bool bConsoleStyle ); + bool IsConsoleStylePanel() const; + + // For 360: support directional navigation between UI controls via dpad + enum NAV_DIRECTION { ND_UP, ND_DOWN, ND_LEFT, ND_RIGHT, ND_BACK, ND_NONE }; + virtual Panel* NavigateUp(); + virtual Panel* NavigateDown(); + virtual Panel* NavigateLeft(); + virtual Panel* NavigateRight(); + virtual void NavigateTo(); + virtual void NavigateFrom(); + virtual void NavigateToChild( Panel *pNavigateTo ); //mouse support + + Panel* SetNavUp( Panel* navUp ); + Panel* SetNavDown( Panel* navDown ); + Panel* SetNavLeft( Panel* navLeft ); + Panel* SetNavRight( Panel* navRight ); + NAV_DIRECTION GetLastNavDirection(); + MESSAGE_FUNC_CHARPTR( OnNavigateTo, "OnNavigateTo", panelName ); + MESSAGE_FUNC_CHARPTR( OnNavigateFrom, "OnNavigateFrom", panelName ); + +// Drag Drop protected/internal interface +protected: + + virtual void OnStartDragging(); + virtual void OnContinueDragging(); + virtual void OnFinishDragging( bool mousereleased, MouseCode code, bool aborted = false ); + + virtual void DragDropStartDragging(); + + virtual void GetDragData( CUtlVector< KeyValues * >& list ); + virtual void CreateDragData(); + + virtual void PaintTraverse(bool Repaint, bool allowForce = true); + +protected: + MESSAGE_FUNC_ENUM_ENUM( OnRequestFocus, "OnRequestFocus", VPANEL, subFocus, VPANEL, defaultPanel); + MESSAGE_FUNC_INT_INT( OnScreenSizeChanged, "OnScreenSizeChanged", oldwide, oldtall ); + virtual void *QueryInterface(EInterfaceID id); + + void AddToOverridableColors( Color *pColor, char const *scriptname ) + { + int iIdx = m_OverridableColorEntries.AddToTail(); + m_OverridableColorEntries[iIdx].m_pszScriptName = scriptname; + m_OverridableColorEntries[iIdx].m_pColor = pColor; + m_OverridableColorEntries[iIdx].m_bOverridden = false; + } + + void ApplyOverridableColors( IScheme *pScheme ); + void SetOverridableColor( Color *pColor, const Color &newColor ); + +protected: + void SetNavUp( const char* controlName ); + void SetNavDown( const char* controlName ); + void SetNavLeft( const char* controlName ); + void SetNavRight( const char* controlName ); + +public: + /* + Will recursively look for the next visible panel in the navigation chain, parameters are for internal use. + It will stop looking if first == nextpanel (to prevent infinite looping). + */ + Panel* GetNavUp( Panel *first = NULL ); + Panel* GetNavDown( Panel *first = NULL ); + Panel* GetNavLeft( Panel *first = NULL ); + Panel* GetNavRight( Panel *first = NULL ); + + // if set, Panel gets PerformLayout called after the camera and the renderer's m_matrixWorldToScreen has been setup, so panels can be correctly attached to entities in the world + inline void SetWorldPositionCurrentFrame( bool bWorldPositionCurrentFrame ) { m_bWorldPositionCurrentFrame = bWorldPositionCurrentFrame; } + inline bool GetWorldPositionCurrentFrame() { return m_bWorldPositionCurrentFrame; } + +protected: + //this will return m_NavDown and will not look for the next visible panel + Panel* GetNavUpPanel(); + Panel* GetNavDownPanel(); + Panel* GetNavLeftPanel(); + Panel* GetNavRightPanel(); + + bool m_PassUnhandledInput; + NAV_DIRECTION m_LastNavDirection; + + void InternalInitDefaultValues( PanelAnimationMap *map ); + + +private: + enum BuildModeFlags_t + { + BUILDMODE_EDITABLE = 0x01, + BUILDMODE_DELETABLE = 0x02, + BUILDMODE_SAVE_XPOS_RIGHTALIGNED = 0x04, + BUILDMODE_SAVE_XPOS_CENTERALIGNED = 0x08, + BUILDMODE_SAVE_YPOS_BOTTOMALIGNED = 0x10, + BUILDMODE_SAVE_YPOS_CENTERALIGNED = 0x20, + BUILDMODE_SAVE_WIDE_FULL = 0x40, + BUILDMODE_SAVE_TALL_FULL = 0x80, + BUILDMODE_SAVE_PROPORTIONAL_TO_PARENT = 0x100, + BUILDMODE_SAVE_PERCENTAGE = 0x200, + }; + + enum PanelFlags_t + { + MARKED_FOR_DELETION = 0x0001, + NEEDS_REPAINT = 0x0002, + PAINT_BORDER_ENABLED = 0x0004, + PAINT_BACKGROUND_ENABLED = 0x0008, + PAINT_ENABLED = 0x0010, + POST_CHILD_PAINT_ENABLED = 0x0020, + AUTODELETE_ENABLED = 0x0040, + NEEDS_LAYOUT = 0x0080, + NEEDS_SCHEME_UPDATE = 0x0100, + NEEDS_DEFAULT_SETTINGS_APPLIED = 0x0200, +#if defined( VGUI_USEKEYBINDINGMAPS ) + ALLOW_CHAIN_KEYBINDING_TO_PARENT = 0x0400, +#endif + IN_PERFORM_LAYOUT = 0x0800, + IS_PROPORTIONAL = 0x1000, + TRIPLE_PRESS_ALLOWED = 0x2000, + DRAG_REQUIRES_PANEL_EXIT = 0x4000, + IS_MOUSE_DISABLED_FOR_THIS_PANEL_ONLY = 0x8000, + ALL_FLAGS = 0xFFFF, + }; + + // used to get the Panel * for users with only IClientPanel + virtual Panel *GetPanel() { return this; } + + // private methods + void Think(); + void PerformApplySchemeSettings(); + + void InternalPerformLayout(); + void InternalSetCursor(); + + MESSAGE_FUNC_INT_INT( InternalCursorMoved, "CursorMoved", xpos, ypos ); + MESSAGE_FUNC( InternalCursorEntered, "CursorEntered" ); + MESSAGE_FUNC( InternalCursorExited, "CursorExited" ); + + MESSAGE_FUNC_INT( InternalMousePressed, "MousePressed", code ); + MESSAGE_FUNC_INT( InternalMouseDoublePressed, "MouseDoublePressed", code ); + // Triple presses are synthesized + MESSAGE_FUNC_INT( InternalMouseTriplePressed, "MouseTriplePressed", code ); + MESSAGE_FUNC_INT( InternalMouseReleased, "MouseReleased", code ); + MESSAGE_FUNC_INT( InternalMouseWheeled, "MouseWheeled", delta ); + MESSAGE_FUNC_INT( InternalKeyCodePressed, "KeyCodePressed", code ); + MESSAGE_FUNC_INT( InternalKeyCodeTyped, "KeyCodeTyped", code ); + MESSAGE_FUNC_INT( InternalKeyTyped, "KeyTyped", unichar ); + MESSAGE_FUNC_INT( InternalKeyCodeReleased, "KeyCodeReleased", code ); + + MESSAGE_FUNC( InternalKeyFocusTicked, "KeyFocusTicked" ); + MESSAGE_FUNC( InternalMouseFocusTicked, "MouseFocusTicked" ); + + MESSAGE_FUNC( InternalInvalidateLayout, "Invalidate" ); + + MESSAGE_FUNC( InternalMove, "Move" ); + virtual void InternalFocusChanged(bool lost); // called when the focus gets changed + + void Init( int x, int y, int wide, int tall ); + void PreparePanelMap( PanelMap_t *panelMap ); + + bool InternalRequestInfo( PanelAnimationMap *map, KeyValues *outputData ); + bool InternalSetInfo( PanelAnimationMap *map, KeyValues *inputData ); + + PanelAnimationMapEntry *FindPanelAnimationEntry( char const *scriptname, PanelAnimationMap *map ); + + // Recursively invoke settings for PanelAnimationVars + void InternalApplySettings( PanelAnimationMap *map, KeyValues *inResourceData); + + // Purpose: Loads panel details related to autoresize from the resource info + void ApplyAutoResizeSettings(KeyValues *inResourceData); + + void FindDropTargetPanel_R( CUtlVector< VPANEL >& panelList, int x, int y, VPANEL check ); + Panel *FindDropTargetPanel(); + + int GetProportionalScaledValue( int rootTall, int normalizedValue ); + +#if defined( VGUI_USEDRAGDROP ) + DragDrop_t *m_pDragDrop; + Color m_clrDragFrame; + Color m_clrDropFrame; +#endif + + Tooltip *m_pTooltips; + + PHandle m_SkipChild; + long m_lLastDoublePressTime; + HFont m_infoFont; // this is used exclusively by drag drop panels + +#if defined( VGUI_USEKEYBINDINGMAPS ) + KeyBindingContextHandle_t m_hKeyBindingsContext; +#endif + + // data + VPANEL _vpanel; // handle to a vgui panel + CUtlString _panelName; // string name of the panel - only unique within the current context + IBorder *_border; + + CUtlFlags< unsigned short > _flags; // see PanelFlags_t + Dar _actionSignalTargetDar; // the panel to direct notify messages to ("Command", "TextChanged", etc.) + + CUtlVector m_OverridableColorEntries; + + Color _fgColor; // foreground color + Color _bgColor; // background color + + HBuildGroup _buildGroup; + + short m_nPinDeltaX; // Relative position of the pinned corner to the edge + short m_nPinDeltaY; + short m_nResizeDeltaX; // Relative position of the non-pinned corner to the edge + short m_nResizeDeltaY; + + HCursor _cursor; + unsigned short _buildModeFlags; // flags that control how the build mode dialog handles this panel + + byte _pinCorner : 4; // the corner of the dialog this panel is pinned to + byte _autoResizeDirection : 4; // the directions in which the panel will auto-resize to + + DECLARE_DMXELEMENT_BITFIELD( _pinCorner, byte, Panel ) + DECLARE_DMXELEMENT_BITFIELD( _autoResizeDirection, byte, Panel ) + + + unsigned char _tabPosition; // the panel's place in the tab ordering + HScheme m_iScheme; // handle to the scheme to use + + bool m_bIsDMXSerialized : 1; // Is this a DMX panel? + bool m_bUseSchemeColors : 1; // Should we use colors from the scheme? + bool m_bIsSilent : 1; // should this panel PostActionSignals? + bool m_bIsConsoleStylePanel : 1; + + DECLARE_DMXELEMENT_BITFIELD( m_bUseSchemeColors, bool, Panel ) + DECLARE_DMXELEMENT_BITFIELD( m_bIsSilent, bool, Panel ) + + // Sibling pinning + char *_pinToSibling; // string name of the sibling panel we're pinned to + byte _pinToSiblingCorner; // the corner of the sibling panel we're pinned to + byte _pinCornerToSibling; // the corner of our panel that we're pinning to our sibling + PHandle m_pinSibling; + + bool m_bWorldPositionCurrentFrame; // if set, Panel gets PerformLayout called after the camera and the renderer's m_matrixWorldToScreen has been setup, so panels can be correctly attached to entities in the world + + CUtlString m_sNavUpName; + PHandle m_NavUp; + + CUtlString m_sNavDownName; + PHandle m_NavDown; + + CUtlString m_sNavLeftName; + PHandle m_NavLeft; + + CUtlString m_sNavRightName; + PHandle m_NavRight; +protected: + static int s_NavLock; + +private: + + CPanelAnimationVar( float, m_flAlpha, "alpha", "255" ); + + // 1 == Textured (TextureId1 only) + // 2 == Rounded Corner Box + CPanelAnimationVar( int, m_nPaintBackgroundType, "PaintBackgroundType", "0" ); + CPanelAnimationVarAliasType( int, m_nBgTextureId1, "Texture1", "vgui/hud/800corner1", "textureid" ); + CPanelAnimationVarAliasType( int, m_nBgTextureId2, "Texture2", "vgui/hud/800corner2", "textureid" ); + CPanelAnimationVarAliasType( int, m_nBgTextureId3, "Texture3", "vgui/hud/800corner3", "textureid" ); + CPanelAnimationVarAliasType( int, m_nBgTextureId4, "Texture4", "vgui/hud/800corner4", "textureid" ); + + friend class BuildGroup; + friend class BuildModeDialog; + friend class PHandle; + + // obselete, remove soon + void OnOldMessage(KeyValues *params, VPANEL ifromPanel); + +public: + + virtual void GetSizerMinimumSize(int &wide, int &tall); + virtual void GetSizerClientArea(int &x, int &y, int &wide, int &tall); + CSizerBase *GetSizer(); + void SetSizer( CSizerBase* pSizer ); + +protected: + + CSizerBase *m_pSizer; +}; + +inline void Panel::DisableMouseInputForThisPanel( bool bDisable ) +{ + _flags.SetFlag( IS_MOUSE_DISABLED_FOR_THIS_PANEL_ONLY, bDisable ); +} + +inline bool Panel::IsMouseInputDisabledForThisPanel() const +{ + return _flags.IsFlagSet( IS_MOUSE_DISABLED_FOR_THIS_PANEL_ONLY ); +} + + template< class S > + inline void Panel::PostMessageToAllSiblingsOfType( KeyValues *msg, float delaySeconds /*= 0.0f*/ ) + { + Panel *parent = GetParent(); + if ( parent ) + { + int nChildCount = parent->GetChildCount(); + for ( int i = 0; i < nChildCount; ++i ) + { + Panel *sibling = parent->GetChild( i ); + if ( sibling == this ) + continue; + if ( dynamic_cast< S * >( sibling ) ) + { + PostMessage( sibling->GetVPanel(), msg->MakeCopy(), delaySeconds ); + } + } + } + + msg->deleteThis(); + } + +} // namespace vgui + + +#endif // PANEL_H diff --git a/public/vgui_controls/PanelAnimationVar.h b/public/vgui_controls/PanelAnimationVar.h new file mode 100644 index 0000000..fb52ec2 --- /dev/null +++ b/public/vgui_controls/PanelAnimationVar.h @@ -0,0 +1,161 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PANELANIMATIONVAR_H +#define PANELANIMATIONVAR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlvector.h" +#include + +#define DECLARE_PANELANIMATION( className ) \ + static void AddToAnimationMap( char const *scriptname, char const *type, char const *var, \ + char const *defaultvalue, bool array, PANELLOOKUPFUNC func ) \ + { \ + PanelAnimationMap *map = FindOrAddPanelAnimationMap( GetPanelClassName() ); \ + \ + PanelAnimationMapEntry entry; \ + entry.m_pszScriptName = scriptname; \ + entry.m_pszVariable = var; \ + entry.m_pszType = type; \ + entry.m_pszDefaultValue = defaultvalue; \ + entry.m_pfnLookup = func; \ + entry.m_bArray = array; \ + \ + map->entries.AddToTail( entry ); \ + } \ + \ + static void ChainToAnimationMap( void ) \ + { \ + static bool chained = false; \ + if ( chained ) \ + return; \ + chained = true; \ + PanelAnimationMap *map = FindOrAddPanelAnimationMap( GetPanelClassName() ); \ + map->pfnClassName = GetPanelClassName; \ + if ( map && GetPanelBaseClassName() && GetPanelBaseClassName()[0] ) \ + { \ + map->baseMap = FindOrAddPanelAnimationMap( GetPanelBaseClassName() ); \ + } \ + } \ + \ + class className##_Register; \ + friend class className##_Register; \ + class className##_Register \ + { \ + public: \ + className##_Register() \ + { \ + className::ChainToAnimationMap(); \ + } \ + }; \ + className##_Register m_RegisterAnimationClass; \ + \ + virtual PanelAnimationMap *GetAnimMap() \ + { \ + return FindOrAddPanelAnimationMap( GetPanelClassName() ); \ + } + +typedef void *( *PANELLOOKUPFUNC )( vgui::Panel *panel ); + +// Use this macro to define a variable which hudanimations.txt and hudlayout.res scripts can access +#define CPanelAnimationVarAliasType( type, name, scriptname, defaultvalue, typealias ) \ + class PanelAnimationVar_##name; \ + friend class PanelAnimationVar_##name; \ + static void *GetVar_##name( vgui::Panel *panel ) \ + { \ + return &(( ThisClass *)panel)->name; \ + } \ + class PanelAnimationVar_##name \ + { \ + public: \ + static void InitVar() \ + { \ + static bool bAdded = false; \ + if ( !bAdded ) \ + { \ + bAdded = true; \ + AddToAnimationMap( scriptname, typealias, #name, defaultvalue, false, ThisClass::GetVar_##name ); \ + } \ + } \ + PanelAnimationVar_##name() \ + { \ + PanelAnimationVar_##name::InitVar(); \ + } \ + }; \ + PanelAnimationVar_##name m_##name##_register; \ + type name; + +#define CPanelAnimationVar( type, name, scriptname, defaultvalue ) \ + CPanelAnimationVarAliasType( type, name, scriptname, defaultvalue, #type ) + +// Use this macro to define a variable which hudanimations.txt and hudlayout.res scripts can access +#define CPanelAnimationStringVarAliasType( count, name, scriptname, defaultvalue, typealias ) \ + class PanelAnimationVar_##name; \ + friend class PanelAnimationVar_##name; \ + static void *GetVar_##name( vgui::Panel *panel ) \ + { \ + return &(( ThisClass *)panel)->name; \ + } \ + class PanelAnimationVar_##name \ + { \ + public: \ + static void InitVar() \ + { \ + static bool bAdded = false; \ + if ( !bAdded ) \ + { \ + bAdded = true; \ + AddToAnimationMap( scriptname, typealias, #name, defaultvalue, true, ThisClass::GetVar_##name ); \ + } \ + } \ + PanelAnimationVar_##name() \ + { \ + PanelAnimationVar_##name::InitVar(); \ + } \ + }; \ + PanelAnimationVar_##name m_##name##_register; \ + char name[ count ]; + +#define CPanelAnimationStringVar( count, name, scriptname, defaultvalue ) \ + CPanelAnimationStringVarAliasType( count, name, scriptname, defaultvalue, "string" ) + +struct PanelAnimationMapEntry +{ + char const *name() { return m_pszScriptName; } + char const *type() { return m_pszType; } + char const *defaultvalue() { return m_pszDefaultValue; } + bool isarray() { return m_bArray; } + + char const *m_pszScriptName; + char const *m_pszVariable; + char const *m_pszType; + char const *m_pszDefaultValue; + bool m_bArray; + + PANELLOOKUPFUNC m_pfnLookup; +}; + +struct PanelAnimationMap +{ + PanelAnimationMap() + { + baseMap = NULL; + pfnClassName = NULL; + } + + CUtlVector< PanelAnimationMapEntry > entries; + PanelAnimationMap *baseMap; + char const *(*pfnClassName)( void ); +}; + +PanelAnimationMap *FindPanelAnimationMap( char const *className ); +PanelAnimationMap *FindOrAddPanelAnimationMap( char const *className ); +void PanelAnimationDumpVars( char const *className ); + +#endif // PANELANIMATIONVAR_H diff --git a/public/vgui_controls/PanelListPanel.h b/public/vgui_controls/PanelListPanel.h new file mode 100644 index 0000000..be129f5 --- /dev/null +++ b/public/vgui_controls/PanelListPanel.h @@ -0,0 +1,143 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PANELLISTPANEL_H +#define PANELLISTPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include + +class KeyValues; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: A list of variable height child panels +// each list item consists of a label-panel pair. Height of the item is +// determined from the label. +//----------------------------------------------------------------------------- +class PanelListPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( PanelListPanel, Panel ); + +public: + PanelListPanel( vgui::Panel *parent, char const *panelName ); + ~PanelListPanel(); + + // DATA & ROW HANDLING + // The list now owns the panel + virtual int AddItem( Panel *labelPanel, Panel *panel ); + int GetItemCount() const; + int GetItemIDFromRow( int nRow ) const; + int GetVisibleItemCount(); + + // Show / hide an item + void SetItemVisible( int nItemID, bool bVisible ); + bool IsItemVisible( int nItemID ) const; + + // Iteration. Use these until they return InvalidItemID to iterate all the items. + int FirstItem() const; + int NextItem( int nItemID ) const; + int InvalidItemID() const; + + virtual Panel *GetItemLabel(int itemID); + virtual Panel *GetItemPanel(int itemID); + + virtual void RemoveItem(int itemID); // removes an item from the table (changing the indices of all following items) + virtual void DeleteAllItems(); // clears and deletes all the memory used by the data items + void RemoveAll(); + void HideAllItems(); + + // painting + virtual vgui::Panel *GetCellRenderer( int row ); + + // layout + void SetFirstColumnWidth( int width ); + int GetFirstColumnWidth(); + void SetNumColumns( int iNumColumns ); + int GetNumColumns( void ); + void MoveScrollBarToTop(); + void OverrideChildPanelWidth( bool bOverride ); // if true, width of child panels is set to fill this panel's width + + // selection + void SetSelectedPanel( Panel *panel ); + Panel *GetSelectedPanel(); + /* + On a panel being selected, a message gets sent to it + "PanelSelected" int "state" + where state is 1 on selection, 0 on deselection + */ + + void SetVerticalBufferPixels( int buffer ); + int GetVerticalBufferPixels() { return m_iPanelBuffer; } + + void ScrollToItem( int itemNumber ); + + // scrollbar + void SetShowScrollBar( bool bShow ); + bool GetShowScrollbar() { return m_bShowScrollBar; } + ScrollBar *GetScrollBar() { return m_vbar; } + + // mouse wheel + void AllowMouseWheel( bool bAllow ); + + CUtlVector< int > *GetSortedVector( void ) + { + return &m_SortedItems; + } + +protected: + // overrides + virtual void OnSizeChanged(int wide, int tall); + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); + virtual void PerformLayout(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void OnMouseWheeled(int delta); + + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void GetSettings( KeyValues *outResourceData ); + virtual const char *GetDescription( void ); + +private: + int ComputeVPixelsNeeded(); + + enum { DEFAULT_HEIGHT = 24, PANELBUFFER = 5 }; + + typedef struct dataitem_s + { + // Always store a panel pointer + Panel *panel; + Panel *labelPanel; + } DATAITEM; + + // list of the column headers + + CUtlLinkedList m_DataItems; + CUtlVector m_SortedItems; + + ScrollBar *m_vbar; + Panel *m_pPanelEmbedded; + + PHandle m_hSelectedItem; + int m_iFirstColumnWidth; + int m_iNumColumns; + int m_iDefaultHeight; + int m_iPanelBuffer; + bool m_bShowScrollBar; + bool m_bAllowMouseWheel; + bool m_bOverrideChildPanelWidth; +}; + +} +#endif // PANELLISTPANEL_H diff --git a/public/vgui_controls/PerforceFileExplorer.h b/public/vgui_controls/PerforceFileExplorer.h new file mode 100644 index 0000000..7c63709 --- /dev/null +++ b/public/vgui_controls/PerforceFileExplorer.h @@ -0,0 +1,67 @@ +//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Allows you to browse a directory structure, showing perforce files +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PERFORCEFILEEXPLORER_H +#define PERFORCEFILEEXPLORER_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/utlstring.h" +#include "vgui_controls/Frame.h" + + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class PerforceFileList; +class ComboBox; +class Button; + + +//----------------------------------------------------------------------------- +// Contains a list of files, determines their perforce status +//----------------------------------------------------------------------------- +class PerforceFileExplorer : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( PerforceFileExplorer, Frame ); + +public: + // The context keyvalues are added to all messages sent by this dialog if they are specified + PerforceFileExplorer( Panel *parent, const char *pPanelName ); + ~PerforceFileExplorer(); + + // Inherited from Frame + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void PerformLayout(); + +protected: + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", kv ); + MESSAGE_FUNC( OnItemDoubleClicked, "ItemDoubleClicked" ); + MESSAGE_FUNC( OnFolderUp, "FolderUp" ); + + void PopulateFileList(); + void PopulateDriveList(); + + // Returns the current directory + void SetCurrentDirectory( const char *pCurrentDirectory ); + + Button *m_pFolderUpButton; + ComboBox *m_pFullPathCombo; + PerforceFileList *m_pFileList; + CUtlString m_CurrentDirectory; +}; + + +} // namespace vgui + +#endif // PERFORCEFILEEXPLORER_H diff --git a/public/vgui_controls/PerforceFileList.h b/public/vgui_controls/PerforceFileList.h new file mode 100644 index 0000000..efc6911 --- /dev/null +++ b/public/vgui_controls/PerforceFileList.h @@ -0,0 +1,114 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Contains a list of files, determines their perforce status +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PERFORCEFILELIST_H +#define PERFORCEFILELIST_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utlstring.h" +#include "tier1/utlstringmap.h" +#include "vgui_controls/ListPanel.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct P4File_t; + +namespace vgui +{ + class ListPanel; +} + + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Contains a list of files, determines their perforce status +//----------------------------------------------------------------------------- +class PerforceFileList : public vgui::ListPanel +{ + DECLARE_CLASS_SIMPLE( PerforceFileList, ListPanel ); + +public: + // The context keyvalues are added to all messages sent by this dialog if they are specified + PerforceFileList( Panel *parent, const char *pPanelName ); + ~PerforceFileList(); + + // Add a file to the file list. Note that this file may exist on disk or not + // and it may exist in perforce or not. It's specified as a full path on disk though. + // In the case where a file doesn't exist on disk, but it does exist in perforce + // specify where that file would appear on disk. + // This function returns the itemID of the added file + // If you already know the file exists or is a directory (or not), specify that in the call. + // -1 means autodetect whether the file exists or is a directory + int AddFile( const char *pFullPath, int nFileExists = -1, int nIsDirectory = -1 ); + + // Is a file already in the list? + bool IsFileInList( const char *pFullPath ); + + // Find the item ID associated with a particular file + int FindFile( const char *pFullPath ); + + // Remove all files from the list + void RemoveAllFiles(); + + // Refresh perforce information + void Refresh(); + + // Refresh perforce information manually + void RefreshPerforceState( int nItemID, bool bFileExists, P4File_t *pFileInfo ); + + // Is a particular list item a directory? + bool IsDirectoryItem( int nItemID ); + + // Returns the file associated with a particular item ID + const char *GetFile( int nItemID ); + + // Toggle showing deleted files or not + void ShowDeletedFiles( bool bShowDeletedFiles ); + + // Inherited from vgui::EditablePanel + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void OnMouseDoublePressed( MouseCode code ); + + /* + messages sent: + "ItemDoubleClicked" // Called when an item is double-clicked + */ + +protected: + struct DirectoryInfo_t + { + CUtlString m_ClientSpec; + CUtlVector< int > m_ItemIDs; + }; + + // Add a file to the file list. + int AddFileToFileList( const char *pFullPath, bool bExistsOnDisk ); + + // Add a directory to the file list. + int AddDirectoryToFileList( const char *pFullPath, bool bExistsOnDisk ); + + // Add a directory to the directory list, returns client spec + void AddItemToDirectoryList( const char *pFullPath, int nItemID, bool bIsDirectory ); + + // Used to look up directories -> client specs + CUtlStringMap< DirectoryInfo_t > m_Directories; + + // Show deleted files? + bool m_bShowDeletedFiles; +}; + + +} // namespace vgui + +#endif // PERFORCEFILELIST_H diff --git a/public/vgui_controls/ProgressBar.h b/public/vgui_controls/ProgressBar.h new file mode 100644 index 0000000..fa48f27 --- /dev/null +++ b/public/vgui_controls/ProgressBar.h @@ -0,0 +1,119 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +enum progress_textures_t +{ + PROGRESS_TEXTURE_FG, + PROGRESS_TEXTURE_BG, + + NUM_PROGRESS_TEXTURES, +}; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Status bar that visually displays discrete progress in the form +// of a segmented strip +//----------------------------------------------------------------------------- +class ProgressBar : public Panel +{ + DECLARE_CLASS_SIMPLE( ProgressBar, Panel ); + +public: + ProgressBar(Panel *parent, const char *panelName); + ~ProgressBar(); + + // 'progress' is in the range [0.0f, 1.0f] + MESSAGE_FUNC_FLOAT( SetProgress, "SetProgress", progress ); + float GetProgress(); + virtual void SetSegmentInfo( int gap, int width ); + + // utility function for calculating a time remaining string + static bool ConstructTimeRemainingString(wchar_t *output, int outputBufferSizeInBytes, float startTime, float currentTime, float currentProgress, float lastProgressUpdateTime, bool addRemainingSuffix); + + void SetBarInset( int pixels ); + int GetBarInset( void ); + + virtual void ApplySettings(KeyValues *inResourceData); + virtual void GetSettings(KeyValues *outResourceData); + virtual const char *GetDescription(); + + // returns the number of segment blocks drawn + int GetDrawnSegmentCount(); + + enum ProgressDir_e + { + PROGRESS_EAST, + PROGRESS_WEST, + PROGRESS_NORTH, + PROGRESS_SOUTH + }; + + int GetProgressDirection() const { return m_iProgressDirection; } + void SetProgressDirection( int val ) { m_iProgressDirection = val; } + +protected: + virtual void Paint(); + void PaintSegment( int &x, int &y, int tall, int wide ); + virtual void PaintBackground(); + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_PARAMS( OnDialogVariablesChanged, "DialogVariables", dialogVariables ); + /* CUSTOM MESSAGE HANDLING + "SetProgress" + input: "progress" - float value of the progress to set + */ + +protected: + int m_iProgressDirection; + float _progress; + +private: + int _segmentCount; + int _segmentGap; + int _segmentWide; + int m_iBarInset; + char *m_pszDialogVar; +}; + +#define NUM_CONTINUOUS_PROGRESS_BAR_TEXTURES 2 + +//----------------------------------------------------------------------------- +// Purpose: Non-segmented progress bar +//----------------------------------------------------------------------------- +class ContinuousProgressBar : public ProgressBar +{ + DECLARE_CLASS_SIMPLE( ContinuousProgressBar, ProgressBar ); + +public: + ContinuousProgressBar(Panel *parent, const char *panelName); + + virtual void Paint(); + virtual void PaintBackground(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + void SetImage(const char *imageName, progress_textures_t iPos); + +private: + int m_nTextureId[NUM_PROGRESS_TEXTURES]; + char *m_pszImageName[NUM_PROGRESS_TEXTURES]; + int m_lenImageName[NUM_PROGRESS_TEXTURES]; +}; + +} // namespace vgui + +#endif // PROGRESSBAR_H diff --git a/public/vgui_controls/ProgressBox.h b/public/vgui_controls/ProgressBox.h new file mode 100644 index 0000000..65ea145 --- /dev/null +++ b/public/vgui_controls/ProgressBox.h @@ -0,0 +1,99 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROGRESSBOX_H +#define PROGRESSBOX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +// prevent windows macros from messing with the class +#ifdef ProgressBox +#undef ProgressBox +#endif + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Popup discardable message box +//----------------------------------------------------------------------------- +class ProgressBox : public Frame +{ + DECLARE_CLASS_SIMPLE( ProgressBox, Frame ); + +public: + // title - Text to be displayed in the title bar of the window + // text - Text message in the message box + // parent - parent panel of the message box, by default it has no parent. + ProgressBox(const char *title, const char *text, const char *pszUnknownTimeString, Panel *parent = NULL); + ProgressBox(const wchar_t *wszTitle, const wchar_t *wszText, const wchar_t *wszUnknownTimeString, Panel *parent = NULL); + ~ProgressBox(); + + // Put the message box into a modal state + virtual void DoModal(Frame *pFrameOver = NULL); + + // make the message box appear and in a modeless state + virtual void ShowWindow(Frame *pFrameOver = NULL); + + // updates progress bar, range [0, 1] + virtual void SetProgress(float progress); + + // sets the info text + virtual void SetText(const char *text); + + // toggles visibility of the close box. + virtual void SetCancelButtonVisible(bool state); + + // toggles the enabled state of the cancel button (for if it needs to be disabled part way through a process) + virtual void SetCancelButtonEnabled(bool state); + + /* custom messages: + + "ProgressBoxCancelled" + sent if the user pressed the cancel button (must be enabled & visible for this to happen) + + */ + +protected: + virtual void PerformLayout(); + virtual void OnClose(); + virtual void OnCloseFrameButtonPressed(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnThink(); + virtual void OnCommand(const char *command); + virtual void OnTick(); + + // called when the update has been cancelled + virtual void OnCancel(); + +private: + MESSAGE_FUNC( OnShutdownRequest, "ShutdownRequest" ); + void Init(); + void UpdateTitle(); + + Label *m_pMessageLabel; + ProgressBar *m_pProgressBar; + Button *m_pCancelButton; + + wchar_t m_wszTitleString[128]; + wchar_t m_wcsInfoString[128]; + wchar_t m_wszUnknownTimeString[128]; + + float m_flFirstProgressUpdate; + float m_flLastProgressUpdate; + float m_flCurrentProgress; +}; + +} // namespace vgui + + +#endif // PROGRESSBOX_H diff --git a/public/vgui_controls/PropertyDialog.h b/public/vgui_controls/PropertyDialog.h new file mode 100644 index 0000000..2c3d144 --- /dev/null +++ b/public/vgui_controls/PropertyDialog.h @@ -0,0 +1,84 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROPERTYDIALOG_H +#define PROPERTYDIALOG_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Simple frame that holds a property sheet +//----------------------------------------------------------------------------- +class PropertyDialog : public Frame +{ + DECLARE_CLASS_SIMPLE( PropertyDialog, Frame ); + +public: + PropertyDialog(Panel *parent, const char *panelName); + ~PropertyDialog(); + + // returns a pointer to the PropertySheet this dialog encapsulates + virtual PropertySheet *GetPropertySheet(); + + // wrapper for PropertySheet interface + virtual void AddPage(Panel *page, const char *title); + virtual Panel *GetActivePage(); + virtual void ResetAllData(); + virtual void ApplyChanges(); + + // sets the text on the OK/Cancel buttons, overriding the default + void SetOKButtonText(const char *text); + void SetCancelButtonText(const char *text); + void SetApplyButtonText(const char *text); + + // changes the visibility of the buttons + void SetOKButtonVisible(bool state); + void SetCancelButtonVisible(bool state); + void SetApplyButtonVisible(bool state); + + /* MESSAGES SENT + "ResetData" - sent when page is loaded. Data should be reloaded from document into controls. + "ApplyChanges" - sent when the OK / Apply button is pressed. Changed data should be written into document. + */ + +protected: + // Called when the OK button is pressed. Simply closes the dialog. + virtual bool OnOK(bool applyOnly); + + // called when the Cancel button is pressed + virtual void OnCancel(); + + // vgui overrides + virtual void PerformLayout(); + virtual void OnCommand(const char *command); + virtual void ActivateBuildMode(); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void RequestFocus(int direction = 0); + + MESSAGE_FUNC( OnApplyButtonEnable, "ApplyButtonEnable" ); + void EnableApplyButton(bool bEnable); + +private: + PropertySheet *_propertySheet; + Button *_okButton; + Button *_cancelButton; + Button *_applyButton; + + CPanelAnimationVar( int, m_iSheetInsetBottom, "sheetinset_bottom", "32" ); +}; + +}; // vgui + +#endif // PROPERTYDIALOG_H diff --git a/public/vgui_controls/PropertyPage.h b/public/vgui_controls/PropertyPage.h new file mode 100644 index 0000000..7faedfe --- /dev/null +++ b/public/vgui_controls/PropertyPage.h @@ -0,0 +1,58 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROPERTYPAGE_H +#define PROPERTYPAGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Property page, as held by a set of property sheets +//----------------------------------------------------------------------------- +class PropertyPage : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( PropertyPage, EditablePanel ); + +public: + PropertyPage(Panel *parent, const char *panelName); + ~PropertyPage(); + + // Called when page is loaded. Data should be reloaded from document into controls. + MESSAGE_FUNC( OnResetData, "ResetData" ); + + // Called when the OK / Apply button is pressed. Changed data should be written into document. + MESSAGE_FUNC( OnApplyChanges, "ApplyChanges" ); + + // called when the page is shown/hidden + MESSAGE_FUNC( OnPageShow, "PageShow" ); + MESSAGE_FUNC( OnPageHide, "PageHide" ); + + virtual void OnKeyCodeTyped(KeyCode code); + virtual bool HasUserConfigSettings() { return true; } + + virtual void SetVisible(bool state); + +protected: + // called to be notified of the tab button used to Activate this page + // if overridden this must be chained back to + MESSAGE_FUNC_PTR( OnPageTabActivated, "PageTabActivated", panel ); + +private: + PHandle _pageTab; +}; + +} // namespace vgui + +#endif // PROPERTYPAGE_H diff --git a/public/vgui_controls/PropertySheet.h b/public/vgui_controls/PropertySheet.h new file mode 100644 index 0000000..45cc254 --- /dev/null +++ b/public/vgui_controls/PropertySheet.h @@ -0,0 +1,208 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PROPERTYSHEET_H +#define PROPERTYSHEET_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui/VGUI.h" +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/PHandle.h" +#include "utlvector.h" + +namespace vgui +{ + +class PageTab; +class ImagePanel; + +//----------------------------------------------------------------------------- +// Purpose: Tabbed property sheet. Holds and displays a set of Panel's +//----------------------------------------------------------------------------- +class PropertySheet : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( PropertySheet, EditablePanel ); + +public: + PropertySheet(Panel *parent, const char *panelName, bool draggableTabs = false ); + PropertySheet(Panel *parent, const char *panelName,ComboBox *combo); + ~PropertySheet(); + + virtual bool IsDraggableTab() const; + void SetDraggableTabs( bool state ); + + // Adds a page to the sheet. The first page added becomes the active sheet. + virtual void AddPage(Panel *page, const char *title, char const *imageName = NULL, bool showContextMenu = false, int nInsertBefore = -1 ); + + virtual void SetPageTitle( Panel *page, const char *title ); + + // sets the current page + virtual void SetActivePage(Panel *page); + + // sets the width, in pixels, of the page tab buttons. + virtual void SetTabWidth(int pixels); + + // Gets a pointer to the currently active page. + virtual Panel *GetActivePage(); + + // Removes (but doesn't delete) all pages + virtual void RemoveAllPages(); + + // reloads the data in all the property page + virtual void ResetAllData(); + + // writes out any changed data to the doc + virtual void ApplyChanges(); + + // focus handling - passed on to current active page + virtual void RequestFocus(int direction = 0); + virtual bool RequestFocusPrev(VPANEL panel = NULL); + virtual bool RequestFocusNext(VPANEL panel = NULL); + + // returns the ith panel + virtual Panel *GetPage(int i); + + // deletes this panel from the sheet + virtual void DeletePage(Panel *panel); + // removes this panel from the sheet, sets its parent to NULL, but does not delete it + virtual void RemovePage(Panel *panel); + + // returns the current activated tab + virtual Panel *GetActiveTab(); + + // returns the title text of the tab + virtual void GetActiveTabTitle(char *textOut, int bufferLen); + + // return tab "i" + virtual Panel *GetTab(int i); + + // returns the title of tab "i" + virtual bool GetTabTitle(int i,char *textOut, int bufferLen); + + // returns the index of the active page + virtual int GetActivePageNum(); + + // returns the number of pages in the sheet + virtual int GetNumPages(); + + // disable the page with title "title" + virtual void DisablePage(const char *title); + + // enable the page with title "title" + virtual void EnablePage(const char *title); + + virtual void SetSmallTabs( bool state ); + virtual bool IsSmallTabs() const; + virtual void SetShowTabs( bool state ); + + /* MESSAGES SENT TO PAGES + "PageShow" - sent when a page is shown + "PageHide" - sent when a page is hidden + "ResetData" - sent when the data should be reloaded from doc + "ApplyChanges" - sent when data should be written to doc + */ + + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); + virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); + // Mouse is now over a droppable panel + virtual void OnDroppablePanelPaint( CUtlVector< KeyValues * >& msglist, CUtlVector< Panel * >& dragPanels ); + + void ShowContextButtons( bool state ); + bool ShouldShowContextButtons() const; + + int FindPage( Panel *page ) const; + + bool PageHasContextMenu( Panel *page ) const; + + void SetKBNavigationEnabled( bool state ); + bool IsKBNavigationEnabled() const; + + virtual bool HasUserConfigSettings() { return true; } + virtual void OnThink(); + +protected: + virtual void PaintBorder(); + virtual void PerformLayout(); + virtual Panel *HasHotkey(wchar_t key); + virtual void ChangeActiveTab(int index); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnCommand(const char *command); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void ApplySettings(KeyValues *inResourceData); + + // internal message handlers + MESSAGE_FUNC_PTR( OnTabPressed, "TabPressed", panel ); + MESSAGE_FUNC_PTR_WCHARPTR( OnTextChanged, "TextChanged", panel, text ); + MESSAGE_FUNC_PARAMS( OnOpenContextMenu, "OpenContextMenu", params ); + MESSAGE_FUNC( OnApplyButtonEnable, "ApplyButtonEnable" ); + // called when default button has been set + MESSAGE_FUNC_HANDLE( OnDefaultButtonSet, "DefaultButtonSet", button ); + // called when the current default button has been set + MESSAGE_FUNC_HANDLE( OnCurrentDefaultButtonSet, "CurrentDefaultButtonSet", button); + MESSAGE_FUNC( OnFindDefaultButton, "FindDefaultButton" ); + + virtual void LayoutTabs(); + +private: + + // enable/disable the page with title "title" + virtual void SetPageEnabled(const char *title,bool state); + + // Grabs mouse coords and figures out best tab to insert "before" or -1 if insert should be at end of tab section row + PageTab *FindInsertBeforeTab(); + + void AddPageDropTab( char const *pTabName, Panel *pPage ); + void ClearPageDropTab(); + + struct Page_t + { + Page_t() : + page( 0 ), + contextMenu( false ) + { + } + + Panel *page; + bool contextMenu; + }; + + CUtlVector m_Pages; + CUtlVector m_PageTabs; + Panel *_activePage; + PageTab *_activeTab; + int _tabWidth; + int _activeTabIndex; + ComboBox *_combo; + bool _showTabs; + bool _tabFocus; + + PHandle m_hPreviouslyActivePage; + float m_flPageTransitionEffectTime; + bool m_bSmallTabs; + HFont m_tabFont; + bool m_bDraggableTabs; + bool m_bContextButton; + bool m_bKBNavigationEnabled; + + CPanelAnimationVarAliasType( int, m_iTabXIndent, "tabxindent", "0", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTabXDelta, "tabxdelta", "0", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTabHeight, "tabheight", "28", "proportional_int" ); + CPanelAnimationVarAliasType( int, m_iTabHeightSmall, "tabheight_small", "14", "proportional_int" ); + + KeyValues *m_pTabKV; + + int m_nPageDropTabVisibleTime; + PageTab *m_pDragDropTab; + PageTab *m_pTemporarilyRemoved; +}; + +}; // namespace vgui + +#endif // PROPERTYSHEET_H diff --git a/public/vgui_controls/QueryBox.h b/public/vgui_controls/QueryBox.h new file mode 100644 index 0000000..123819c --- /dev/null +++ b/public/vgui_controls/QueryBox.h @@ -0,0 +1,61 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Creates a Message box with a question in it and yes/no buttons +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef QUERYBOX_H +#define QUERYBOX_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Creates A Message box with a question in it and yes/no buttons +//----------------------------------------------------------------------------- +class QueryBox : public MessageBox +{ + DECLARE_CLASS_SIMPLE( QueryBox, MessageBox ); + +public: + QueryBox(const char *title, const char *queryText,vgui::Panel *parent = NULL ); + QueryBox(const wchar_t *wszTitle, const wchar_t *wszQueryText,vgui::Panel *parent = NULL); + ~QueryBox(); + + // Layout the window for drawing + virtual void PerformLayout(); + + // Set the keyvalues to send when ok button is hit + void SetOKCommand(KeyValues *keyValues); + + // Set the keyvalues to send when the cancel button is hit + void SetCancelCommand(KeyValues *keyValues); + + // Set the text on the Cancel button + void SetCancelButtonText(const char *buttonText); + void SetCancelButtonText(const wchar_t *wszButtonText); + + // Set a value of the ok command + void SetOKCommandValue(const char *keyName, int value); + +protected: + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnCommand(const char *command); + Button *m_pCancelButton; + +private: + KeyValues *m_pCancelCommand; + KeyValues *m_pOkCommand; +}; + +} +#endif // QUERYBOX_H diff --git a/public/vgui_controls/RadioButton.h b/public/vgui_controls/RadioButton.h new file mode 100644 index 0000000..d4e963c --- /dev/null +++ b/public/vgui_controls/RadioButton.h @@ -0,0 +1,75 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RADIOBUTTON_H +#define RADIOBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +class RadioImage; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Radio buttons are automatically selected into groups by who their +// parent is. At most one radio button is active at any time. +//----------------------------------------------------------------------------- +class RadioButton : public ToggleButton +{ + DECLARE_CLASS_SIMPLE( RadioButton, ToggleButton ); + +public: + RadioButton(Panel *parent, const char *panelName, const char *text); + ~RadioButton(); + + // Set the radio button checked. When a radio button is checked, a + // message is sent to all other radio buttons in the same group so + // they will become unchecked. + virtual void SetSelected(bool state); + + // Get the tab position of the radio button with the set of radio buttons + // A group of RadioButtons must have the same TabPosition, with [1, n] subtabpositions + virtual int GetSubTabPosition(); + virtual void SetSubTabPosition(int position); + + // Return the RadioButton's real tab position (its Panel one changes) + virtual int GetRadioTabPosition(); + +protected: + virtual void DoClick(); + + virtual void Paint(); + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_INT( OnRadioButtonChecked, "RadioButtonChecked", tabposition); + virtual void OnKeyCodeTyped(KeyCode code); + + virtual IBorder *GetBorder(bool depressed, bool armed, bool selected, bool keyfocus); + + virtual void ApplySettings(KeyValues *inResourceData); + virtual void GetSettings(KeyValues *outResourceData); + virtual const char *GetDescription(); + virtual void PerformLayout(); + + RadioButton *FindBestRadioButton(int direction); + +private: + RadioImage *_radioBoxImage; + int _oldTabPosition; + Color _selectedFgColor; + + int _subTabPosition; // tab position with the radio button list +}; + +}; // namespace vgui + +#endif // RADIOBUTTON_H diff --git a/public/vgui_controls/RichText.h b/public/vgui_controls/RichText.h new file mode 100644 index 0000000..fa36c42 --- /dev/null +++ b/public/vgui_controls/RichText.h @@ -0,0 +1,290 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef RICHTEXT_H +#define RICHTEXT_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class ClickPanel; + +//----------------------------------------------------------------------------- +// Purpose: Non-editable display of a rich text control +//----------------------------------------------------------------------------- +class RichText : public Panel +{ + DECLARE_CLASS_SIMPLE( RichText, Panel ); + +public: + RichText(Panel *parent, const char *panelName); + ~RichText(); + + // text manipulation + void SetText(const char *text); + void SetText(const wchar_t *text); + void GetText(int offset, wchar_t *buf, int bufLenInBytes); + void GetText(int offset, char *pch, int bufLenInBytes); + + // configuration + void SetFont(HFont font); + + // inserts characters at the end of the stream + void InsertChar(wchar_t ch); + void InsertString(const char *text); + void InsertString(const wchar_t *wszText); + + // selection + void SelectNone(); + void SelectAllText(); + void SelectNoText(); + MESSAGE_FUNC( CutSelected, "DoCutSelected" ); + MESSAGE_FUNC( CopySelected, "DoCopySelected" ); + + // sets the RichText control interactive or not (meaning you can select/copy text in the window) + void SetPanelInteractive( bool bInteractive ){ m_bInteractive = bInteractive; } + + // sets the RichText scrollbar invisible if it's not going to be used + void SetUnusedScrollbarInvisible( bool bInvis ){ m_bUnusedScrollbarInvis = bInvis; } + + // cursor movement + void GotoTextStart(); // go to start of text buffer + void GotoTextEnd(); // go to end of text buffer + + // configuration + // sets visibility of scrollbar + void SetVerticalScrollbar(bool state); + // sets limit of number of characters insertable into field; set to -1 to remove maximum + // only works with if rich-edit is NOT enabled + void SetMaximumCharCount(int maxChars); + + // rich edit commands + void InsertColorChange(Color col); + // IndentChange doesn't take effect until the next newline character + void InsertIndentChange(int pixelsIndent); + // clickable text + // notification that text was clicked is through "TextClicked" message + void InsertClickableTextStart( const char *pchClickAction = NULL ); + void InsertClickableTextEnd(); + // inserts a string that needs to be scanned for urls/mailto commands to be made clickable + void InsertPossibleURLString(const char *text, Color URLTextColor, Color normalTextColor); + + void InsertFade( float flSustain, float flLength ); + + void ResetAllFades( bool bHold, bool bOnlyExpired = false, float flNewSustain = -1.0f ); + + // sets the height of the window so all text is visible. + // used by tooltips + void SetToFullHeight(); + int GetNumLines(); + + /* CUSTOM MESSAGE HANDLING + "SetText" + input: "text" - text is set to be this string + */ + + /* MESSAGE SENDING (to action signal targets) + "TextChanged" - sent when the text is edited by the user + + + "TextClicked" - sent when clickable text has been clicked on + "text" - the text that was clicked on + */ + + virtual bool RequestInfo(KeyValues *outputData); + /* INFO HANDLING + "GetText" + returns: + "text" - text contained in the text box + */ + virtual void SetFgColor( Color color ); + virtual void SetDrawOffsets( int ofsx, int ofsy ); + bool IsScrollbarVisible(); + ScrollBar* GetScrollBar() { return _vertScrollBar; } + + void SetUnderlineFont( HFont font ); + + bool IsAllTextAlphaZero() const; + bool HasText() const; + + void SetDrawTextOnly(); + +protected: + virtual void OnThink(); + virtual void PerformLayout(); // layout the text in the window + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void Paint(); + + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void GetSettings( KeyValues *outResourceData ); + virtual const char *GetDescription( void ); + MESSAGE_FUNC_WCHARPTR( OnSetText, "SetText", text ); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); // respond to scroll bar events + virtual void OnKillFocus(); + virtual void OnMouseWheeled(int delta); // respond to mouse wheel events + virtual void OnKeyCodeTyped(KeyCode code); //respond to keyboard events + + MESSAGE_FUNC_INT( OnClickPanel, "ClickPanel", index); + + virtual void OnCursorMoved(int x, int y); // respond to moving the cursor with mouse button down + virtual void OnMousePressed(MouseCode code); // respond to mouse down events + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); // respond to mouse up events + + virtual void OnMouseFocusTicked(); // do while window has mouse focus + virtual void OnCursorEntered(); // handle cursor entering window + virtual void OnCursorExited(); // handle cursor exiting window + + virtual void OnMouseCaptureLost(); + virtual void OnSizeChanged(int newWide, int newTall); + virtual void OnSetFocus(); + + // clickable url handling + int ParseTextStringForUrls(const char *text, int startPos, char *pchURLText, int cchURLText, char *pchURL, int cchURL, bool &clickable); + virtual void OnTextClicked(const wchar_t *text); + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, char *pchName ); +#endif // DBGFLAG_VALIDATE + +protected: + ScrollBar *_vertScrollBar; // the scroll bar used in the window + +private: + const wchar_t *ResolveLocalizedTextAndVariables( char const *pchLookup, wchar_t *outbuf, size_t outbufsizeinbytes ); + void CheckRecalcLineBreaks(); + + void GotoWordRight(); // move cursor to start of next word + void GotoWordLeft(); // move cursor to start of prev word + + void TruncateTextStream(); + bool GetSelectedRange(int& cx0,int& cx1); + void CursorToPixelSpace(int cursorPos, int &cx, int &cy); + int PixelToCursorSpace(int cx, int cy); + void AddAnotherLine(int &cx, int &cy); + void RecalculateDefaultState(int startIndex); + + void LayoutVerticalScrollBarSlider(); + void OpenEditMenu(); + void FinishingURL(int x, int y); + // Returns the character index the drawing should Start at + int GetStartDrawIndex(int &lineBreakIndexIndex); + int GetCursorLine(); + int GetClickableTextIndexStart(int startIndex); + void CreateEditMenu(); // create copy/cut/paste menu + + MESSAGE_FUNC_INT( MoveScrollBar, "MoveScrollBar", delta ); + MESSAGE_FUNC_INT( MoveScrollBarDirect, "MoveScrollBarDirect", delta ); + + // linebreak stream functions + void InvalidateLineBreakStream(); + void RecalculateLineBreaks(); + + struct TFade + { + float flFadeStartTime; + float flFadeLength; + float flFadeSustain; + int iOriginalAlpha; + }; + + // format stream - describes changes in formatting for the text stream + struct TFormatStream + { + // render state + Color color; + int pixelsIndent; + bool textClickable; + CUtlSymbol m_sClickableTextAction; + + TFade fade; + + // position in TextStream that these changes take effect + int textStreamIndex; + }; + + bool m_bResetFades; + bool m_bInteractive; + bool m_bUnusedScrollbarInvis; + bool m_bAllTextAlphaIsZero; + + // data + CUtlVector m_TextStream; // the text in the text window is stored in this buffer + CUtlVector m_LineBreaks; // an array that holds the index in the buffer to wrap lines at + CUtlVector m_FormatStream; // list of format changes + + bool m_bRecalcLineBreaks; + + int _recalculateBreaksIndex; // tells next linebreakindex index to Start recalculating line breaks + bool _invalidateVerticalScrollbarSlider; + int _cursorPos; // the position in the text buffer of the blinking cursor + bool _mouseSelection; // whether we are highlighting text or not (selecting text) + bool _mouseDragSelection; // tells weather mouse is outside window and button is down so we select text + int _select[2]; // select[1] is the offset in the text to where the cursor is currently + // select[0] is the offset to where the cursor was dragged to. or -1 if no drag. + int _pixelsIndent; + int _maxCharCount; // max number of chars that can be in the text buffer + HFont _font; // font of chars in the text buffer + HFont m_hFontUnderline; + Color _selectionColor; + Color _selectionTextColor; // color of the highlighted text + bool _currentTextClickable; + CUtlVector _clickableTextPanels; + int _clickableTextIndex; + Color _defaultTextColor; + int _drawOffsetX; + int _drawOffsetY; + + Panel *m_pInterior; + + + // sub-controls + Menu *m_pEditMenu; // cut/copy/paste popup + + char *m_pszInitialText; // initial text + + // saved state + bool _recalcSavedRenderState; + + struct TRenderState + { + // rendering positions + int x, y; + + // basic state + Color textColor; + int pixelsIndent; + bool textClickable; + + // index into our current position in the formatting stream + int formatStreamIndex; + }; + TRenderState m_CachedRenderState; // cached render state for the beginning of painting + + // updates a render state based on the formatting and color streams + // returns true if any state changed + bool UpdateRenderState(int textStreamPos, TRenderState &renderState); + void CalculateFade( TRenderState &renderState ); + + void GenerateRenderStateForTextStreamIndex(int textStreamIndex, TRenderState &renderState); + int FindFormatStreamIndexForTextStreamPos(int textStreamIndex); + + // draws a string of characters with the same formatting using the current render state + int DrawString(int iFirst, int iLast, TRenderState &renderState, HFont font); +}; + +} // namespace vgui + + +#endif // RICHTEXT_H diff --git a/public/vgui_controls/RotatingProgressBar.h b/public/vgui_controls/RotatingProgressBar.h new file mode 100644 index 0000000..5e38818 --- /dev/null +++ b/public/vgui_controls/RotatingProgressBar.h @@ -0,0 +1,67 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef ROTATINGPROGRESSBAR_H +#define ROTATINGPROGRESSBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + + //----------------------------------------------------------------------------- + // Purpose: Progress Bar that rotates an image around its center + //----------------------------------------------------------------------------- + class RotatingProgressBar : public ProgressBar + { + DECLARE_CLASS_SIMPLE( RotatingProgressBar, ProgressBar ); + + public: + RotatingProgressBar(Panel *parent, const char *panelName); + ~RotatingProgressBar(); + + virtual void ApplySettings(KeyValues *inResourceData); + virtual void ApplySchemeSettings(IScheme *pScheme); + + void SetImage( const char *imageName ); + + protected: + virtual void Paint(); + virtual void PaintBackground(); + virtual void OnTick(); + + private: + int m_nTextureId; + char *m_pszImageName; + + float m_flStartRadians; + float m_flEndRadians; + + float m_flLastAngle; + + float m_flTickDelay; + float m_flApproachSpeed; + + float m_flRotOriginX; + float m_flRotOriginY; + + float m_flRotatingX; + float m_flRotatingY; + float m_flRotatingWide; + float m_flRotatingTall; + + }; + +} // namespace vgui + +#endif // ROTATINGPROGRESSBAR_H \ No newline at end of file diff --git a/public/vgui_controls/ScalableImagePanel.h b/public/vgui_controls/ScalableImagePanel.h new file mode 100644 index 0000000..7c822bc --- /dev/null +++ b/public/vgui_controls/ScalableImagePanel.h @@ -0,0 +1,54 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCALABLEIMAGEPANEL_H +#define SCALABLEIMAGEPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + //----------------------------------------------------------------------------- + // Purpose: 9-way Segmented background + //----------------------------------------------------------------------------- + class ScalableImagePanel : public Panel + { + DECLARE_CLASS_SIMPLE( ScalableImagePanel, Panel ); + public: + ScalableImagePanel(Panel *parent, const char *name); + + virtual void SetImage(const char *imageName); + + protected: + virtual void PaintBackground(); + virtual void GetSettings(KeyValues *outResourceData); + virtual void ApplySettings(KeyValues *inResourceData); + virtual void PerformLayout( void ); + virtual const char *GetDescription(); + + private: + int m_iSrcCornerHeight; // in pixels, how tall is the corner inside the image + int m_iSrcCornerWidth; // same for width + int m_iCornerHeight; // output size of the corner height in pixels + int m_iCornerWidth; // same for width + + int m_iTextureID; + + float m_flCornerWidthPercent; // corner width as percentage of image width + float m_flCornerHeightPercent; // same for height + + char *m_pszImageName; + }; + +} // namespace vgui + +#endif // SCALABLEIMAGEPANEL_H diff --git a/public/vgui_controls/ScrollBar.h b/public/vgui_controls/ScrollBar.h new file mode 100644 index 0000000..f428388 --- /dev/null +++ b/public/vgui_controls/ScrollBar.h @@ -0,0 +1,131 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SCROLLBAR_H +#define SCROLLBAR_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class Button; +class ScrollBarSlider; + +//----------------------------------------------------------------------------- +// Purpose: Generic scrollbar +// Uses Buttons & SliderBars for the main functionality +//----------------------------------------------------------------------------- +class ScrollBar : public Panel +{ + DECLARE_CLASS_SIMPLE( ScrollBar, Panel ); + +public: + ScrollBar(Panel *parent, const char *panelName, bool vertical); + + // Set the value of the scroll bar slider. + virtual void SetValue(int value); + + // Get the value of the scroll bar slider. + virtual int GetValue(); + + // Set the rangeof numbers the slider can scroll through + virtual void SetRange(int min,int max); + + virtual void GetRange(int &min, int &max); + + // Set how many lines are displayed at one time + // in the window the scroll bar is attached to. + virtual void SetRangeWindow(int rangeWindow); + + // Get how many lines are displayed at one time + // in the window the scroll bar is attached to. + virtual int GetRangeWindow(); + + // Check if the scrollbar is vertical or horizontal + virtual bool IsVertical(); + + // Purpose: Check if the slider can move through one or more pixels per + // unit of its range. + virtual bool HasFullRange(); + + // Setup the indexed scroll bar button with the input params. + virtual void SetButton(Button* button,int index); + // Return the indexed scroll bar button + virtual Button *GetButton(int index); + // Set up the slider. + virtual void SetSlider(ScrollBarSlider* slider); + // Return a pointer to the slider. + virtual ScrollBarSlider *GetSlider(); + // Set how far the scroll bar slider moves + // when a scroll bar button is pressed + virtual void SetButtonPressedScrollValue(int value); + + virtual void Validate(); + + // Update and look for clicks when mouse is in the scroll bar window. + virtual void OnMouseFocusTicked(); + + // Set the slider's Paint border enabled. + virtual void SetPaintBorderEnabled(bool state); + // Set the slider's Paint background enabled. + virtual void SetPaintBackgroundEnabled(bool state); + // Set the slider's Paint enabled. + virtual void SetPaintEnabled(bool state); + + // Sets the scrollbar buttons visible or not + virtual void SetScrollbarButtonsVisible(bool visible); + + void SetAutohideButtons( bool bAutohide ) { m_bAutoHideButtons = bAutohide; } + + void UseImages( const char *pszUpArrow, const char *pszDownArrow, const char *pszLine, const char *pszBox ); + + /* MESSAGES SENT: + "ScrollBarSliderMoved" + "position" - new value of the slider + "ScrollBarSliderReleased" + "position" - final position of slider + */ + +protected: + + virtual void PerformLayout(); + virtual void SendSliderMoveMessage(int value); + virtual void SendScrollBarSliderReleasedMessage(int value); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void OnSizeChanged(int wide, int tall); + + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); + MESSAGE_FUNC_INT( OnSliderReleased, "ScrollBarSliderReleased", position ); + + virtual void RespondToScrollArrow(int const direction); + + virtual void UpdateButtonsForImages( void ); + virtual void UpdateSliderImages( void ); + +private: + Button* _button[2]; + ScrollBarSlider* _slider; + int _buttonPressedScrollValue; + int _scrollDelay; // used to control delays in scrolling + bool _respond; + CPanelAnimationVar( bool, m_bAutoHideButtons, "autohide_buttons", "0" ); + + vgui::ImagePanel *m_pUpArrow; + vgui::ImagePanel *m_pLine; + vgui::ImagePanel *m_pDownArrow; + vgui::ImagePanel *m_pBox; +}; + +} + +#endif // SCROLLBAR_H diff --git a/public/vgui_controls/ScrollBarSlider.h b/public/vgui_controls/ScrollBarSlider.h new file mode 100644 index 0000000..b9dd24f --- /dev/null +++ b/public/vgui_controls/ScrollBarSlider.h @@ -0,0 +1,113 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef SCROLLBARSLIDER_H +#define SCROLLBARSLIDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class IBorder; + +//----------------------------------------------------------------------------- +// Purpose: ScrollBarSlider bar, as used in ScrollBar's +//----------------------------------------------------------------------------- +class ScrollBarSlider : public Panel +{ + DECLARE_CLASS_SIMPLE( ScrollBarSlider, Panel ); + +public: + + enum SliderBorderType_t + { + Slider_Idle = 0, + Slider_Hover, + Slider_Dragging, + + Slider_Count, + }; + + ScrollBarSlider(Panel *parent, const char *panelName, bool vertical); + + // Set the ScrollBarSlider value of the nob. + virtual void SetValue(int value); + virtual int GetValue(); + + // Check whether the scroll bar is vertical or not + virtual bool IsVertical(); + + // Set max and min range of lines to display + virtual void SetRange(int min, int max); + + virtual void GetRange(int &min, int &max); + + // Set number of rows that can be displayed in window + virtual void SetRangeWindow(int rangeWindow); + + // Get number of rows that can be displayed in window + virtual int GetRangeWindow(); + + // Set the size of the ScrollBarSlider nob + virtual void SetSize(int wide, int tall); + + // Get current ScrollBarSlider bounds + virtual void GetNobPos(int &min, int &max); + + virtual bool HasFullRange(); + virtual void SetButtonOffset(int buttonOffset); + virtual void OnCursorMoved(int x, int y); + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + + // Return true if this slider is actually drawing itself + virtual bool IsSliderVisible( void ); + virtual bool IsMouseOverNob(); + +protected: + virtual void Paint(); + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + + virtual void OnCursorEntered(); + virtual void OnCursorExited(); + +private: + virtual void RecomputeNobPosFromValue(); + virtual void RecomputeValueFromNobPos(); + virtual void SendScrollBarSliderMovedMessage(); + virtual void SendScrollBarSliderReleasedMessage(); + void SetNobFocusColor( const Color &color ); + void SetNobDragColor( const Color &color ); + + bool _vertical; + bool _dragging; + bool m_bCursorOver; + int _nobPos[2]; + int _nobDragStartPos[2]; + int _dragStartPos[2]; + int _range[2]; + int _value; // the position of the ScrollBarSlider, in coordinates as specified by SetRange/SetRangeWindow + int _rangeWindow; + int _buttonOffset; + int m_nNobInset; + IBorder *_ScrollBarSliderBorder[ Slider_Count ]; + Color m_NobFocusColor; + Color m_NobDragColor; +}; + +} // namespace vgui + +#endif // SCROLLBARSLIDER_H diff --git a/public/vgui_controls/ScrollableEditablePanel.h b/public/vgui_controls/ScrollableEditablePanel.h new file mode 100644 index 0000000..0782671 --- /dev/null +++ b/public/vgui_controls/ScrollableEditablePanel.h @@ -0,0 +1,54 @@ +//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef SCROLLABLEEDITABLEPANEL_H +#define SCROLLABLEEDITABLEPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +namespace vgui +{ + class ScrollBar; +} + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// An editable panel that has a scrollbar +//----------------------------------------------------------------------------- +class ScrollableEditablePanel : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( ScrollableEditablePanel, vgui::EditablePanel ); + +public: + ScrollableEditablePanel( vgui::Panel *pParent, vgui::EditablePanel *pChild, const char *pName ); + virtual ~ScrollableEditablePanel() {} + virtual void PerformLayout(); + + vgui::ScrollBar *GetScrollbar( void ) { return m_pScrollBar; } + + virtual void OnMouseWheeled(int delta); + + MESSAGE_FUNC( OnScrollBarSliderMoved, "ScrollBarSliderMoved" ); + +private: + vgui::ScrollBar *m_pScrollBar; + vgui::EditablePanel *m_pChild; +}; + + +} // end namespace vgui + +#endif // SCROLLABLEEDITABLEPANEL_H \ No newline at end of file diff --git a/public/vgui_controls/SectionedListPanel.h b/public/vgui_controls/SectionedListPanel.h new file mode 100644 index 0000000..6d3024f --- /dev/null +++ b/public/vgui_controls/SectionedListPanel.h @@ -0,0 +1,245 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SECTIONEDLISTPANEL_H +#define SECTIONEDLISTPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace vgui +{ + +class CSectionHeader; +class CItemButton; + +// sorting function, should return true if itemID1 should be displayed before itemID2 +typedef bool (*SectionSortFunc_t)(SectionedListPanel *list, int itemID1, int itemID2); + +//----------------------------------------------------------------------------- +// Purpose: List panel control that is divided up into discrete sections +//----------------------------------------------------------------------------- +class SectionedListPanel : public Panel +{ + DECLARE_CLASS_SIMPLE( SectionedListPanel, Panel ); + +public: + SectionedListPanel(vgui::Panel *parent, const char *name); + ~SectionedListPanel(); + + // adds a new section; returns false if section already exists + virtual void AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc = NULL); + virtual void AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc = NULL); + + // clears all the sections - leaves the items in place + virtual void RemoveAllSections(); + + // modifies section info + virtual void SetSectionFgColor(int sectionID, Color color); + virtual void SetSectionDividerColor( int sectionID, Color color); + // forces a section to always be visible + virtual void SetSectionAlwaysVisible(int sectionID, bool visible = true); + + // adds a new column to a section + enum ColumnFlags_e + { + HEADER_IMAGE = 0x01, // set if the header for the column is an image instead of text + COLUMN_IMAGE = 0x02, // set if the column contains an image instead of text (images are looked up by index from the ImageList) (see SetImageList below) + COLUMN_BRIGHT = 0x04, // set if the column text should be the bright color + COLUMN_CENTER = 0x08, // set to center the text/image in the column + COLUMN_RIGHT = 0x10, // set to right-align the text in the column + }; + virtual bool AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont = INVALID_FONT ); + virtual bool AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont = INVALID_FONT ); + + // modifies the text in an existing column + virtual bool ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText); + + // adds an item to the list; returns the itemID of the new item + virtual int AddItem(int sectionID, const KeyValues *data); + + // modifies an existing item; returns false if the item does not exist + virtual bool ModifyItem(int itemID, int sectionID, const KeyValues *data); + + // removes an item from the list; returns false if the item does not exist or is already removed + virtual bool RemoveItem(int itemID); + + // clears the list + virtual void RemoveAll() { DeleteAllItems(); } + // DeleteAllItems() is deprecated, use RemoveAll(); + virtual void DeleteAllItems(); + + // set the text color of an item + virtual void SetItemFgColor(int itemID, Color color); + virtual void SetItemFont( int itemID, HFont font ); + + /* MESSAGES SENT: + "RowSelected" + "itemID" - the selected item id, -1 if nothing selected + + // when an item has been clicked on + "RowContextMenu" "itemID" + "RowLeftClick" "itemID" + "RowDoubleLeftClick" "itemID" + */ + + // returns the number of columns in a section + virtual int GetColumnCountBySection(int sectionID); + + // returns the name of a column by section and column index; returns NULL if there are no more columns + // valid range of columnIndex is [0, GetColumnCountBySection) + virtual const char *GetColumnNameBySection(int sectionID, int columnIndex); + virtual const wchar_t *GetColumnTextBySection(int sectionID, int columnIndex); + virtual int GetColumnFlagsBySection(int sectionID, int columnIndex); + virtual int GetColumnWidthBySection(int sectionID, int columnIndex); + virtual HFont GetColumnFallbackFontBySection( int sectionID, int columnIndex ); + + // returns the id of the currently selected item, -1 if nothing is selected + virtual int GetSelectedItem(); + + // sets which item is currently selected + virtual void SetSelectedItem(int itemID); + + // remove selection + virtual void ClearSelection( void ); + + // returns the data of a selected item + // InvalidateItem(itemID) needs to be called if the KeyValues are modified + virtual KeyValues *GetItemData(int itemID); + + // returns what section an item is in + virtual int GetItemSection(int itemID); + + // forces an item to redraw (use when keyvalues have been modified) + virtual void InvalidateItem(int itemID); + + // returns true if the itemID is valid for use + virtual bool IsItemIDValid(int itemID); + virtual int GetHighestItemID(); + + // returns the number of items (ignoring section dividers) + virtual int GetItemCount(); + + // returns the item ID from the row, again ignoring section dividers - valid from [0, GetItemCount ) + virtual int GetItemIDFromRow(int row); + + // returns the row that this itemID occupies. -1 if the itemID is invalid + virtual int GetRowFromItemID(int itemID); + + // gets the local coordinates of a cell + virtual bool GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall); + + // set up a field for editing + virtual void EnterEditMode(int itemID, int column, vgui::Panel *editPanel); + + // leaves editing mode + virtual void LeaveEditMode(); + + // returns true if we are currently in inline editing mode + virtual bool IsInEditMode(); + + // sets whether or not the vertical scrollbar should ever be displayed + virtual void SetVerticalScrollbar(bool state); + + // returns the size required to fully draw the contents of the panel + virtual void GetContentSize(int &wide, int &tall); + + // image handling + virtual void SetImageList(ImageList *imageList, bool deleteImageListWhenDone); + + virtual void ScrollToItem(int iItem); + + virtual void SetProportional(bool state); + + HFont GetHeaderFont( void ) const; + void SetHeaderFont( HFont hFont ); + HFont GetRowFont( void ) const; + void SetRowFont( HFont hFont ); + void MoveSelectionDown( void ); + void MoveSelectionUp( void ); + + ScrollBar* GetScrollBar() { return m_pScrollBar; } + +protected: + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void ApplySettings(KeyValues *inResourceData); + virtual void OnSizeChanged(int wide, int tall); + virtual void OnMouseWheeled(int delta); + virtual void OnMousePressed( MouseCode code); + virtual void OnKeyCodeTyped( KeyCode code); + virtual void OnSetFocus(); // called after the panel receives the keyboard focus + +public: + virtual void SetFontSection(int sectionID, HFont font); +private: + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); + + void AddSectionHelper(int sectionID, CSectionHeader *header, SectionSortFunc_t sortFunc); + int GetSectionTall(); + void LayoutPanels(int &contentTall); + + // Returns the index of a new item button, reusing an existing item button if possible + int GetNewItemButton(); + + friend class CItemButton; + void SetSelectedItem(CItemButton *item); + DHANDLE m_hSelectedItem; + + struct column_t + { + char m_szColumnName[32]; + wchar_t m_szColumnText[64]; + int m_iColumnFlags; + int m_iWidth; + HFont m_hFallbackFont; + }; + struct section_t + { + int m_iID; + bool m_bAlwaysVisible; + CSectionHeader *m_pHeader; + CUtlVector m_Columns; + SectionSortFunc_t m_pSortFunc; + }; + + CUtlVector m_Sections; + CUtlLinkedList m_Items; + CUtlLinkedList m_FreeItems; + CUtlVector m_SortedItems; + + PHandle m_hEditModePanel; + int m_iEditModeItemID; + int m_iEditModeColumn; + int m_iContentHeight; + int m_iLineSpacing; + + int FindSectionIndexByID(int sectionID); + void ReSortList(); + + ScrollBar *m_pScrollBar; + ImageList *m_pImageList; + bool m_bDeleteImageListWhenDone; + bool m_bSortNeeded; + bool m_bVerticalScrollbarEnabled; + + HFont m_hHeaderFont; + HFont m_hRowFont; + + CPanelAnimationVar( bool, m_bShowColumns, "show_columns", "false" ); +}; + +} // namespace vgui + +#endif // SECTIONEDLISTPANEL_H diff --git a/public/vgui_controls/Slider.h b/public/vgui_controls/Slider.h new file mode 100644 index 0000000..b2d28eb --- /dev/null +++ b/public/vgui_controls/Slider.h @@ -0,0 +1,121 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef SLIDER_H +#define SLIDER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Labeled horizontal slider +//----------------------------------------------------------------------------- +class Slider : public Panel +{ + DECLARE_CLASS_SIMPLE( Slider, Panel ); +public: + Slider(Panel *parent, const char *panelName); + + // interface + virtual void SetValue(int value, bool bTriggerChangeMessage = true); + virtual int GetValue(); + virtual void SetRange(int min, int max); // set to max and min range of rows to display + virtual void GetRange(int &min, int &max); + virtual void GetNobPos(int &min, int &max); // get current Slider position + virtual void SetButtonOffset(int buttonOffset); + virtual void OnCursorMoved(int x, int y); + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + virtual void OnMouseReleased(MouseCode code); + virtual void SetTickCaptions(const wchar_t *left, const wchar_t *right); + virtual void SetTickCaptions(const char *left, const char *right); + virtual void SetNumTicks(int ticks); + virtual void SetThumbWidth( int width ); + virtual int EstimateValueAtPos( int localMouseX, int localMouseY ); + virtual void SetInverted( bool bInverted ); + + // If you click on the slider outside of the nob, the nob jumps + // to the click position, and if this setting is enabled, the nob + // is then draggable from the new position until the mouse is released + virtual void SetDragOnRepositionNob( bool state ); + virtual bool IsDragOnRepositionNob() const; + + // Get if the slider nob is being dragged by user, usually the application + // should refuse from forcefully setting slider value if it is being dragged + // by user since the next frame the nob will pop back to mouse position + virtual bool IsDragged( void ) const; + + // This allows the slider to behave like it's larger than what's actually being drawn + virtual void SetSliderThumbSubRange( bool bEnable, int nMin = 0, int nMax = 100 ); + +protected: + virtual void OnSizeChanged(int wide, int tall); + virtual void Paint(); + virtual void PaintBackground(); + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void GetSettings(KeyValues *outResourceData); + virtual void ApplySettings(KeyValues *inResourceData); + virtual const char *GetDescription(); +#ifdef _X360 + virtual void OnKeyCodePressed(KeyCode code); +#endif + virtual void OnKeyCodeTyped(KeyCode code); + + virtual void DrawNob(); + virtual void DrawTicks(); + virtual void DrawTickLabels(); + + virtual void GetTrackRect( int &x, int &y, int &w, int &h ); + +protected: + virtual void RecomputeNobPosFromValue(); + virtual void RecomputeValueFromNobPos(); + + virtual void SendSliderMovedMessage(); + virtual void SendSliderDragStartMessage(); + virtual void SendSliderDragEndMessage(); + + bool _dragging; + int _nobPos[2]; + int _nobDragStartPos[2]; + int _dragStartPos[2]; + int _range[2]; + int _subrange[ 2 ]; + int _value; // the position of the Slider, in coordinates as specified by SetRange/SetRangeWindow + int _buttonOffset; + IBorder *_sliderBorder; + IBorder *_insetBorder; + float _nobSize; + + TextImage *_leftCaption; + TextImage *_rightCaption; + + Color m_TickColor; + Color m_TrackColor; + Color m_DisabledTextColor1; + Color m_DisabledTextColor2; +#ifdef _X360 + Color m_DepressedBgColor; +#endif + + int m_nNumTicks; + bool m_bIsDragOnRepositionNob : 1; + bool m_bUseSubRange : 1; + bool m_bInverted : 1; +}; + +} + +#endif // SLIDER_H diff --git a/public/vgui_controls/Splitter.h b/public/vgui_controls/Splitter.h new file mode 100644 index 0000000..dc9a45a --- /dev/null +++ b/public/vgui_controls/Splitter.h @@ -0,0 +1,98 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef SPLITTER_H +#define SPLITTER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +enum SplitterMode_t +{ + SPLITTER_MODE_HORIZONTAL = 0, + SPLITTER_MODE_VERTICAL +}; + + +class SplitterHandle; +class SplitterChildPanel; + +//----------------------------------------------------------------------------- +// Purpose: Thin line used to divide sections, can be moved dragged! +//----------------------------------------------------------------------------- +class Splitter : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( Splitter, EditablePanel ); + +public: + // nCount is the number of splitters to create. + // NOTE: The constructor here will create (nCount+1) EditablePanel children + // and name them child0...childN for .res file purposes. + Splitter( Panel *parent, const char *name, SplitterMode_t mode, int nCount ); + ~Splitter(); + + // Evenly respace all splitters + void EvenlyRespaceSplitters(); + + // respace splitters using given fractions (must sum to 1) + void RespaceSplitters( float *flFractions ); + + // Inherited from Panel + virtual void ApplySettings(KeyValues *inResourceData); + virtual void GetSettings( KeyValues *outResourceData ); + virtual void PerformLayout(); + virtual void OnSizeChanged(int newWide, int newTall); + virtual void ApplyUserConfigSettings(KeyValues *userConfig); + virtual void GetUserConfigSettings(KeyValues *userConfig); + virtual bool HasUserConfigSettings() { return true; } + + // Sets the splitter color + void SetSplitterColor( Color c ); + + // Enables borders on the splitters + void EnableBorders( bool bEnable ); + + // Locks the size of a particular child in pixels. + void LockChildSize( int nChildIndex, int nSize ); + void UnlockChildSize( int nChildIndex ); + +private: + void RecreateSplitters( int nCount ); + + struct SplitterInfo_t + { + SplitterChildPanel *m_pPanel; // This panel is to the left or above the handle + SplitterHandle *m_pHandle; + float m_flPos; + bool m_bLocked; + int m_nLockedSize; + }; + + int GetPosRange(); + int GetSplitterCount() const; + int GetSplitterPosition( int nIndex ); + void SetSplitterPosition( int nIndex, int nPos ); + int GetSubPanelCount() const; + int ComputeLockedSize( int nStartingIndex ); + + CUtlVector< SplitterInfo_t > m_Splitters; + SplitterMode_t m_Mode; + + friend class SplitterHandle; +}; + + +} // namespace vgui + + +#endif // SPLITTER_H diff --git a/public/vgui_controls/TextEntry.h b/public/vgui_controls/TextEntry.h new file mode 100644 index 0000000..d75cc42 --- /dev/null +++ b/public/vgui_controls/TextEntry.h @@ -0,0 +1,405 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A Class to create a window that you can type and edit text in. +// Window can hold single line or multiline text. +// If it is single it can scroll horizontally in response to +// key input and mouse selection. +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TEXTENTRY_H +#define TEXTENTRY_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include +#include + +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Text-input handler +// Behaviour Specs: +// This class handles input from mouse and keyboard. +// TextEntry classes support several box styles, horizontal scrolling with no scrollbar +// vertical scrolling with or without a scrollbar, single line, multiline, +// editable and noneditable. +// +// Shared behaviour: +// URL's are a different text color and are clickable. Clicking them brings up a web browser. +// For vertical scroll bars, up and down arrows scroll one line at a time. +// Clicking and dragging the nob scrolls through text lines. +// Mouse wheel also moves the nob. +// User can select and highlight text in the window. +// Double clicking on a word selects it. +// +// Non editable: +// No blinking cursor in non editable windows. +// Right clicking mouse opens copy menu. Menu's top left corner is where mouse is. +// Ctrl-c will also copy the text. +// Editable: +// Blinking cursor is positioned where text will be inserted. +// Text keys type chars in the window. +// ctrl-c copy highlighted text +// ctrl-v paste highlighted text +// ctrl-x cut highlighted text +// ctrl-right arrow move cursor to the start of the next word +// ctrl-left arrow move cursor to the start of the prev word +// ctrl-enter delete the selected text (and inserts a newline if _catchEnterKey is true) +// insert delete selected text and pastes text from the clipboard +// delete delete the selected text +// ctrl-home move cursor to the start of the text +// ctrl-end move cursor to the end of the text. +// left arrow move cursor before prev char +// ctrl-shift left/right arrow selects text a word at a time +// right arrow move cursor before next char +// up arrow move cursor up one line. +// down arrow move cursor down one line. +// home move cursor to start of current line +// end move cursor to end of current line +// backspace delete prev char or selected text. +// Trying to move to the prev/next char/line/word when there is none moves the cursor to the +// start/end of the text. +// Horizontal scrolling: +// Trying to move to the prev/next char/line/word when there is none scrolls the text +// horizontally in the window so the new text displays at the correct side. +// When moving to prev chars scrolling is staggered. To next chars it is one char at a time. +// Cut/Copy/Paste Menu: +// Right clicking mouse brings up cut/copy/paste menu. +// If no text is highlighted the cut/copy options are dimmed. Cut is dimmed in non editable panels +// If there is no text in the clipboard or panel is not editable the paste option is dimmed. +// If the mouse is right clicked over selected text, the text stays selected. +// If the mouse is right clicked over unselected text, any selected text is deselected. +// +// +//----------------------------------------------------------------------------- +class TextEntry : public Panel +{ + DECLARE_CLASS_SIMPLE( TextEntry, Panel ); +public: + TextEntry(Panel *parent, const char *panelName); + virtual ~TextEntry(); + + virtual void SetText(const wchar_t *wszText); + virtual void SetText(const char *text); + virtual void GetText(char *buf, int bufLen); + virtual void GetText(wchar_t *buf, int bufLen); + virtual int GetTextLength() const; + virtual bool IsTextFullySelected() const; + + // editing + virtual void GotoLeft(); // move cursor one char left + virtual void GotoRight(); // move cursor one char right + virtual void GotoUp(); // move cursor one line up + virtual void GotoDown(); // move cursor one line down + virtual void GotoWordRight(); // move cursor to Start of next word + virtual void GotoWordLeft(); // move cursor to Start of prev word + virtual void GotoFirstOfLine(); // go to Start of the current line + virtual void GotoEndOfLine(); // go to end of the current line + virtual void GotoTextStart(); // go to Start of text buffer + virtual void GotoTextEnd(); // go to end of text buffer + virtual int GetTextCursorPos() { return _cursorPos; } + + virtual void InsertChar(wchar_t ch); + virtual void InsertString(const char *text); + virtual void InsertString(wchar_t *wszText); + virtual void Backspace(); + virtual void Delete(); + virtual void SelectNone(); + virtual void OpenEditMenu(); + MESSAGE_FUNC( CutSelected, "DoCutSelected" ); + MESSAGE_FUNC( CopySelected, "DoCopySelected" ); + MESSAGE_FUNC( Paste, "DoPaste" ); + + MESSAGE_FUNC_INT( LanguageChanged, "DoLanguageChanged", handle ); + MESSAGE_FUNC_INT( ConversionModeChanged, "DoConversionModeChanged", handle ); + MESSAGE_FUNC_INT( SentenceModeChanged, "DoSentenceModeChanged", handle ); + + MESSAGE_FUNC_WCHARPTR( CompositionString, "DoCompositionString", string ); + + MESSAGE_FUNC( ShowIMECandidates, "DoShowIMECandidates" ); + MESSAGE_FUNC( HideIMECandidates, "DoHideIMECandidates" ); + MESSAGE_FUNC( UpdateIMECandidates, "DoUpdateIMECandidates" ); + + virtual void DeleteSelected(); + virtual void Undo(); + virtual void SaveUndoState(); + virtual void SetFont(HFont font); + virtual void SetTextHidden(bool bHideText); + virtual void SetEditable(bool state); + virtual bool IsEditable(); + virtual void SetEnabled(bool state); + // move the cursor to line 'line', given how many pixels are in a line + virtual void MoveCursor(int line, int pixelsAcross); + + // sets the color of the background when the control is disabled + virtual void SetDisabledBgColor(Color col); + + // set whether the box handles more than one line of entry + virtual void SetMultiline(bool state); + virtual bool IsMultiline(); + + // sets visibility of scrollbar + virtual void SetVerticalScrollbar(bool state); + + // sets whether or not the edit catches and stores ENTER key presses + virtual void SetCatchEnterKey(bool state); + + // sets whether or not to send "TextNewLine" msgs when ENTER key is pressed + virtual void SendNewLine(bool send); + + // sets whether or not the edit catches and stores TAB key presses + virtual void SetCatchTabKey(bool state); + + // set the number of spaces inserted for a tab key press when catch tab is enabled + virtual void SetTabSpaces(int count); + + // sets limit of number of characters insertable into field; set to -1 to remove maximum + // only works with if rich-edit is NOT enabled + virtual void SetMaximumCharCount(int maxChars); + virtual int GetMaximumCharCount(); + virtual void SetAutoProgressOnHittingCharLimit(bool state); + + // sets whether to wrap text once maxChars is reached (on a line by line basis) + virtual void SetWrap(bool wrap); + + virtual void RecalculateLineBreaks(); + virtual void LayoutVerticalScrollBarSlider(); + + virtual bool RequestInfo(KeyValues *outputData); + + // sets the height of the window so all text is visible. + // used by tooltips + void SetToFullHeight(); + + // sets the width of the window so all text is visible. (will create one line) + // used by tooltips + void SetToFullWidth(); + + int GetNumLines(); + + // gets the current starting line, the first line to be displayed + int GetCurrentStartLine() const; + + /* INFO HANDLING + "GetText" + returns: + "text" - text contained in the text box + */ + + /* CUSTOM MESSAGE HANDLING + "SetText" + input: "text" - text is set to be this string + */ + + /* MESSAGE SENDING (to action signal targets) + "TextChanged" - sent when the text is edited by the user + + "TextNewLine" - sent when the end key is pressed in the text entry AND _sendNewLines is true + + "TextKillFocus" - sent when focus leaves textentry field + */ + + // Selects all the text in the text entry. + void SelectAllText(bool bResetCursorPos); + void SelectNoText(); + void SelectAllOnFirstFocus( bool status ); + void SetDrawWidth(int width); // width from right side of window we have to draw in + int GetDrawWidth(); + void SetHorizontalScrolling(bool status); // turn horizontal scrolling on or off. + + // sets whether non-asci characters (unicode chars > 127) are allowed in the control - defaults to OFF + void SetAllowNonAsciiCharacters(bool state); + + // sets whether or not number input only is allowed + void SetAllowNumericInputOnly(bool state); + + // By default, we draw the language shortname on the right hand side of the control + void SetDrawLanguageIDAtLeft( bool state ); + + virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& data ); + virtual bool IsDroppable( CUtlVector< KeyValues * >& data ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& data ); + virtual Panel *GetDragPanel(); + virtual void OnCreateDragData( KeyValues *msg ); + + void SelectAllOnFocusAlways( bool status ); + void SetSelectionTextColor( const Color& clr ); + void SetSelectionBgColor( const Color& clr ); + void SetSelectionUnfocusedBgColor( const Color& clr ); + + void SetUseFallbackFont( bool bState, HFont hFallback ); + virtual void SetAutoLocalize( bool bState ) { m_bAutoLocalize = bState; } + + virtual void GetSizerMinimumSize(int &wide, int &tall); + +protected: + virtual void ResetCursorBlink(); + virtual void PerformLayout(); // layout the text in the window + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void PaintBackground(); + virtual int DrawChar(wchar_t ch, HFont font, int index, int x, int y); + virtual bool DrawCursor(int x, int y); + + virtual void SetCharAt(wchar_t ch, int index); // set the value of a char in the text buffer + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void GetSettings( KeyValues *outResourceData ); + virtual const char *GetDescription( void ); + virtual void FireActionSignal(); + virtual bool GetSelectedRange(int& cx0,int& cx1); + virtual void CursorToPixelSpace(int cursorPos, int &cx, int &cy); + virtual int PixelToCursorSpace(int cx, int cy); + virtual void AddAnotherLine(int &cx, int &cy); + virtual int GetYStart(); // works out ypixel position drawing started at + + virtual bool SelectCheck( bool fromMouse = false ); // check if we are in text selection mode + MESSAGE_FUNC_WCHARPTR( OnSetText, "SetText", text ); + MESSAGE_FUNC( OnSliderMoved, "ScrollBarSliderMoved" ); // respond to scroll bar events + virtual void OnKillFocus(); + virtual void OnMouseWheeled(int delta); // respond to mouse wheel events + virtual void OnKeyCodeTyped(KeyCode code); //respond to keyboard events + virtual void OnKeyTyped(wchar_t unichar); //respond to keyboard events + + virtual void OnCursorMoved(int x, int y); // respond to moving the cursor with mouse button down + virtual void OnMousePressed(MouseCode code); // respond to mouse down events + virtual void OnMouseDoublePressed( MouseCode code ); + virtual void OnMouseTriplePressed( MouseCode code ); + virtual void OnMouseReleased( MouseCode code ); // respond to mouse up events + + virtual void OnKeyFocusTicked(); // do while window has keyboard focus + virtual void OnMouseFocusTicked(); // do while window has mouse focus + virtual void OnCursorEntered(); // handle cursor entering window + virtual void OnCursorExited(); // handle cursor exiting window + + virtual void OnMouseCaptureLost(); + virtual void OnSizeChanged(int newWide, int newTall); + + // Returns the character index the drawing should Start at + virtual int GetStartDrawIndex(int &lineBreakIndexIndex); + +public: + // helper accessors for common gets + virtual float GetValueAsFloat(); + virtual int GetValueAsInt(); + +protected: + void ScrollRight(); // scroll to right until cursor is visible + void ScrollLeft(); // scroll to left + bool IsCursorOffRightSideOfWindow(int cursorPos); // check if cursor is off right side of window + bool IsCursorOffLeftSideOfWindow(int cursorPos); // check if cursor is off left side of window + void ScrollLeftForResize(); + + void OnSetFocus(); + // Change keyboard layout type + void OnChangeIME( bool forward ); + + bool NeedsEllipses( HFont font, int *pIndex ); + +private: + MESSAGE_FUNC_INT( OnSetState, "SetState", state ); + // get index in buffer of the Start of the current line we are on + int GetCurrentLineStart(); + // get index in buffer of the end of the current line we are on + int GetCurrentLineEnd(); + bool IsLineBreak(int index); + int GetCursorLine(); + void MoveScrollBar(int delta); + void CalcBreakIndex(); // calculate _recalculateLineBreaksIndex + void CreateEditMenu(); // create copy/cut/paste menu + +public: + Menu *GetEditMenu(); // retrieve copy/cut/paste menu + +private: + void FlipToLastIME(); + +public: + virtual void GetTextRange( wchar_t *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination + virtual void GetTextRange( char *buf, int from, int numchars ); // copy a portion of the text to the buffer and add zero-termination + +private: + + CUtlVector m_TextStream; // the text in the text window is stored in this buffer + CUtlVector m_UndoTextStream; // a copy of the text buffer to revert changes + CUtlVector m_LineBreaks; // an array that holds the index in the buffer to wrap lines at + + int _cursorPos; // the position in the text buffer of the blinking cursor + bool _cursorIsAtEnd; + bool _putCursorAtEnd; + int _undoCursorPos; // a copy of the cursor position to revert changes + bool _cursorBlink; // whether cursor is blinking or not + bool _hideText; // whether text is visible on screen or not + bool _editable; // whether text is editable or not + bool _mouseSelection; // whether we are highlighting text or not (selecting text) + bool _mouseDragSelection; // tells weather mouse is outside window and button is down so we select text + int _mouseSelectCursorStart; // where mouse button was pressed down in text window + long _cursorNextBlinkTime; // time of next cursor blink + int _cursorBlinkRate; // speed of cursor blinking + int _select[2]; // select[1] is the offset in the text to where the cursor is currently + // select[0] is the offset to where the cursor was dragged to. or -1 if no drag. + int _pixelsIndent; + int _charCount; + int _maxCharCount; // max number of chars that can be in the text buffer + HFont _font; // font of chars in the text buffer + HFont _smallfont; + bool _dataChanged; // whether anything in the window has changed. + bool _multiline; // whether buffer is multiline or just a single line + bool _verticalScrollbar; // whether window has a vertical scroll bar + ScrollBar *_vertScrollBar; // the scroll bar used in the window + Color _cursorColor; // color of the text cursor + Color _disabledFgColor; + Color _disabledBgColor; + Color _selectionColor; + Color _selectionTextColor; // color of the highlighted text + Color _defaultSelectionBG2Color; + int _currentStartLine; // use for checking vertical text scrolling (multiline) + int _currentStartIndex; // use for horizontal text scrolling (!multiline) + bool _horizScrollingAllowed; // use to disable horizontal text scrolling period. + Color _focusEdgeColor; + bool _catchEnterKey; + bool _catchTabKey; + bool _wrap; + bool _sendNewLines; + int _drawWidth; + int _tabSpaces; // number of spaces inserted for a tab + + // selection data + Menu *m_pEditMenu; ///cut/copy/paste popup + + int _recalculateBreaksIndex; // tells next linebreakindex index to Start recalculating line breaks + bool _selectAllOnFirstFocus : 1; // highlights all text in window when focus is gained. + bool _selectAllOnFocusAlways : 1; + bool _firstFocusStatus; // keep track if we've had that first focus or not + bool m_bAllowNumericInputOnly; + bool m_bAllowNonAsciiCharacters; + bool m_bAutoProgressOnHittingCharLimit; + + enum + { + MAX_COMPOSITION_STRING = 256, + }; + + wchar_t m_szComposition[ MAX_COMPOSITION_STRING ]; + Menu *m_pIMECandidates; + int m_hPreviousIME; + bool m_bDrawLanguageIDAtLeft; + int m_nLangInset; + + bool m_bUseFallbackFont : 1; + HFont m_hFallbackFont; + + bool m_bAutoLocalize; +}; + +} + +#endif // TEXTENTRY_H diff --git a/public/vgui_controls/TextImage.h b/public/vgui_controls/TextImage.h new file mode 100644 index 0000000..c041080 --- /dev/null +++ b/public/vgui_controls/TextImage.h @@ -0,0 +1,119 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TEXTIMAGE_H +#define TEXTIMAGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +#include + +class KeyValues; + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Image that handles drawing of a text string +//----------------------------------------------------------------------------- +class TextImage : public Image +{ +public: + TextImage(const char *text); + TextImage(const wchar_t *wszText); + ~TextImage(); + +public: + // takes the string and looks it up in the localization file to convert it to unicode + virtual void SetText(const char *text); + // sets unicode text directly + virtual void SetText(const wchar_t *text, bool bClearUnlocalizedSymbol = false); + // get the full text in the image + virtual void GetText(char *buffer, int bufferSize); + virtual void GetText(wchar_t *buffer, int bufferLength); + // get the text in it's unlocalized form + virtual void GetUnlocalizedText(char *buffer, int bufferSize); + virtual StringIndex_t GetUnlocalizedTextSymbol(); + + // set the font of the text + virtual void SetFont(vgui::HFont font); + // get the font of the text + virtual vgui::HFont GetFont(); + + // set the width of the text to be drawn + // use this function if the textImage is in another window to cause + // the text to be truncated to the width of the window (elipsis added) + void SetDrawWidth(int width); + // get the width of the text to be drawn + void GetDrawWidth(int &width); + + void ResizeImageToContent(); + void ResizeImageToContentMaxWidth( int nMaxWidth ); + + // set the size of the image + virtual void SetSize(int wide,int tall); + + // get the full size of a text string + virtual void GetContentSize(int &wide, int &tall); + + // draws the text + virtual void Paint(); + + void SetWrap( bool bWrap ); + void RecalculateNewLinePositions(); + + void SetUseFallbackFont( bool bState, HFont hFallback ); + + void SetAllCaps( bool bAllCaps ); + + void SetCenterWrap( bool bWrap ); + void RecalculateCenterWrapIndents(); + + const wchar_t *GetUText( void ) { return _utext; } + + void SetBounds( int x, int y, int w, int h ); + +protected: + // truncate the _text string to fit into the draw width + void SizeText(wchar_t *tempText, int stringLength); + // gets the size of a specified piece of text + virtual void GetTextSize(int &wide, int &tall); + +private: + void RecalculateEllipsesPosition(); + + wchar_t *_utext; // unicode version of the text + short _textBufferLen; // size of the text buffer + short _textLen; // length of the text string + vgui::HFont _font; // font of the text string + vgui::HFont _fallbackFont; + int _drawWidth; // this is the width of the window we are drawing into. + // if there is not enough room truncate the txt and add an elipsis + + StringIndex_t _unlocalizedTextSymbol; // store off the unlocalized text index for build mode + wchar_t *m_pwszEllipsesPosition; + + bool m_bRecalculateTruncation : 1; + bool m_bWrap : 1; + bool m_bUseFallbackFont : 1; + bool m_bRenderUsingFallbackFont : 1; + bool m_bAllCaps : 1; + CUtlVector m_LineBreaks; // an array that holds the index in the buffer to wrap lines at + + bool m_bWrapCenter; // Separate from m_bWrap to ensure it doesn't break legacy code. + CUtlVector m_LineXIndent; // For centered word wrap. The X indent for each line. +}; + +} // namespace vgui + +#endif // TEXTIMAGE_H diff --git a/public/vgui_controls/ToggleButton.h b/public/vgui_controls/ToggleButton.h new file mode 100644 index 0000000..6368d70 --- /dev/null +++ b/public/vgui_controls/ToggleButton.h @@ -0,0 +1,54 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef TOGGLEBUTTON_H +#define TOGGLEBUTTON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Type of button that when pressed stays selected & depressed until pressed again +//----------------------------------------------------------------------------- +class ToggleButton : public Button +{ + DECLARE_CLASS_SIMPLE( ToggleButton, Button ); + +public: + ToggleButton(Panel *parent, const char *panelName, const char *text); + + virtual void DoClick(); + + /* messages sent (get via AddActionSignalTarget()): + "ButtonToggled" + int "state" + */ + +protected: + // overrides + virtual void OnMouseDoublePressed(MouseCode code); + + virtual Color GetButtonFgColor(); + virtual void ApplySchemeSettings(IScheme *pScheme); + + virtual bool CanBeDefaultButton(void); + virtual void OnKeyCodePressed(KeyCode code); + +private: + Color _selectedColor; +}; + +} // namespace vgui + +#endif // TOGGLEBUTTON_H diff --git a/public/vgui_controls/ToolWindow.h b/public/vgui_controls/ToolWindow.h new file mode 100644 index 0000000..eb14ea5 --- /dev/null +++ b/public/vgui_controls/ToolWindow.h @@ -0,0 +1,108 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef TOOLWINDOW_H +#define TOOLWINDOW_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +struct TWEdgePair_t; + +namespace vgui +{ + +class ToolWindow; +class PropertySheet; + +// So that an app can have a "custom" tool window class created during window drag/drop operations on the property sheet +class IToolWindowFactory +{ +public: + virtual ToolWindow *InstanceToolWindow( Panel *parent, bool contextLabel, Panel *firstPage, char const *title, bool contextMenu ) = 0; +}; + +enum ESharedEdge +{ + TOOLWINDOW_NONE = -1, + TOOLWINDOW_LEFT = 0, // The other window's left side is 'shared' with our right side + TOOLWINDOW_TOP, + TOOLWINDOW_RIGHT, + TOOLWINDOW_BOTTOM, +}; + +//----------------------------------------------------------------------------- +// Purpose: Simple frame that holds a property sheet +//----------------------------------------------------------------------------- +class ToolWindow : public Frame +{ + DECLARE_CLASS_SIMPLE( ToolWindow, Frame ); + +public: + ToolWindow(Panel *parent, bool contextLabel, IToolWindowFactory *factory = 0, Panel *page = NULL, char const *title = NULL, bool contextMenu = false, bool inGlobalList = true ); + + ~ToolWindow(); + + virtual bool IsDraggableTabContainer() const; + + // returns a pointer to the PropertySheet this dialog encapsulates + PropertySheet *GetPropertySheet(); + + // wrapper for PropertySheet interface + void AddPage(Panel *page, const char *title, bool contextMenu ); + void RemovePage( Panel *page ); + Panel *GetActivePage(); + void SetActivePage( Panel *page ); + + void SetToolWindowFactory( IToolWindowFactory *factory ); + IToolWindowFactory *GetToolWindowFactory(); + + static int GetToolWindowCount(); + static ToolWindow *GetToolWindow( int index ); + + static CUtlVector< ToolWindow * > s_ToolWindows; + + virtual void Grow( int edge = 0, int from_x = -1, int from_y = -1 ); + virtual void GrowFromClick(); + + void GetSiblingToolWindows( CUtlVector< ToolWindow * > &vecSiblings ); + + void EnableStickyEdges( bool bEnable ); + bool IsStickEdgesEnabled() const; + +protected: + // vgui overrides + virtual void PerformLayout(); + virtual void ActivateBuildMode(); + virtual void RequestFocus(int direction = 0); + virtual void OnSetFocus(); + + virtual void OnMousePressed(MouseCode code); + virtual void OnMouseDoublePressed(MouseCode code); + + MESSAGE_FUNC( OnPageChanged, "PageChanged" ); + + // Override Frame method in order to grow sibling tool windows if possible + virtual void OnGripPanelMoved( int nNewX, int nNewY, int nNewW, int nNewH ); + virtual void OnGripPanelMoveFinished(); + +private: + + void FindOverlappingEdges_R( CUtlVector< ToolWindow * > &vecSiblings, CUtlRBTree< TWEdgePair_t > &rbCurrentEdges, ESharedEdge eEdgeType, int line[ 4 ] ); + void MoveSibling( ToolWindow *pSibling, ESharedEdge eEdge, int dpixels ); + + PropertySheet *m_pPropertySheet; + IToolWindowFactory *m_pFactory; + bool m_bStickyEdges : 1; +}; + +}; // vgui + + +#endif // TOOLWINDOW_H diff --git a/public/vgui_controls/Tooltip.h b/public/vgui_controls/Tooltip.h new file mode 100644 index 0000000..152f4e2 --- /dev/null +++ b/public/vgui_controls/Tooltip.h @@ -0,0 +1,59 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: Creates a Message box with a question in it and yes/no buttons +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TOOLTIP_H +#define TOOLTIP_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Tooltip for a panel - shows text when cursor hovers over a panel +//----------------------------------------------------------------------------- +class Tooltip +{ +public: + Tooltip(Panel *parent, const char *text = NULL); + ~Tooltip(); + + void SetText(const char *text); + const char *GetText(); + void ShowTooltip(Panel *currentPanel); + void HideTooltip(); + void SizeTextWindow(); + void ResetDelay(); + void PerformLayout(); + + void SetTooltipFormatToSingleLine(); + void SetTooltipFormatToMultiLine(); + void SetTooltipDelay(int tooltipDelayMilliseconds); + int GetTooltipDelay(); + void SetEnabled( bool bState ); + +private: + Panel *m_pParent; + virtual void ApplySchemeSettings(IScheme *pScheme); + CUtlVector m_Text; + int _delay; // delay that counts down + int _tooltipDelay; // delay before tooltip comes up. + bool _makeVisible : 1; + bool _displayOnOneLine : 1; + bool _isDirty : 1; + bool _enabled : 1; +}; + +}; + +#endif // TOOLTIP_H diff --git a/public/vgui_controls/TreeView.h b/public/vgui_controls/TreeView.h new file mode 100644 index 0000000..772a83a --- /dev/null +++ b/public/vgui_controls/TreeView.h @@ -0,0 +1,231 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TREEVIEW_H +#define TREEVIEW_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include +#include +#include + +class KeyValues; + +namespace vgui +{ + +class ExpandButton; +class TreeNode; +class TreeViewSubPanel; + +// sorting function, should return true if node1 should be displayed before node2 +typedef bool (*TreeViewSortFunc_t)(KeyValues *node1, KeyValues *node2); + +class TreeView : public Panel +{ + DECLARE_CLASS_SIMPLE( TreeView, Panel ); + +public: + TreeView(Panel *parent, const char *panelName); + ~TreeView(); + + void SetSortFunc(TreeViewSortFunc_t pSortFunc); + + virtual int AddItem(KeyValues *data, int parentItemIndex); + + virtual int GetRootItemIndex(); + virtual int GetNumChildren( int itemIndex ); + virtual int GetChild( int iParentItemIndex, int iChild ); // between 0 and GetNumChildren( iParentItemIndex ). + + virtual int GetItemCount(void); + virtual KeyValues *GetItemData(int itemIndex) const; + virtual void RemoveItem(int itemIndex, bool bPromoteChildren, bool bRecursivelyRemove = false ); + virtual void RemoveAll(); + virtual bool ModifyItem(int itemIndex, KeyValues *data); + virtual int GetItemParent(int itemIndex) const; + + virtual void SetFont(HFont font); + + virtual void SetImageList(ImageList *imageList, bool deleteImageListWhenDone); + + void SetTreeIndent( int nIndentAmount ); + + void SetAllowMultipleSelections( bool state ); + bool IsMultipleSelectionAllowed() const; + + virtual void ClearSelection(); + virtual void AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus = true, bool bMakeItemVisible = true ); + virtual void RemoveSelectedItem( int itemIndex ); + virtual void SelectAll(); + + virtual bool IsItemSelected( int itemIndex ) const; + virtual void RangeSelectItems( int clickedItem ); + virtual void FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices ); + + // returns the id of the currently selected item, -1 if nothing is selected + virtual int GetSelectedItemCount() const; + virtual int GetFirstSelectedItem() const; + virtual int GetSelectedItem( int nSelectionIndex ) const; + virtual void GetSelectedItems( CUtlVector< int >& list ) const; + virtual void GetSelectedItemsForDrag( int nPrimaryDagItem, CUtlVector< int >& list ); + virtual void GetSelectedItemData( CUtlVector< KeyValues * >& list ); + + // set colors for individual elments + virtual void SetItemFgColor(int itemIndex, const Color& color); + virtual void SetItemBgColor(int itemIndex, const Color& color); + virtual void SetItemSelectionTextColor( int itemIndex, const Color& clr ); + virtual void SetItemSelectionBgColor( int itemIndex, const Color& clr ); + virtual void SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr ); + + // returns true if the itemID is valid for use + virtual bool IsItemIDValid(int itemIndex); + + // item iterators + // iterate from [0..GetHighestItemID()], + // and check each with IsItemIDValid() before using + virtual int GetHighestItemID(); + + virtual void ExpandItem(int itemIndex, bool bExpand); + virtual bool IsItemExpanded( int itemIndex ); + + virtual void MakeItemVisible(int itemIndex); + + // This tells which of the visible items is the top one. + virtual void GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible ); + + virtual HFont GetFont(); + + virtual void GenerateDragDataForItem( int itemIndex, KeyValues *msg ); + virtual void SetDragEnabledItems( bool state ); + + virtual void OnLabelChanged( int itemIndex, char const *oldString, char const *newString ); + virtual bool IsLabelEditingAllowed() const; + virtual bool IsLabelBeingEdited() const; + virtual void SetAllowLabelEditing( bool state ); + virtual bool CanCurrentlyEditLabel( int nItemIndex ) const; + + /* message sent + + "TreeViewItemSelected" int "itemIndex" + called when the selected item changes + "TreeViewItemDeselected" int "itemIndex" + called when item is deselected + */ + int GetRowHeight(); + int GetVisibleMaxWidth(); + virtual void OnMousePressed(MouseCode code); + + // By default, the tree view expands nodes on left-click. This enables/disables that feature + void EnableExpandTreeOnLeftClick( bool bEnable ); + + virtual void SetLabelEditingAllowed( int itemIndex, bool state ); + virtual void StartEditingLabel( int itemIndex ); + + virtual bool IsItemDroppable( int itemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist ); + virtual void OnItemDropped( int itemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist ); + virtual bool GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ); + virtual HCursor GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ); + + virtual int GetPrevChildItemIndex( int itemIndex ); + virtual int GetNextChildItemIndex( int itemIndex ); + + virtual void PerformLayout(); + + // Makes the scrollbar parented to some other panel... + ScrollBar *SetScrollBarExternal( bool vertical, Panel *newParent ); + void GetScrollBarSize( bool vertical, int& w, int& h ); + + int FindItemUnderMouse( int mx, int my ); + + // Returns false if item is not visible + bool GetItemBounds( int itemIndex, int &x, int &y, int &w, int &h ); + bool IsItemBeingDisplayed( int itemIndex ); + + // If set to false, all of the immediate children of the root node are displayed, but not the root + void SetShowRootNode( bool bRootVisible ); + + // Insert drop toggle and state, the insert drop location functionality provides drop + // locations between nodes which can be used to perform an insertion at a specific location. + void SetEnableInsertDropLocation( bool bEnable ); + bool AreInsertDropLocationsEnabled() const; + + + // Use these until they return InvalidItemID to iterate all the items. + virtual int FirstItem() const; + virtual int NextItem( int iItem ) const; + virtual int InvalidItemID() const; + +protected: + // functions to override + // called when a node, marked as "Expand", needs to generate it's child nodes when expanded + virtual void GenerateChildrenOfNode(int itemIndex) {} + + // override to open a custom context menu on a node being selected and right-clicked + virtual void GenerateContextMenu( int itemIndex, int x, int y ) {} + + // override to change selection behavior when right clicking to open a context menu + virtual void OnContextMenuSelection( int itemIndex ); + + // overrides + virtual void OnMouseWheeled(int delta); + virtual void OnSizeChanged(int wide, int tall); + virtual void ApplySchemeSettings(IScheme *pScheme); + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); + virtual void SetBgColor( Color color ); + +private: + friend class TreeNode; + friend class TreeNodeText; + + TreeNode* GetItem( int itemIndex ); + virtual void RemoveChildrenOfNode( int itemIndex ); + void SetLabelBeingEdited( bool state ); + + // Clean up the image list + void CleanUpImageList( ); + + // to be accessed by TreeNodes + IImage* GetImage(int index); + + // Add the specified list of items to the selection list. + void AddSelectedItems( const CUtlVector< TreeNode * > &selectionList, bool clearCurrentSelection, bool requestFocus = true, bool bMakeItemVisible = true ); + + // bools + bool m_bAllowLabelEditing : 1; + bool m_bDragEnabledItems : 1; + bool m_bDeleteImageListWhenDone : 1; + bool m_bLeftClickExpandsTree : 1; + bool m_bLabelBeingEdited : 1; + bool m_bAllowMultipleSelections : 1; + bool m_bRootVisible : 1; + bool m_bInsertDropLocations : 1; + + // cross reference - no hierarchy ordering in this list + CUtlLinkedList m_NodeList; + ScrollBar *m_pHorzScrollBar, *m_pVertScrollBar; + int m_nRowHeight; + int m_nTreeIndent; + + ImageList *m_pImageList; + TreeNode *m_pRootNode; + TreeViewSortFunc_t m_pSortFunc; + HFont m_Font; + + CUtlVector< TreeNode * > m_SelectedItems; + TreeViewSubPanel *m_pSubPanel; + + int m_nMostRecentlySelectedItem; + bool m_bScrollbarExternal[ 2 ]; // 0 = vert, 1 = horz +}; + +} + +#endif // TREEVIEW_H diff --git a/public/vgui_controls/TreeViewListControl.h b/public/vgui_controls/TreeViewListControl.h new file mode 100644 index 0000000..87fa141 --- /dev/null +++ b/public/vgui_controls/TreeViewListControl.h @@ -0,0 +1,130 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef TREEVIEWLISTCONTROL_H +#define TREEVIEWLISTCONTROL_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include +#include +#include +#include "utlsymbol.h" + + +namespace vgui +{ + +// --------------------------------------------------------------------------------- // +// CTreeViewListControl +// +// This control has N columns, with a tree view in the leftmost column. +// --------------------------------------------------------------------------------- // + +class CTreeViewListControl : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CTreeViewListControl, Panel ); + +public: + + CTreeViewListControl( vgui::Panel *pParent, const char *pName ); + + // Set the tree view to be displayed on the left. If this isn't set, then nothing displays in here. + virtual void SetTreeView( vgui::TreeView *pTree ); + + // Set the height of the title bar. + virtual void SetTitleBarInfo( vgui::HFont hFont, int titleBarHeight ); + + // Set the color to draw the border lines in. + virtual void SetBorderColor( Color clr ); + + // Initialize the column headers.. This info includes the tree view on the left, so this + virtual void SetNumColumns( int nColumns ); + virtual int GetNumColumns() const; + // ciFlags is a combination of CI_ flags. + virtual void SetColumnInfo( int iColumn, const char *pTitle, int width, int ciFlags=0 ); + + // Use this to render your stuff. Iterate over the rows in the tree view and + virtual int GetNumRows(); + virtual int GetTreeItemAtRow( int iRow ); // You can use m_pTree->GetItemData to get at the data for the row. + + // Use this to find out the client area to render in for each grid element. + // The returned box is inclusive. + // The rule is that the the top and left pixels in each grid element are reserved for lines. + virtual void GetGridElementBounds( int iColumn, int iRow, int &left, int &top, int &right, int &bottom ); + + virtual vgui::TreeView *GetTree(); + + virtual int GetTitleBarHeight(); + + virtual int GetScrollBarSize(); + +// Overrides. +public: + + // This is where it recalculates the row infos. + virtual void PerformLayout(); + + // Usually, you'll want to override paint. After calling the base, use GetNumRows() to + // iterate over the data in the tree control and fill in the other columns. + virtual void Paint(); + virtual void PostChildPaint(); + + // You can override this to change the way the title bars are drawn. + virtual void DrawTitleBars(); + + +public: + + enum + { + // By default, column header text is centered. + CI_HEADER_LEFTALIGN =0x0001 + }; + + +protected: + + void RecalculateRows(); + void RecalculateRows_R( int index ); + void RecalculateColumns(); + +private: + + vgui::TreeView *m_pTree; + + class CColumnInfo + { + public: + CColumnInfo() + { + m_Width = m_Left = m_Right = m_ciFlags = 0; + } + + CUtlSymbol m_Title; + int m_Width; + int m_Left; + int m_Right; + int m_ciFlags; // Combination of CI_ flags. + }; + CUtlVector m_Columns; + + vgui::HFont m_TitleBarFont; + int m_TitleBarHeight; + + // These are indices into the tree view. + CUtlVector m_Rows; + + Color m_BorderColor; +}; + +} // namespace + + +#endif // TREEVIEWLISTCONTROL_H diff --git a/public/vgui_controls/URLLabel.h b/public/vgui_controls/URLLabel.h new file mode 100644 index 0000000..68c92f6 --- /dev/null +++ b/public/vgui_controls/URLLabel.h @@ -0,0 +1,49 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef URLLABEL_H +#define URLLABEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +namespace vgui +{ + +class URLLabel : public Label +{ + DECLARE_CLASS_SIMPLE( URLLabel, Label ); + +public: + URLLabel(Panel *parent, const char *panelName, const char *text, const char *pszURL); + URLLabel(Panel *parent, const char *panelName, const wchar_t *wszText, const char *pszURL); + ~URLLabel(); + + void SetURL(const char *pszURL); + +protected: + virtual void OnMousePressed(MouseCode code); + virtual void ApplySettings( KeyValues *inResourceData ); + virtual void GetSettings( KeyValues *outResourceData ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual const char *GetDescription( void ); + + const char *GetURL( void ) { return m_pszURL; } + +private: + char *m_pszURL; + int m_iURLSize; + bool m_bUnderline; +}; + +} + +#endif // URLLABEL_H diff --git a/public/vgui_controls/WizardPanel.h b/public/vgui_controls/WizardPanel.h new file mode 100644 index 0000000..12fdca6 --- /dev/null +++ b/public/vgui_controls/WizardPanel.h @@ -0,0 +1,127 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WIZARDPANEL_H +#define WIZARDPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +class WizardSubPanel; + +//----------------------------------------------------------------------------- +// Purpose: Type of dialog that supports moving back and forth through a series +// of sub-dialogs, WizardSubPanels +//----------------------------------------------------------------------------- +class WizardPanel : public Frame +{ + DECLARE_CLASS_SIMPLE( WizardPanel, Frame ); + +public: + WizardPanel(Panel *parent, const char *panelName); + ~WizardPanel(); + + // Start the wizard, starting with the startPanel + virtual void Run(WizardSubPanel *startPanel); + + // Called when the buttons are pressed + // WizardSubPanels can also call these functions to simulate a button being pressed + MESSAGE_FUNC( OnNextButton, "NextButton" ); + MESSAGE_FUNC( OnPrevButton, "PrevButton" ); + MESSAGE_FUNC( OnFinishButton, "FinishButton" ); + MESSAGE_FUNC( OnCancelButton, "CancelButton" ); + + // sets whether or not a button is enabled + // this state is managed, and will be reset whenever going to a new page + virtual void SetNextButtonEnabled(bool state); + virtual void SetPrevButtonEnabled(bool state); + virtual void SetFinishButtonEnabled(bool state); + virtual void SetCancelButtonEnabled(bool state); + + // sets whether or not a button is visible + // this state is unmanaged, the user needs to ensure that the buttons state + // is correct when going both back and prev through the wizard + virtual void SetNextButtonVisible(bool state); + virtual void SetPrevButtonVisible(bool state); + virtual void SetFinishButtonVisible(bool state); + virtual void SetCancelButtonVisible(bool state); + + // sets the text for a button + // setting the text to be NULL resets the text to it's default state + // this state is unmanaged, the user needs to ensure that the buttons state + // is correct when going both back and prev through the wizard + virtual void SetNextButtonText(const char *text); + virtual void SetPrevButtonText(const char *text); + virtual void SetFinishButtonText(const char *text); + virtual void SetCancelButtonText(const char *text); + + // general wizard state for all the subpanels to access + virtual KeyValues *GetWizardData(); + + // recalculates where the key focus should be in the wizard + virtual void ResetKeyFocus(); + virtual void ResetDefaultButton(); + + // resets the sub panel history for the control + virtual void ResetHistory(); + + // returns a page by name + virtual WizardSubPanel *GetSubPanelByName(const char *pageName); + + virtual void ShowButtons(bool state); + virtual void GetClientArea(int &x, int &y, int &wide, int &tall); + +protected: + MESSAGE_FUNC_PTR( InternalActivateNextSubPanel, "ActivateNextSubPanel", panel ) + { + ActivateNextSubPanel( (WizardSubPanel *)panel ); + } + + virtual void ActivateNextSubPanel(WizardSubPanel *subPanel); + virtual void ActivatePrevSubPanel(); + virtual void CreateButtons(); + virtual void RecalculateTabOrdering(); + virtual vgui::WizardSubPanel *GetCurrentSubPanel() { return _currentSubPanel; } + + // overrides + virtual void PerformLayout(); + virtual void ApplySchemeSettings(IScheme *pScheme); + + // reroute build messages to the currently active sub panel + virtual void ActivateBuildMode(); + + // close maps to the cancel button + virtual void OnClose(); + virtual void OnCommand(const char *command); + virtual void OnCloseFrameButtonPressed(); + +private: + WizardSubPanel *FindNextValidSubPanel(WizardSubPanel *currentPanel); + + Button *_prevButton; + Button *_nextButton; + Button *_cancelButton; + Button *_finishButton; + + WizardSubPanel *_currentSubPanel; + KeyValues *_currentData; + + Dar _subPanelStack; // contains a list of all the subpanels (not including the current one) + + bool _showButtons; +}; + +} // namespace vgui + + +#endif // WIZARDPANEL_H diff --git a/public/vgui_controls/WizardSubPanel.h b/public/vgui_controls/WizardSubPanel.h new file mode 100644 index 0000000..6db14b9 --- /dev/null +++ b/public/vgui_controls/WizardSubPanel.h @@ -0,0 +1,91 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef WIZARDSUBPANEL_H +#define WIZARDSUBPANEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Base panel for use in Wizards and in property sheets +//----------------------------------------------------------------------------- +class WizardSubPanel : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( WizardSubPanel, EditablePanel ); + +public: + // constructor + WizardSubPanel(Panel *parent, const char *panelName); + ~WizardSubPanel(); + + // called when the subpanel is displayed + // All controls & data should be reinitialized at this time + virtual void OnDisplayAsNext() {} + + // called anytime the panel is first displayed, whether the user is moving forward or back + // called immediately after OnDisplayAsNext/OnDisplayAsPrev + virtual void OnDisplay() {} + + // called when displayed as previous + virtual void OnDisplayAsPrev() {} + + // called when one of the wizard buttons are pressed + // returns true if the wizard should advance, false otherwise + virtual bool OnNextButton() { return true; } + virtual bool OnPrevButton() { return true; } + virtual bool OnFinishButton() { return true; } + virtual bool OnCancelButton() { return true; } + + // returns true if this panel should be displayed, or if we should just skip over it + virtual bool ShouldDisplayPanel() { return true; } + + // return true if this subpanel doesn't need the next/prev/finish/cancel buttons or will do it itself + virtual bool isNonWizardPanel() { return false; } + + // returns a pointer to the next subpanel that should be displayed + virtual WizardSubPanel *GetNextSubPanel() = 0; + + // returns a pointer to the panel to return to + // it must be a panel that is already in the wizards panel history + // returning NULL tells it to use the immediate previous panel in the history + virtual WizardSubPanel *GetPrevSubPanel() { return NULL; } + + virtual WizardPanel *GetWizardPanel() { return _wizardPanel; } + virtual void SetWizardPanel(WizardPanel *wizardPanel) { _wizardPanel = wizardPanel; } + + // returns a pointer to the wizard's doc + virtual KeyValues *GetWizardData(); + + // returns a pointer + virtual WizardSubPanel *GetSiblingSubPanelByName(const char *pageName); + + // gets the size this subpanel would like the wizard to be + // returns true if it has a desired size + virtual bool GetDesiredSize(int &wide, int &tall); + +protected: + virtual void ApplySettings(KeyValues *inResourceData); + virtual void GetSettings( KeyValues *outResourceData ); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual const char *GetDescription(); + +private: + WizardPanel *_wizardPanel; + int m_iDesiredWide, m_iDesiredTall; +}; + +} // namespace vgui + + +#endif // WIZARDSUBPANEL_H diff --git a/public/vgui_controls/consoledialog.h b/public/vgui_controls/consoledialog.h new file mode 100644 index 0000000..56cd171 --- /dev/null +++ b/public/vgui_controls/consoledialog.h @@ -0,0 +1,169 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef CONSOLEDIALOG_H +#define CONSOLEDIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include +#include "tier1/UtlVector.h" +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/Frame.h" +#include "icvar.h" + +class ConCommandBase; + + +namespace vgui +{ + +// Things the user typed in and hit submit/return with +class CHistoryItem +{ +public: + CHistoryItem( void ); + CHistoryItem( const char *text, const char *extra = NULL ); + CHistoryItem( const CHistoryItem& src ); + ~CHistoryItem( void ); + + const char *GetText() const; + const char *GetExtra() const; + void SetText( const char *text, const char *extra ); + bool HasExtra() { return m_bHasExtra; } + +private: + char *m_text; + char *m_extraText; + bool m_bHasExtra; +}; + +//----------------------------------------------------------------------------- +// Purpose: Game/dev console dialog +//----------------------------------------------------------------------------- +class CConsolePanel : public vgui::EditablePanel, public IConsoleDisplayFunc +{ + DECLARE_CLASS_SIMPLE( CConsolePanel, vgui::EditablePanel ); + +public: + CConsolePanel( Panel *pParent, const char *pName, bool bStatusVersion ); + ~CConsolePanel(); + + // Inherited from IConsoleDisplayFunc + virtual void ColorPrint( const Color& clr, const char *pMessage ); + virtual void Print( const char *pMessage ); + virtual void DPrint( const char *pMessage ); + virtual void GetConsoleText( char *pchText, size_t bufSize ) const; + + // clears the console + void Clear(); + + // writes console to a file + void DumpConsoleTextToFile(); + + // Hides the console + void Hide(); + + bool TextEntryHasFocus() const; + void TextEntryRequestFocus(); + + + +private: + enum + { + MAX_HISTORY_ITEMS = 100, + }; + + class CompletionItem + { + public: + CompletionItem( void ); + CompletionItem( const CompletionItem& src ); + CompletionItem& operator =( const CompletionItem& src ); + ~CompletionItem( void ); + const char *GetItemText( void ); + const char *GetCommand( void ) const; + const char *GetName() const; + + bool m_bIsCommand; + ConCommandBase *m_pCommand; + CHistoryItem *m_pText; + }; + +protected: + // methods + void OnAutoComplete(bool reverse); + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + void RebuildCompletionList(const char *partialText); + void UpdateCompletionListPosition(); + MESSAGE_FUNC( CloseCompletionList, "CloseCompletionList" ); + MESSAGE_FUNC_CHARPTR( OnMenuItemSelected, "CompletionCommand", command ); + void ClearCompletionList(); + void AddToHistory( const char *commandText, const char *extraText ); + + // vgui overrides + virtual void PerformLayout(); + virtual void ApplySchemeSettings(vgui::IScheme *pScheme); + virtual void OnCommand(const char *command); + virtual void OnKeyCodeTyped(vgui::KeyCode code); + virtual void OnThink(); + + vgui::RichText *m_pHistory; + vgui::TextEntry *m_pEntry; + vgui::Button *m_pSubmit; + vgui::Menu *m_pCompletionList; + Color m_PrintColor; + Color m_DPrintColor; + + int m_iNextCompletion; // the completion that we'll next go to + char m_szPartialText[256]; + char m_szPreviousPartialText[256]; + bool m_bAutoCompleteMode; // true if the user is currently tabbing through completion options + bool m_bWasBackspacing; + bool m_bStatusVersion; + + CUtlVector< CompletionItem * > m_CompletionList; + CUtlVector< CHistoryItem > m_CommandHistory; + + friend class CConsoleDialog; +}; + + +class CConsoleDialog : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CConsoleDialog, vgui::Frame ); + +public: + CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ); + + virtual void OnScreenSizeChanged( int iOldWide, int iOldTall ); + virtual void Close(); + virtual void PerformLayout(); + + // brings dialog to the fore + MESSAGE_FUNC( Activate, "Activate" ); + MESSAGE_FUNC_CHARPTR( OnCommandSubmitted, "CommandSubmitted", command ); + + // hides the console + void Hide(); + + // Chain to the page + void Print( const char *msg ); + void DPrint( const char *msg ); + void ColorPrint( const Color& clr, const char *msg ); + void Clear(); + void DumpConsoleTextToFile(); + +protected: + CConsolePanel *m_pConsolePanel; +}; + +} // end namespace vgui + +#endif // CONSOLEDIALOG_H diff --git a/public/vgui_controls/pch_vgui_controls.h b/public/vgui_controls/pch_vgui_controls.h new file mode 100644 index 0000000..a047066 --- /dev/null +++ b/public/vgui_controls/pch_vgui_controls.h @@ -0,0 +1,115 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef PCH_VGUI_CONTROLS_H +#define PCH_VGUI_CONTROLS_H + +#ifdef _WIN32 +#pragma once +#endif + +// general includes +#include +#include +#include "tier0/dbg.h" +#include "tier0/valve_off.h" +#include "tier1/KeyValues.h" + +#include "tier0/valve_on.h" +#include "tier0/memdbgon.h" + +#include "FileSystem.h" +#include "tier0/validator.h" + +// vgui includes +#include "vgui/IBorder.h" +#include "vgui/IInput.h" +#include "vgui/ILocalize.h" +#include "vgui/IPanel.h" +#include "vgui/IScheme.h" +#include "vgui/ISurface.h" +#include "vgui/ISystem.h" +#include "vgui/IVGui.h" +#include "vgui/KeyCode.h" +#include "vgui/Cursor.h" +#include "vgui/MouseCode.h" + +// vgui controls includes +#include "vgui_controls/controls.h" + +#include "vgui_controls/AnimatingImagePanel.h" +#include "vgui_controls/AnimationController.h" +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui_controls/BuildGroup.h" +#include "vgui_controls/BuildModeDialog.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/CheckButtonList.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Controls.h" +#include "vgui_controls/DialogManager.h" +#include "vgui_controls/DirectorySelectDialog.h" +#include "vgui_controls/Divider.h" +#include "vgui_controls/EditablePanel.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/FocusNavGroup.h" +#include "vgui_controls/Frame.h" +#include "vgui_controls/GraphPanel.h" +#include "vgui_controls/HTML.h" +#include "vgui_controls/Image.h" +#include "vgui_controls/ImageList.h" +#include "vgui_controls/ImagePanel.h" +#include "vgui_controls/Label.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/ListViewPanel.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/MenuBar.h" +#include "vgui_controls/MenuButton.h" +#include "vgui_controls/MenuItem.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/Panel.h" +#ifndef HL1 +#include "vgui_controls/PanelAnimationVar.h" +#endif +#include "vgui_controls/PanelListPanel.h" +#include "vgui_controls/PHandle.h" +#include "vgui_controls/ProgressBar.h" +#include "vgui_controls/ProgressBox.h" +#include "vgui_controls/PropertyDialog.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/QueryBox.h" +#include "vgui_controls/RadioButton.h" +#include "vgui_controls/RichText.h" +#include "vgui_controls/ScrollBar.h" +#include "vgui_controls/ScrollBarSlider.h" +#include "vgui_controls/SectionedListPanel.h" +#include "vgui_controls/Slider.h" +#ifndef HL1 +#include "vgui_controls/Splitter.h" +#endif +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/ToggleButton.h" +#include "vgui_controls/Tooltip.h" +#ifndef HL1 +#include "vgui_controls/ToolWindow.h" +#endif +#include "vgui_controls/TreeView.h" +#ifndef HL1 +#include "vgui_controls/TreeViewListControl.h" +#endif +#include "vgui_controls/URLLabel.h" +#include "vgui_controls/WizardPanel.h" +#include "vgui_controls/WizardSubPanel.h" + +#ifndef HL1 +#include "vgui_controls/KeyBoardEditorDialog.h" +#include "vgui_controls/InputDialog.h" +#endif + +#endif // PCH_VGUI_CONTROLS_H \ No newline at end of file diff --git a/public/vgui_controls/perforcefilelistframe.h b/public/vgui_controls/perforcefilelistframe.h new file mode 100644 index 0000000..4ee9045 --- /dev/null +++ b/public/vgui_controls/perforcefilelistframe.h @@ -0,0 +1,151 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. ======= +// +// List of perforce files and operations +// +//============================================================================= + +#ifndef PERFORCEFILELISTFRAME_H +#define PERFORCEFILELISTFRAME_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/frame.h" +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" +#include "p4lib/ip4.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Enumeration of operation dialog ids +//----------------------------------------------------------------------------- +enum +{ + OPERATION_DIALOG_ID_PERFORCE = 0, + + OPERATION_DIALOG_STANDARD_ID_COUNT, + OPERATION_DIALOG_STANDARD_ID_MAX = OPERATION_DIALOG_STANDARD_ID_COUNT - 1, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for a list of files + an operation to perform +//----------------------------------------------------------------------------- +class COperationFileListFrame : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( COperationFileListFrame, vgui::Frame ); + +public: + // NOTE: The dialog ID is used to allow dialogs to have different configurations saved + COperationFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, bool bShowDescription, bool bShowOkOnly = false, int nDialogID = 1 ); + virtual ~COperationFileListFrame(); + + // Command handler + virtual void OnCommand( const char *pCommand ); + virtual void PerformLayout(); + + // Adds files to the frame + void ClearAllOperations(); + void AddOperation( const char *pOperation, const char *pFileName ); + void AddOperation( const char *pOperation, const char *pFileName, const Color& clr ); + + // Resizes the operation column to fit the operation text + void ResizeOperationColumnToContents(); + + // Sets the column header for the 'operation' column + void SetOperationColumnHeaderText( const char *pText ); + + // Shows the panel + void DoModal( KeyValues *pContextKeyValues = NULL, const char *pMessage = NULL ); + + // Retrieves the number of files, the file names, and operations + int GetOperationCount(); + const char *GetFileName( int i ); + const char *GetOperation( int i ); + + // Retreives the description (only if it was shown) + const char *GetDescription(); + +private: + virtual bool PerformOperation() { return true; } + const char *CompletionMessage(); + void CleanUpMessage(); + + vgui::ListPanel *m_pFileBrowser; + vgui::Splitter *m_pSplitter; + vgui::TextEntry *m_pDescription; + vgui::Button *m_pYesButton; + vgui::Button *m_pNoButton; + KeyValues *m_pContextKeyValues; + CUtlString m_MessageName; + char *m_pText; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for picker +//----------------------------------------------------------------------------- +enum PerforceAction_t +{ + PERFORCE_ACTION_NONE = -1, + PERFORCE_ACTION_FILE_ADD = 0, + PERFORCE_ACTION_FILE_EDIT, + PERFORCE_ACTION_FILE_DELETE, + PERFORCE_ACTION_FILE_REVERT, + PERFORCE_ACTION_FILE_SUBMIT, +}; + + +//----------------------------------------------------------------------------- +// Purpose: Modal dialog for picker +//----------------------------------------------------------------------------- +class CPerforceFileListFrame : public COperationFileListFrame +{ + DECLARE_CLASS_SIMPLE( CPerforceFileListFrame, COperationFileListFrame ); + +public: + CPerforceFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, PerforceAction_t action ); + virtual ~CPerforceFileListFrame(); + + // Adds files to the frame + void ClearAllFiles(); + void AddFile( const char *pFullPath ); + void AddFile( const char *pRelativePath, const char *pPathId ); + + void DoModal( KeyValues *pContextKeys = NULL, const char *pMessage = NULL ); + +private: + virtual bool PerformOperation(); + + // Adds files for open, submit + void AddFileForOpen( const char *pFullPath ); + void AddFileForSubmit( const char *pFullPath, P4FileState_t state ); + + // Does the perforce operation + void PerformPerforceAction( ); + + PerforceAction_t m_Action; + CUtlVector< P4File_t > m_OpenedFiles; + CUtlString m_LastOpenedFilePathId; +}; + + +//----------------------------------------------------------------------------- +// Show the perforce query dialog +// The specified keyvalues message will be sent either +// 1) If you open the file for add/edit +// 2) If you indicate to not add a file for add but don't hit cancel +// If a specific perforce action is specified, then the dialog will only +// be displayed if that action is appropriate +//----------------------------------------------------------------------------- +void ShowPerforceQuery( vgui::Panel *pParent, const char *pFileName, vgui::Panel *pActionSignalTarget, KeyValues *pKeyValues, PerforceAction_t actionFilter = PERFORCE_ACTION_NONE ); + + +#endif // PERFORCEFILELISTFRAME_H + diff --git a/public/vgui_controls/savedocumentquery.h b/public/vgui_controls/savedocumentquery.h new file mode 100644 index 0000000..589f640 --- /dev/null +++ b/public/vgui_controls/savedocumentquery.h @@ -0,0 +1,37 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// This dialog asks if you want to save your work +// +//============================================================================= + +#ifndef SAVEDOCUMENTQUERY_H +#define SAVEDOCUMENTQUERY_H + +#ifdef _WIN32 +#pragma once +#endif + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class KeyValues; +namespace vgui +{ +class Panel; +} + + +//----------------------------------------------------------------------------- +// Show the save document query dialog +// NOTE: The following commands will be posted to the action signal target: +// "OnExit" - when we want to quit +// "OnSave" - when we want to save the file +// "OnCloseNoSave" - when we want to close the file without saving it +// "commandname" - additional command send after saving (SAVEDOC_POSTCOMMAND_AFTER_SAVE) +// "OnMarkNotDirty" - when we want to mark the file not dirty +//----------------------------------------------------------------------------- +void ShowSaveDocumentQuery( vgui::Panel *pParent, const char *pFileName, const char *pFileType, int nContext, vgui::Panel *pActionSignalTarget, KeyValues *pPostSaveCommand ); + + +#endif // SAVEDOCUMENTQUERY_H diff --git a/public/vgui_controls/subrectimage.h b/public/vgui_controls/subrectimage.h new file mode 100644 index 0000000..13973b4 --- /dev/null +++ b/public/vgui_controls/subrectimage.h @@ -0,0 +1,51 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef SUBRECTIMAGE_H +#define SUBRECTIMAGE_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Image.h" +#include "vgui/vgui.h" + + +//----------------------------------------------------------------------------- +// Purpose: Check box image +//----------------------------------------------------------------------------- +class CSubRectImage : public vgui::Image +{ +public: + CSubRectImage( const char *filename, bool hardwareFiltered, int subx, int suby, int subw, int subh ); + virtual ~CSubRectImage(); + + void GetSize( int &wide, int &tall ); + void GetContentSize( int &wide, int &tall ); + void SetSize( int x, int y ); + void SetPos( int x, int y ); + void SetColor( Color col ); + const char *GetName(); + void Paint(); + void ForceUpload(); + vgui::HTexture GetID(); + bool IsValid(); + +private: + vgui::HTexture _id; + int sub[ 4 ]; + char *_filename; + int _pos[2]; + int _wide,_tall; + Color _color; + bool _uploaded; + bool _valid; + bool _filtered; +}; + + +#endif // SUBRECTIMAGE_H \ No newline at end of file diff --git a/public/vgui_controls/tgaimagepanel.h b/public/vgui_controls/tgaimagepanel.h new file mode 100644 index 0000000..53a4087 --- /dev/null +++ b/public/vgui_controls/tgaimagepanel.h @@ -0,0 +1,59 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef TGAIMAGEPANEL_H +#define TGAIMAGEPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/Panel.h" +#include "tier1/utlstring.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: Displays a tga image +//----------------------------------------------------------------------------- +class CTGAImagePanel : public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CTGAImagePanel, vgui::Panel ); + +public: + CTGAImagePanel( vgui::Panel *parent, const char *name ); + + ~CTGAImagePanel(); + + void SetTGAFilename( const char *filename ); + char const *GetTGAFilename() const; + void SetTGAFilenameNonMod( const char *filename ); + + void SetShouldScaleImage( bool state ); + + void SetImageColor( Color imageColor ); + + int GetTextureID( void ) const { return m_iTextureID; } + + virtual void GetSettings(KeyValues *outResourceData); + virtual void ApplySettings(KeyValues *inResourceData); + + virtual void Paint( void ); + +private: + int m_iTextureID; + int m_iImageWidth; + int m_iImageHeight; + Color m_ImageColor; + CUtlString m_sTGAFilenameWithPath; + CUtlString m_sTGAFilename; + + bool m_bHasValidTexture : 1; + bool m_bLoadedTexture : 1; + bool m_bScaleImage : 1; + +}; + +#endif //TGAIMAGEPANEL_H diff --git a/public/vgui_controls/vgui_controls.cpp b/public/vgui_controls/vgui_controls.cpp new file mode 100644 index 0000000..ae1b088 --- /dev/null +++ b/public/vgui_controls/vgui_controls.cpp @@ -0,0 +1,57 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= +#include "vgui/IVgui.h" +#include "vgui_controls/Controls.h" + +#include "vgui_controls/AnimatingImagePanel.h" +#include "vgui_controls/BitmapImagePanel.h" +#include "vgui_controls/ExpandButton.h" +#include "vgui_controls/TreeViewListControl.h" +#include "vgui_controls/HTML.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +USING_BUILD_FACTORY( Button ); +USING_BUILD_FACTORY( EditablePanel ); +USING_BUILD_FACTORY( ImagePanel ); +USING_BUILD_FACTORY( Label ); +USING_BUILD_FACTORY( Panel ); +USING_BUILD_FACTORY( ToggleButton ); +USING_BUILD_FACTORY( AnimatingImagePanel ); +USING_BUILD_FACTORY( CBitmapImagePanel ); +USING_BUILD_FACTORY( CheckButton ); +USING_BUILD_FACTORY( ComboBox ); +USING_BUILD_FACTORY_ALIAS( CvarToggleCheckButton, CvarToggleCheckButton ); +USING_BUILD_FACTORY( Divider ); +USING_BUILD_FACTORY( ExpandButton ); +USING_BUILD_FACTORY( GraphPanel ); +//USING_BUILD_FACTORY_ALIAS( HTML, HTML_NoJavascript ); +//USING_BUILD_FACTORY_ALIAS( HTML, HTML_Javascript ); +USING_BUILD_FACTORY( ListPanel ); +USING_BUILD_FACTORY( ListViewPanel ); +USING_BUILD_FACTORY( Menu ); +USING_BUILD_FACTORY( MenuBar ); +USING_BUILD_FACTORY( MenuButton ); +USING_BUILD_FACTORY( MenuItem ); +USING_BUILD_FACTORY( MessageBox ); +USING_BUILD_FACTORY( ProgressBar ); +USING_BUILD_FACTORY( CircularProgressBar ); +USING_BUILD_FACTORY( RadioButton ); +USING_BUILD_FACTORY( RichText ); +USING_BUILD_FACTORY( ScalableImagePanel ); +USING_BUILD_FACTORY_ALIAS( ScrollBar, ScrollBar_Vertical ); +USING_BUILD_FACTORY_ALIAS( ScrollBar, ScrollBar_Horizontal ); +USING_BUILD_FACTORY( ScrollBar ); +USING_BUILD_FACTORY( TextEntry ); +USING_BUILD_FACTORY( TreeView ); +USING_BUILD_FACTORY( CTreeViewListControl ); +USING_BUILD_FACTORY( URLLabel ); + +int g_nYou_Must_Add_Public_Vgui_Controls_Vgui_ControlsCpp_To_Your_Project = 0; diff --git a/public/videocfg/videocfg.h b/public/videocfg/videocfg.h new file mode 100644 index 0000000..27f3cb2 --- /dev/null +++ b/public/videocfg/videocfg.h @@ -0,0 +1,103 @@ +//===== Copyright © 2005-2008, Valve Corporation, All rights reserved. ======// +// +// +//===========================================================================// + +#ifndef VIDEOCFG_H +#define VIDEOCFG_H + +#if defined( _WIN32 ) +#pragma once +#endif + +#include "tier1/utlvector.h" +#include "shaderapi/IShaderDevice.h" + +class KeyValues; + +struct VidMatConfigData_t +{ + char szFileName[MAX_PATH]; + char szPathID[64]; + KeyValues *pConfigKeys; + int nVendorID; + int nDeviceID; + int nDXLevel; + unsigned int nSystemMemory; + unsigned int nVideoMemory; + int nPhysicalScreenWidth; + int nPhysicalScreenHeight; + CUtlVector< ShaderDisplayMode_t > displayModes; + bool bIsVideo; +}; + +enum CPULevel_t +{ + CPU_LEVEL_UNKNOWN = -1, + + CPU_LEVEL_LOW = 0, + CPU_LEVEL_MEDIUM, + CPU_LEVEL_HIGH, + CPU_LEVEL_PC_COUNT, + + CPU_LEVEL_360 = CPU_LEVEL_PC_COUNT, + CPU_LEVEL_COUNT, + + CPU_LEVEL_BIT_COUNT = 2, +}; + +enum GPULevel_t +{ + GPU_LEVEL_UNKNOWN = -1, + + GPU_LEVEL_LOW = 0, + GPU_LEVEL_MEDIUM, + GPU_LEVEL_HIGH, + GPU_LEVEL_VERYHIGH, + GPU_LEVEL_PC_COUNT, + + GPU_LEVEL_360 = GPU_LEVEL_PC_COUNT, + GPU_LEVEL_COUNT, + + GPU_LEVEL_BIT_COUNT = 3, +}; + +enum MemLevel_t +{ + MEM_LEVEL_UNKNOWN = -1, + + MEM_LEVEL_LOW = 0, + MEM_LEVEL_MEDIUM, + MEM_LEVEL_HIGH, + MEM_LEVEL_PC_COUNT, + + MEM_LEVEL_360 = MEM_LEVEL_PC_COUNT, + MEM_LEVEL_COUNT, + + MEM_LEVEL_BIT_COUNT = 2, +}; + +enum GPUMemLevel_t +{ + GPU_MEM_LEVEL_UNKNOWN = -1, + + GPU_MEM_LEVEL_LOW = 0, + GPU_MEM_LEVEL_MEDIUM, + GPU_MEM_LEVEL_HIGH, + GPU_MEM_LEVEL_PC_COUNT, + + GPU_MEM_LEVEL_360 = GPU_MEM_LEVEL_PC_COUNT, + GPU_MEM_LEVEL_COUNT, + + GPU_MEM_LEVEL_BIT_COUNT = 2, +}; + +bool RecommendedConfig( VidMatConfigData_t &configData ); +bool ResetVideoConfigToDefaults( KeyValues *pConfigKeys = NULL ); +bool UpdateVideoConfigConVars( KeyValues *pConfigKeys = NULL ); + +bool ReadCurrentVideoConfig( KeyValues *pConfigKeys, bool bDefault = false ); +bool UpdateCurrentVideoConfig( int nWidth, int nHeight, int nAspectRatioMode, bool bFullscreen, bool bNoWindowBorder ); +void UpdateSystemLevel( int nCPULevel, int nGPULevel, int nMemLevel, int nGPUMemLevel, bool bVGUIIsSplitscreen, const char *pModName ); + +#endif // VIDEOCFG_H \ No newline at end of file diff --git a/public/view_shared.h b/public/view_shared.h new file mode 100644 index 0000000..e6ee1f2 --- /dev/null +++ b/public/view_shared.h @@ -0,0 +1,164 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +//=============================================================================// + +#ifndef VIEW_SHARED_H +#define VIEW_SHARED_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "convar.h" +#include "mathlib/vector.h" +#include "materialsystem/MaterialSystemUtil.h" + + +//----------------------------------------------------------------------------- +// Flags passed in with view setup +//----------------------------------------------------------------------------- +enum ClearFlags_t +{ + VIEW_CLEAR_COLOR = 0x1, + VIEW_CLEAR_DEPTH = 0x2, + VIEW_CLEAR_FULL_TARGET = 0x4, + VIEW_NO_DRAW = 0x8, + VIEW_CLEAR_OBEY_STENCIL = 0x10, // Draws a quad allowing stencil test to clear through portals + VIEW_CLEAR_STENCIL = 0x20, +}; + + +enum MotionBlurMode_t +{ + MOTION_BLUR_DISABLE = 1, + MOTION_BLUR_GAME = 2, // Game uses real-time inter-frame data + MOTION_BLUR_SFM = 3 // Use SFM data passed in CViewSetup structure +}; + +//----------------------------------------------------------------------------- +// Purpose: Renderer setup data. +//----------------------------------------------------------------------------- +class CViewSetup +{ +public: + CViewSetup() + { + m_flAspectRatio = 0.0f; + + // These match mat_dof convars + m_flNearBlurDepth = 20.0; + m_flNearFocusDepth = 100.0; + m_flFarFocusDepth = 250.0; + m_flFarBlurDepth = 1000.0; + m_flNearBlurRadius = 10.0; + m_flFarBlurRadius = 5.0; + m_nDoFQuality = 0; + + m_bRenderToSubrectOfLargerScreen = false; + m_bDoBloomAndToneMapping = true; + m_nMotionBlurMode = MOTION_BLUR_GAME; + m_bDoDepthOfField = false; + m_bHDRTarget = false; + m_bOffCenter = false; + m_bCacheFullSceneState = false; + m_bDrawWorldNormal = false; + m_bCullFrontFaces = false; + m_bCustomViewMatrix = false; +// m_bUseExplicitViewVector = false; + m_bRenderFlashlightDepthTranslucents = false; + } + +// shared by 2D & 3D views + + // left side of view window + int x; + // top side of view window + int y; + // width of view window + int width; + // height of view window + int height; + +// the rest are only used by 3D views + + // Orthographic projection? + bool m_bOrtho; + // View-space rectangle for ortho projection. + float m_OrthoLeft; + float m_OrthoTop; + float m_OrthoRight; + float m_OrthoBottom; + + bool m_bCustomViewMatrix; + matrix3x4_t m_matCustomViewMatrix; + + // horizontal FOV in degrees + float fov; + // horizontal FOV in degrees for in-view model + float fovViewmodel; + + // 3D origin of camera + Vector origin; + + // heading of camera (pitch, yaw, roll) + QAngle angles; + // local Z coordinate of near plane of camera + float zNear; + // local Z coordinate of far plane of camera + float zFar; + + // local Z coordinate of near plane of camera ( when rendering view model ) + float zNearViewmodel; + // local Z coordinate of far plane of camera ( when rendering view model ) + float zFarViewmodel; + + // The aspect ratio to use for computing the perspective projection matrix + // (0.0f means use the viewport) + float m_flAspectRatio; + + // Camera settings to control depth of field + float m_flNearBlurDepth; + float m_flNearFocusDepth; + float m_flFarFocusDepth; + float m_flFarBlurDepth; + float m_flNearBlurRadius; + float m_flFarBlurRadius; + int m_nDoFQuality; + + // Camera settings to control motion blur + MotionBlurMode_t m_nMotionBlurMode; + float m_flShutterTime; // In seconds + Vector m_vShutterOpenPosition; // Start of frame or "shutter open" + QAngle m_shutterOpenAngles; // + Vector m_vShutterClosePosition; // End of frame or "shutter close" + QAngle m_shutterCloseAngles; // + + // Controls for off-center projection (needed for poster rendering) + float m_flOffCenterTop; + float m_flOffCenterBottom; + float m_flOffCenterLeft; + float m_flOffCenterRight; + bool m_bOffCenter:1; + + // set to true if this is to draw into a subrect of the larger screen + // this really is a hack, but no more than the rest of the way this class is used + bool m_bRenderToSubrectOfLargerScreen:1; + + // Controls that the SFM needs to tell the engine when to do certain post-processing steps + bool m_bDoBloomAndToneMapping:1; + bool m_bDoDepthOfField:1; + bool m_bHDRTarget:1; + bool m_bDrawWorldNormal:1; + bool m_bCullFrontFaces:1; + + // Cached mode for certain full-scene per-frame varying state such as sun entity coverage + bool m_bCacheFullSceneState:1; + + bool m_bRenderFlashlightDepthTranslucents:1; +}; + + + +#endif // VIEW_SHARED_H diff --git a/public/vphysics/collision_set.h b/public/vphysics/collision_set.h new file mode 100644 index 0000000..249aef4 --- /dev/null +++ b/public/vphysics/collision_set.h @@ -0,0 +1,20 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// A set of collision rules +// NOTE: Defaults to all indices disabled +class IPhysicsCollisionSet +{ +public: + ~IPhysicsCollisionSet() {} + + virtual void EnableCollisions( int index0, int index1 ) = 0; + virtual void DisableCollisions( int index0, int index1 ) = 0; + + virtual bool ShouldCollide( int index0, int index1 ) = 0; +}; diff --git a/public/vphysics/constraints.h b/public/vphysics/constraints.h new file mode 100644 index 0000000..9bbe8db --- /dev/null +++ b/public/vphysics/constraints.h @@ -0,0 +1,353 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CONSTRAINTS_H +#define CONSTRAINTS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vphysics_interface.h" +#include "mathlib/mathlib.h" + +// constraint groups +struct constraint_groupparams_t +{ + int additionalIterations; // additional solver iterations make the constraint system more stable + int minErrorTicks; // minimum number of ticks with an error before it's reported + float errorTolerance; // error tolerance in HL units + + inline void Defaults() + { + additionalIterations = 0; + minErrorTicks = 15; + errorTolerance = 3.0f; + } +}; + +// Breakable constraints; +// +// forceLimit - kg * in / s limit (N * conversion(in/m)) +// torqueLimit - kg * in^2 / s (Nm * conversion(in^2/m^2)) + +// +// strength 0 - 1 +struct constraint_breakableparams_t +{ + float strength; // strength of the constraint 0.0 - 1.0 + float forceLimit; // constraint force limit to break (0 means never break) + float torqueLimit; // constraint torque limit to break (0 means never break) + float bodyMassScale[2]; // scale applied to mass of reference/attached object before solving constriant + bool isActive; + + inline void Defaults() + { + forceLimit = 0.0f; + torqueLimit = 0.0f; + strength = 1.0f; + bodyMassScale[0] = 1.0f; + bodyMassScale[1] = 1.0f; + isActive = true; + } +}; + +//----------------------------------------------------------------------------- +// Purpose: constraint limit on a single rotation axis +//----------------------------------------------------------------------------- +struct constraint_axislimit_t +{ + float minRotation; + float maxRotation; + float angularVelocity; // desired angular velocity around hinge + float torque; // torque to achieve angular velocity (use 0, torque for "friction") + + inline void SetAxisFriction( float rmin, float rmax, float friction ) + { + minRotation = rmin; + maxRotation = rmax; + angularVelocity = 0; + torque = friction; + } + inline void Defaults() + { + SetAxisFriction(0,0,0); + } +}; + +// Builds a transform which maps points in the input object's local space +// to the output object's local space +inline void BuildObjectRelativeXform( IPhysicsObject *pOutputSpace, IPhysicsObject *pInputSpace, matrix3x4_t &xformInToOut ) +{ + matrix3x4_t outInv, tmp, input; + pOutputSpace->GetPositionMatrix( &tmp ); + MatrixInvert( tmp, outInv ); + pInputSpace->GetPositionMatrix( &input ); + ConcatTransforms( outInv, input, xformInToOut ); +} + + +//----------------------------------------------------------------------------- +// Purpose: special limited ballsocket constraint for ragdolls. +// Has axis limits for all 3 axes. +//----------------------------------------------------------------------------- +struct constraint_ragdollparams_t +{ + constraint_breakableparams_t constraint; + matrix3x4_t constraintToReference;// xform constraint space to refobject space + matrix3x4_t constraintToAttached; // xform constraint space to attached object space + int parentIndex; // NOTE: only used for parsing. NEED NOT BE SET for create + int childIndex; // NOTE: only used for parsing. NEED NOT BE SET for create + + constraint_axislimit_t axes[3]; + bool onlyAngularLimits; // only angular limits (not translation as well?) + bool isActive; + bool useClockwiseRotations; // HACKHACK: Did this wrong in version one. Fix in the future. + + inline void Defaults() + { + constraint.Defaults(); + isActive = true; + SetIdentityMatrix( constraintToReference ); + SetIdentityMatrix( constraintToAttached ); + parentIndex = -1; + childIndex = -1; + axes[0].Defaults(); + axes[1].Defaults(); + axes[2].Defaults(); + onlyAngularLimits = false; + useClockwiseRotations = false; + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Used to init a hinge restricting the relative position and orientation +// of two objects to rotation around a single axis +//----------------------------------------------------------------------------- +struct constraint_hingeparams_t +{ + Vector worldPosition; // position in world space on the hinge axis + Vector worldAxisDirection; // unit direction vector of the hinge axis in world space + constraint_axislimit_t hingeAxis; + constraint_breakableparams_t constraint; + + inline void Defaults() + { + worldPosition.Init(); + worldAxisDirection.Init(); + hingeAxis.Defaults(); + constraint.Defaults(); + } +}; + +struct constraint_limitedhingeparams_t : public constraint_hingeparams_t +{ + Vector referencePerpAxisDirection; // unit direction vector vector perpendicular to the hinge axis in world space + Vector attachedPerpAxisDirection; // unit direction vector vector perpendicular to the hinge axis in world space + + constraint_limitedhingeparams_t() {} + constraint_limitedhingeparams_t( const constraint_hingeparams_t &hinge ) + { + static_cast(*this) = hinge; + referencePerpAxisDirection.Init(); + attachedPerpAxisDirection.Init(); + } + + inline void Defaults() + { + this->constraint_hingeparams_t::Defaults(); + referencePerpAxisDirection.Init(); + attachedPerpAxisDirection.Init(); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Used to init a constraint that fixes the position and orientation +// of two objects relative to each other (like glue) +//----------------------------------------------------------------------------- +struct constraint_fixedparams_t +{ + matrix3x4_t attachedRefXform; // xform attached object space to ref object space + constraint_breakableparams_t constraint; + + inline void InitWithCurrentObjectState( IPhysicsObject *pRef, IPhysicsObject *pAttached ) + { + BuildObjectRelativeXform( pRef, pAttached, attachedRefXform ); + } + + inline void Defaults() + { + SetIdentityMatrix( attachedRefXform ); + constraint.Defaults(); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Same parameters as fixed constraint, but torqueLimit has no effect +//----------------------------------------------------------------------------- +struct constraint_ballsocketparams_t +{ + Vector constraintPosition[2]; // position of the constraint in each object's space + constraint_breakableparams_t constraint; + inline void Defaults() + { + constraint.Defaults(); + constraintPosition[0].Init(); + constraintPosition[1].Init(); + } + + void InitWithCurrentObjectState( IPhysicsObject *pRef, IPhysicsObject *pAttached, const Vector &ballsocketOrigin ) + { + pRef->WorldToLocal( &constraintPosition[0], ballsocketOrigin ); + pAttached->WorldToLocal( &constraintPosition[1], ballsocketOrigin ); + } +}; + +struct constraint_slidingparams_t +{ + matrix3x4_t attachedRefXform; // xform attached object space to ref object space + Vector slideAxisRef; // unit direction vector of the slide axis in ref object space + constraint_breakableparams_t constraint; + // NOTE: if limitMin == limitMax there is NO limit set! + float limitMin; // minimum limit coordinate refAxisDirection space + float limitMax; // maximum limit coordinate refAxisDirection space + float friction; // friction on sliding + float velocity; // desired velocity + + inline void Defaults() + { + SetIdentityMatrix( attachedRefXform ); + slideAxisRef.Init(); + limitMin = limitMax = 0; + friction = 0; + velocity = 0; + constraint.Defaults(); + } + + inline void SetFriction( float inputFriction ) + { + friction = inputFriction; + velocity = 0; + } + + inline void SetLinearMotor( float inputVelocity, float maxForce ) + { + friction = maxForce; + velocity = inputVelocity; + } + + inline void InitWithCurrentObjectState( IPhysicsObject *pRef, IPhysicsObject *pAttached, const Vector &slideDirWorldspace ) + { + BuildObjectRelativeXform( pRef, pAttached, attachedRefXform ); + matrix3x4_t tmp; + pRef->GetPositionMatrix( &tmp ); + VectorIRotate( slideDirWorldspace, tmp, slideAxisRef ); + } +}; + +struct constraint_pulleyparams_t +{ + constraint_breakableparams_t constraint; + Vector pulleyPosition[2]; // These are the pulley positions for the reference and attached objects in world space + Vector objectPosition[2]; // local positions of attachments to the ref,att objects + float totalLength; // total rope length (include gearing!) + float gearRatio; // gearing affects attached object ALWAYS + bool isRigid; + + inline void Defaults() + { + constraint.Defaults(); + totalLength = 1.0; + gearRatio = 1.0; + pulleyPosition[0].Init(); + pulleyPosition[1].Init(); + objectPosition[0].Init(); + objectPosition[1].Init(); + isRigid = false; + } +}; + + +struct constraint_lengthparams_t +{ + constraint_breakableparams_t constraint; + Vector objectPosition[2]; // These are the positions for the reference and attached objects in local space + float totalLength; // Length of rope/spring/constraint. Distance to maintain + float minLength; // if rigid, objects are not allowed to move closer than totalLength either + + void InitWorldspace( IPhysicsObject *pRef, IPhysicsObject *pAttached, const Vector &refPosition, const Vector &attachedPosition, bool rigid = false ) + { + pRef->WorldToLocal( &objectPosition[0], refPosition ); + pAttached->WorldToLocal( &objectPosition[1], attachedPosition ); + totalLength = (refPosition - attachedPosition).Length(); + minLength = rigid ? totalLength : 0; + } + + void Init( IPhysicsObject *pRef, IPhysicsObject *pAttached, float flLength, bool rigid = false ) + { + objectPosition[0] = vec3_origin; + objectPosition[1] = vec3_origin; + totalLength = flLength; + minLength = rigid ? totalLength : 0; + } + + inline void Defaults() + { + constraint.Defaults(); + objectPosition[0].Init(); + objectPosition[1].Init(); + totalLength = 1; + minLength = 0; + } +}; + +class IPhysicsConstraint +{ +public: + virtual ~IPhysicsConstraint( void ) {} + + // NOTE: Constraints are active when created. You can temporarily enable/disable them with these functions + virtual void Activate( void ) = 0; + virtual void Deactivate( void ) = 0; + + // set a pointer to the game object + virtual void SetGameData( void *gameData ) = 0; + + // get a pointer to the game object + virtual void *GetGameData( void ) const = 0; + + // Get the parent/referenced object + virtual IPhysicsObject *GetReferenceObject( void ) const = 0; + + // Get the attached object + virtual IPhysicsObject *GetAttachedObject( void ) const = 0; + + virtual void SetLinearMotor( float speed, float maxLinearImpulse ) = 0; + virtual void SetAngularMotor( float rotSpeed, float maxAngularImpulse ) = 0; + + virtual void UpdateRagdollTransforms( const matrix3x4_t &constraintToReference, const matrix3x4_t &constraintToAttached ) = 0; + virtual bool GetConstraintTransform( matrix3x4_t *pConstraintToReference, matrix3x4_t *pConstraintToAttached ) const = 0; + virtual bool GetConstraintParams( constraint_breakableparams_t *pParams ) const = 0; + + virtual void OutputDebugInfo() = 0; +}; + + +class IPhysicsConstraintGroup +{ +public: + virtual ~IPhysicsConstraintGroup( void ) {} + virtual void Activate() = 0; + virtual bool IsInErrorState() = 0; + virtual void ClearErrorState() = 0; + virtual void GetErrorParams( constraint_groupparams_t *pParams ) = 0; + virtual void SetErrorParams( const constraint_groupparams_t ¶ms ) = 0; + virtual void SolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1 ) = 0; +}; + + +#endif // CONSTRAINTS_H diff --git a/public/vphysics/friction.h b/public/vphysics/friction.h new file mode 100644 index 0000000..ef45a68 --- /dev/null +++ b/public/vphysics/friction.h @@ -0,0 +1,51 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef FRICTION_H +#define FRICTION_H +#ifdef _WIN32 +#pragma once +#endif + +// NOTE: This is an iterator for the contact points on an object +// NOTE: This should only be used temporarily. Holding one of these +// NOTE: across collision callbacks or calls into simulation will cause errors! +// NOTE: VPHYSICS may choose to make the data contained within this object invalid +// NOTE: any time simulation is run. +class IPhysicsFrictionSnapshot +{ +public: + virtual ~IPhysicsFrictionSnapshot() {} + + virtual bool IsValid() = 0; + + // Object 0 is this object, Object 1 is the other object + virtual IPhysicsObject *GetObject( int index ) = 0; + virtual int GetMaterial( int index ) = 0; + + virtual void GetContactPoint( Vector &out ) = 0; + + // points away from source object + virtual void GetSurfaceNormal( Vector &out ) = 0; + virtual float GetNormalForce() = 0; + virtual float GetEnergyAbsorbed() = 0; + + // recompute friction (useful if dynamically altering materials/mass) + virtual void RecomputeFriction() = 0; + // clear all friction force at this contact point + virtual void ClearFrictionForce() = 0; + + virtual void MarkContactForDelete() = 0; + virtual void DeleteAllMarkedContacts( bool wakeObjects ) = 0; + + // Move to the next friction data for this object + virtual void NextFrictionData() = 0; + virtual float GetFrictionCoefficient() = 0; +}; + + + +#endif // FRICTION_H diff --git a/public/vphysics/object_hash.h b/public/vphysics/object_hash.h new file mode 100644 index 0000000..e44de53 --- /dev/null +++ b/public/vphysics/object_hash.h @@ -0,0 +1,29 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef OBJECT_HASH_H +#define OBJECT_HASH_H +#ifdef _WIN32 +#pragma once +#endif + +class IPhysicsObjectPairHash +{ +public: + virtual ~IPhysicsObjectPairHash() {} + virtual void AddObjectPair( void *pObject0, void *pObject1 ) = 0; + virtual void RemoveObjectPair( void *pObject0, void *pObject1 ) = 0; + virtual bool IsObjectPairInHash( void *pObject0, void *pObject1 ) = 0; + virtual void RemoveAllPairsForObject( void *pObject0 ) = 0; + virtual bool IsObjectInHash( void *pObject0 ) = 0; + + // Used to iterate over all pairs an object is part of + virtual int GetPairCountForObject( void *pObject0 ) = 0; + virtual int GetPairListForObject( void *pObject0, int nMaxCount, void **ppObjectList ) = 0; +}; + + +#endif // OBJECT_HASH_H diff --git a/public/vphysics/performance.h b/public/vphysics/performance.h new file mode 100644 index 0000000..7fc0ee6 --- /dev/null +++ b/public/vphysics/performance.h @@ -0,0 +1,40 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PERFORMANCE_H +#define PERFORMANCE_H +#ifdef _WIN32 +#pragma once +#endif + +const float DEFAULT_MIN_FRICTION_MASS = 10.0f; +const float DEFAULT_MAX_FRICTION_MASS = 2500.0f; +struct physics_performanceparams_t +{ + int maxCollisionsPerObjectPerTimestep; // object will be frozen after this many collisions (visual hitching vs. CPU cost) + int maxCollisionChecksPerTimestep; // objects may penetrate after this many collision checks (can be extended in AdditionalCollisionChecksThisTick) + float maxVelocity; // limit world space linear velocity to this (in / s) + float maxAngularVelocity; // limit world space angular velocity to this (degrees / s) + float lookAheadTimeObjectsVsWorld; // predict collisions this far (seconds) into the future + float lookAheadTimeObjectsVsObject; // predict collisions this far (seconds) into the future + float minFrictionMass; // min mass for friction solves (constrains dynamic range of mass to improve stability) + float maxFrictionMass; // mas mass for friction solves + + void Defaults() + { + maxCollisionsPerObjectPerTimestep = 6; + maxCollisionChecksPerTimestep = 250; + maxVelocity = 2000.0f; + maxAngularVelocity = 360.0f * 10.0f; + lookAheadTimeObjectsVsWorld = 1.0f; + lookAheadTimeObjectsVsObject = 0.5f; + minFrictionMass = DEFAULT_MIN_FRICTION_MASS; + maxFrictionMass = DEFAULT_MAX_FRICTION_MASS; + } +}; + + +#endif // PERFORMANCE_H diff --git a/public/vphysics/player_controller.h b/public/vphysics/player_controller.h new file mode 100644 index 0000000..2f7749c --- /dev/null +++ b/public/vphysics/player_controller.h @@ -0,0 +1,47 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef PLAYER_CONTROLLER_H +#define PLAYER_CONTROLLER_H +#ifdef _WIN32 +#pragma once +#endif + +class IPhysicsPlayerControllerEvent +{ +public: + virtual int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position ) = 0; +}; + +class IPhysicsPlayerController +{ +public: + virtual ~IPhysicsPlayerController( void ) {} + + virtual void Update( const Vector &position, const Vector &velocity, float secondsToArrival, bool onground, IPhysicsObject *ground ) = 0; + virtual void SetEventHandler( IPhysicsPlayerControllerEvent *handler ) = 0; + virtual bool IsInContact( void ) = 0; + virtual void MaxSpeed( const Vector &maxVelocity ) = 0; + + // allows game code to change collision models + virtual void SetObject( IPhysicsObject *pObject ) = 0; + // UNDONE: Refactor this and shadow controllers into a single class/interface through IPhysicsObject + virtual int GetShadowPosition( Vector *position, QAngle *angles ) = 0; + virtual void StepUp( float height ) = 0; + virtual void Jump() = 0; + virtual void GetShadowVelocity( Vector *velocity ) = 0; + virtual IPhysicsObject *GetObject() = 0; + virtual void GetLastImpulse( Vector *pOut ) = 0; + + virtual void SetPushMassLimit( float maxPushMass ) = 0; + virtual void SetPushSpeedLimit( float maxPushSpeed ) = 0; + + virtual float GetPushMassLimit() = 0; + virtual float GetPushSpeedLimit() = 0; + virtual bool WasFrozen() = 0; +}; + +#endif // PLAYER_CONTROLLER_H diff --git a/public/vphysics/stats.h b/public/vphysics/stats.h new file mode 100644 index 0000000..f1a596e --- /dev/null +++ b/public/vphysics/stats.h @@ -0,0 +1,40 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef STATS_H +#define STATS_H +#ifdef _WIN32 +#pragma once +#endif + +// internal counters to measure cost of physics simulation +struct physics_stats_t +{ + float maxRescueSpeed; + float maxSpeedGain; + + int impactSysNum; + int impactCounter; + int impactSumSys; + int impactHardRescueCount; + int impactRescueAfterCount; + int impactDelayedCount; + int impactCollisionChecks; + int impactStaticCount; + + double totalEnergyDestroyed; + int collisionPairsTotal; + int collisionPairsCreated; + int collisionPairsDestroyed; + + int potentialCollisionsObjectVsObject; + int potentialCollisionsObjectVsWorld; + + int frictionEventsProcessed; +}; + + +#endif // STATS_H diff --git a/public/vphysics/vehicles.h b/public/vphysics/vehicles.h new file mode 100644 index 0000000..f09811c --- /dev/null +++ b/public/vphysics/vehicles.h @@ -0,0 +1,250 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VEHICLES_H +#define VEHICLES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "datamap.h" + + +#define VEHICLE_TYPE_CAR_WHEELS (1<<0) +#define VEHICLE_TYPE_CAR_RAYCAST (1<<1) +#define VEHICLE_TYPE_JETSKI_RAYCAST (1<<2) +#define VEHICLE_TYPE_AIRBOAT_RAYCAST (1<<3) + +#define VEHICLE_MAX_AXLE_COUNT 4 +#define VEHICLE_MAX_GEAR_COUNT 6 +#define VEHICLE_MAX_WHEEL_COUNT (2*VEHICLE_MAX_AXLE_COUNT) + +#define VEHICLE_TIRE_NORMAL 0 +#define VEHICLE_TIRE_BRAKING 1 +#define VEHICLE_TIRE_POWERSLIDE 2 + +struct vehicle_controlparams_t +{ + float throttle; + float steering; + float brake; + float boost; + bool handbrake; + bool handbrakeLeft; + bool handbrakeRight; + bool brakepedal; + bool bHasBrakePedal; + bool bAnalogSteering; +}; + +struct vehicle_operatingparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + float speed; + float engineRPM; + int gear; + float boostDelay; + int boostTimeLeft; + float skidSpeed; + int skidMaterial; + float steeringAngle; + int wheelsNotInContact; + int wheelsInContact; + bool isTorqueBoosting; +}; + +// Debug! +#define VEHICLE_DEBUGRENDERDATA_MAX_WHEELS 10 +#define VEHICLE_DEBUGRENDERDATA_MAX_AXLES 3 + +struct vehicle_debugcarsystem_t +{ + Vector vecAxlePos[VEHICLE_DEBUGRENDERDATA_MAX_AXLES]; + + Vector vecWheelPos[VEHICLE_DEBUGRENDERDATA_MAX_WHEELS]; + Vector vecWheelRaycasts[VEHICLE_DEBUGRENDERDATA_MAX_WHEELS][2]; + Vector vecWheelRaycastImpacts[VEHICLE_DEBUGRENDERDATA_MAX_WHEELS]; +}; + +struct vehicleparams_t; + +class IPhysicsVehicleController +{ +public: + virtual ~IPhysicsVehicleController() {} + // call this from the game code with the control parameters + virtual void Update( float dt, vehicle_controlparams_t &controls ) = 0; + virtual const vehicle_operatingparams_t &GetOperatingParams() = 0; + virtual const vehicleparams_t &GetVehicleParams() = 0; + virtual vehicleparams_t &GetVehicleParamsForChange() = 0; + virtual float UpdateBooster(float dt) = 0; + virtual int GetWheelCount(void) = 0; + virtual IPhysicsObject *GetWheel(int index) = 0; + virtual bool GetWheelContactPoint( int index, Vector *pContactPoint, int *pSurfaceProps ) = 0; + virtual void SetSpringLength(int wheelIndex, float length) = 0; + virtual void SetWheelFriction(int wheelIndex, float friction) = 0; + + virtual void OnVehicleEnter( void ) = 0; + virtual void OnVehicleExit( void ) = 0; + + virtual void SetEngineDisabled( bool bDisable ) = 0; + virtual bool IsEngineDisabled( void ) = 0; + + // Debug + virtual void GetCarSystemDebugData( vehicle_debugcarsystem_t &debugCarSystem ) = 0; + virtual void VehicleDataReload() = 0; +}; + + +// parameters for the body object control of the vehicle +struct vehicle_bodyparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + Vector massCenterOverride; // leave at vec3_origin for no override + float massOverride; // leave at 0 for no override + float addGravity; // keeps car down + float tiltForce; // keeps car down when not on flat ground + float tiltForceHeight; // where the tilt force pulls relative to center of mass + float counterTorqueFactor; + float keepUprightTorque; + float maxAngularVelocity; // clamp the car angular velocity separately from other objects to keep stable +}; + +// wheel objects are created by vphysics, these are the parameters for those objects +// NOTE: They are paired, so only one set of parameters is necessary per axle +struct vehicle_wheelparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + float radius; + float mass; + float inertia; + float damping; // usually 0 + float rotdamping; // usually 0 + float frictionScale; // 1.5 front, 1.8 rear + int materialIndex; + int brakeMaterialIndex; + int skidMaterialIndex; + float springAdditionalLength; // 0 means the spring is at it's rest length +}; + +struct vehicle_suspensionparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + float springConstant; + float springDamping; + float stabilizerConstant; + float springDampingCompression; + float maxBodyForce; +}; + +// NOTE: both raytrace and wheel data here because jetski uses both. +struct vehicle_axleparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + Vector offset; // center of this axle in vehicle object space + Vector wheelOffset; // offset to wheel (assume other wheel is symmetric at -wheelOffset) from axle center + Vector raytraceCenterOffset; // offset to center of axle for the raytrace data. + Vector raytraceOffset; // offset to raytrace for non-wheel (some wheeled) vehicles + vehicle_wheelparams_t wheels; + vehicle_suspensionparams_t suspension; + float torqueFactor; // normalized to 1 across all axles + // e.g. 0,1 for rear wheel drive - 0.5,0.5 for 4 wheel drive + float brakeFactor; // normalized to 1 across all axles +}; + +struct vehicle_steeringparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + float degreesSlow; // angle in degrees of steering at slow speed + float degreesFast; // angle in degrees of steering at fast speed + float degreesBoost; // angle in degrees of steering at fast speed + float steeringRateSlow; // this is the speed the wheels are steered when the vehicle is slow + float steeringRateFast; // this is the speed the wheels are steered when the vehicle is "fast" + float steeringRestRateSlow; // this is the speed at which the wheels move toward their resting state (straight ahead) at slow speed + float steeringRestRateFast; // this is the speed at which the wheels move toward their resting state (straight ahead) at fast speed + float speedSlow; // this is the max speed of "slow" + float speedFast; // this is the min speed of "fast" + float turnThrottleReduceSlow; // this is the amount of throttle reduction to apply at the maximum steering angle + float turnThrottleReduceFast; // this is the amount of throttle reduction to apply at the maximum steering angle + float brakeSteeringRateFactor; // this scales the steering rate when the brake/handbrake is down + float throttleSteeringRestRateFactor; // this scales the steering rest rate when the throttle is down + float powerSlideAccel; // scale of speed to acceleration + float boostSteeringRestRateFactor; // this scales the steering rest rate when boosting + float boostSteeringRateFactor; // this scales the steering rest rate when boosting + float steeringExponent; // this makes the steering response non-linear. The steering function is linear, then raised to this power + + bool isSkidAllowed; // true/false skid flag + bool dustCloud; // flag for creating a dustcloud behind vehicle +}; + +struct vehicle_engineparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + float horsepower; + float maxSpeed; + float maxRevSpeed; + float maxRPM; // redline RPM limit + float axleRatio; // ratio of engine rev to axle rev + float throttleTime; // time to reach full throttle in seconds + + // transmission + int gearCount; // gear count - max 10 + float gearRatio[VEHICLE_MAX_GEAR_COUNT]; // ratio for each gear + + // automatic transmission (simple auto-shifter - switches at fixed RPM limits) + float shiftUpRPM; // max RPMs to switch to a higher gear + float shiftDownRPM; // min RPMs to switch to a lower gear + float boostForce; + float boostDuration; + float boostDelay; + float boostMaxSpeed; + float autobrakeSpeedGain; + float autobrakeSpeedFactor; + bool torqueBoost; + bool isAutoTransmission; // true for auto, false for manual +}; + +struct vehicleparams_t +{ + DECLARE_SIMPLE_DATADESC(); + + int axleCount; + int wheelsPerAxle; + vehicle_bodyparams_t body; + vehicle_axleparams_t axles[VEHICLE_MAX_AXLE_COUNT]; + vehicle_engineparams_t engine; + vehicle_steeringparams_t steering; +}; + +// Iterator for queries +class CPassengerSeatTransition; +typedef CUtlVector< CPassengerSeatTransition> PassengerSeatAnims_t; + +// Seat query types +enum VehicleSeatQuery_e +{ + VEHICLE_SEAT_ANY, // Any available seat for our role + VEHICLE_SEAT_NEAREST, // Seat closest to our starting point +}; + +// Seat anim types for return +enum PassengerSeatAnimType_t +{ + PASSENGER_SEAT_ENTRY, + PASSENGER_SEAT_EXIT +}; + +#define VEHICLE_SEAT_INVALID -1 // An invalid seat + +#endif // VEHICLES_H diff --git a/public/vphysics/virtualmesh.h b/public/vphysics/virtualmesh.h new file mode 100644 index 0000000..08c3df5 --- /dev/null +++ b/public/vphysics/virtualmesh.h @@ -0,0 +1,46 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef VIRTUALMESH_H +#define VIRTUALMESH_H +#ifdef _WIN32 +#pragma once +#endif + +// NOTE: These are fixed length to make it easy to fill these out without memory allocation or storage +const int MAX_VIRTUAL_TRIANGLES = 1024; +struct virtualmeshlist_t +{ + Vector *pVerts; + int indexCount; + int triangleCount; + int vertexCount; + int surfacePropsIndex; + byte *pHull; + unsigned short indices[MAX_VIRTUAL_TRIANGLES*3]; +}; + +struct virtualmeshtrianglelist_t +{ + int triangleCount; + unsigned short triangleIndices[MAX_VIRTUAL_TRIANGLES*3]; +}; + +class IVirtualMeshEvent +{ +public: + virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList ) = 0; + virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs ) = 0; + virtual void GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList ) = 0; +}; +struct virtualmeshparams_t +{ + IVirtualMeshEvent *pMeshEventHandler; + void *userData; + bool buildOuterHull; +}; + +#endif // VIRTUALMESH_H diff --git a/public/vphysics_interface.h b/public/vphysics_interface.h new file mode 100644 index 0000000..11a2236 --- /dev/null +++ b/public/vphysics_interface.h @@ -0,0 +1,1191 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Public interfaces to vphysics DLL +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VPHYSICS_INTERFACE_H +#define VPHYSICS_INTERFACE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include "tier1/interface.h" +#include "appframework/IAppSystem.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "vcollide.h" +#include "tier3/tier3.h" + +// ------------------------------------------------------------------------------------ +// UNITS: +// ------------------------------------------------------------------------------------ +// NOTE: Coordinates are in HL units. 1 unit == 1 inch. X is east (forward), Y is north (left), Z is up (up) +// QAngle are pitch (around y), Yaw (around Z), Roll (around X) +// AngularImpulse are exponetial maps (an axis in HL units scaled by a "twist" angle in degrees) +// They can be transformed like normals/covectors and added linearly +// mass is kg, volume is in^3, acceleration is in/s^2, velocity is in/s + +// density is kg/m^3 (water ~= 998 at room temperature) +// preferably, these would be in kg/in^3, but the range of those numbers makes them not very human readable +// having water be about 1000 is really convenient for data entry. +// Since volume is in in^3 and density is in kg/m^3: +// density = (mass / volume) * CUBIC_METERS_PER_CUBIC_INCH +// Force is applied using impulses (kg*in/s) +// Torque is applied using impulses (kg*degrees/s) +// ------------------------------------------------------------------------------------ + +#define METERS_PER_INCH (0.0254f) +#define CUBIC_METERS_PER_CUBIC_INCH (METERS_PER_INCH*METERS_PER_INCH*METERS_PER_INCH) +// 2.2 lbs / kg +#define POUNDS_PER_KG (2.2f) +#define KG_PER_POUND (1.0f/POUNDS_PER_KG) + +// convert from pounds to kg +#define lbs2kg(x) ((x)*KG_PER_POUND) +#define kg2lbs(x) ((x)*POUNDS_PER_KG) + +const float VPHYSICS_MIN_MASS = 0.1f; +const float VPHYSICS_MAX_MASS = 5e4f; + +class IPhysicsObject; +class IPhysicsEnvironment; +class IPhysicsSurfaceProps; +class IPhysicsConstraint; +class IPhysicsConstraintGroup; +class IPhysicsFluidController; +class IPhysicsSpring; +class IPhysicsVehicleController; +class IConvexInfo; +class IPhysicsObjectPairHash; +class IPhysicsCollisionSet; +class IPhysicsPlayerController; +class IPhysicsFrictionSnapshot; + +struct Ray_t; +struct constraint_ragdollparams_t; +struct constraint_hingeparams_t; +struct constraint_fixedparams_t; +struct constraint_ballsocketparams_t; +struct constraint_slidingparams_t; +struct constraint_pulleyparams_t; +struct constraint_lengthparams_t; +struct constraint_groupparams_t; + +struct vehicleparams_t; +struct matrix3x4_t; + +struct fluidparams_t; +struct springparams_t; +struct objectparams_t; +struct debugcollide_t; +class CGameTrace; +typedef CGameTrace trace_t; +struct physics_stats_t; +struct physics_performanceparams_t; +struct virtualmeshparams_t; + +//enum PhysInterfaceId_t; +struct physsaveparams_t; +struct physrestoreparams_t; +struct physprerestoreparams_t; + +enum PhysInterfaceId_t +{ + PIID_UNKNOWN, + PIID_IPHYSICSOBJECT, + PIID_IPHYSICSFLUIDCONTROLLER, + PIID_IPHYSICSSPRING, + PIID_IPHYSICSCONSTRAINTGROUP, + PIID_IPHYSICSCONSTRAINT, + PIID_IPHYSICSSHADOWCONTROLLER, + PIID_IPHYSICSPLAYERCONTROLLER, + PIID_IPHYSICSMOTIONCONTROLLER, + PIID_IPHYSICSVEHICLECONTROLLER, + PIID_IPHYSICSGAMETRACE, + + PIID_NUM_TYPES +}; + + +class ISave; +class IRestore; + + +#define VPHYSICS_DEBUG_OVERLAY_INTERFACE_VERSION "VPhysicsDebugOverlay001" + +abstract_class IVPhysicsDebugOverlay +{ +public: + virtual void AddEntityTextOverlay(int ent_index, int line_offset, float duration, int r, int g, int b, int a, const char *format, ...) = 0; + virtual void AddBoxOverlay(const Vector& origin, const Vector& mins, const Vector& max, QAngle const& orientation, int r, int g, int b, int a, float duration) = 0; + virtual void AddTriangleOverlay(const Vector& p1, const Vector& p2, const Vector& p3, int r, int g, int b, int a, bool noDepthTest, float duration) = 0; + virtual void AddLineOverlay(const Vector& origin, const Vector& dest, int r, int g, int b,bool noDepthTest, float duration) = 0; + virtual void AddTextOverlay(const Vector& origin, float duration, const char *format, ...) = 0; + virtual void AddTextOverlay(const Vector& origin, int line_offset, float duration, const char *format, ...) = 0; + virtual void AddScreenTextOverlay(float flXPos, float flYPos,float flDuration, int r, int g, int b, int a, const char *text) = 0; + virtual void AddSweptBoxOverlay(const Vector& start, const Vector& end, const Vector& mins, const Vector& max, const QAngle & angles, int r, int g, int b, int a, float flDuration) = 0; + virtual void AddTextOverlayRGB(const Vector& origin, int line_offset, float duration, float r, float g, float b, float alpha, const char *format, ...) = 0; +}; + +#define VPHYSICS_INTERFACE_VERSION "VPhysics031" + +abstract_class IPhysics : public IAppSystem +{ +public: + virtual IPhysicsEnvironment *CreateEnvironment( void ) = 0; + virtual void DestroyEnvironment( IPhysicsEnvironment * ) = 0; + virtual IPhysicsEnvironment *GetActiveEnvironmentByIndex( int index ) = 0; + + // Creates a fast hash of pairs of objects + // Useful for maintaining a table of object relationships like pairs that do not collide. + virtual IPhysicsObjectPairHash *CreateObjectPairHash() = 0; + virtual void DestroyObjectPairHash( IPhysicsObjectPairHash *pHash ) = 0; + + // holds a cache of these by id. So you can get by id to search for the previously created set + // UNDONE: Sets are currently limited to 32 elements. More elements will return NULL in create. + // NOTE: id is not allowed to be zero. + virtual IPhysicsCollisionSet *FindOrCreateCollisionSet( unsigned int id, int maxElementCount ) = 0; + virtual IPhysicsCollisionSet *FindCollisionSet( unsigned int id ) = 0; + virtual void DestroyAllCollisionSets() = 0; +}; + + +// CPhysConvex is a single convex solid +class CPhysConvex; +// CPhysPolysoup is an abstract triangle soup mesh +class CPhysPolysoup; +class ICollisionQuery; +class IVPhysicsKeyParser; +struct convertconvexparams_t; +class CPackedPhysicsDescription; + +class CPolyhedron; + +// UNDONE: Find a better place for this? Should be in collisionutils, but it's needs VPHYSICS' solver. +struct truncatedcone_t +{ + Vector origin; + Vector normal; + float h; // height of the cone (hl units) + float theta; // cone angle (degrees) +}; + + +abstract_class IPhysicsCollision +{ +public: + virtual ~IPhysicsCollision( void ) {} + + // produce a convex element from verts (convex hull around verts) + virtual CPhysConvex *ConvexFromVerts( Vector **pVerts, int vertCount ) = 0; + // produce a convex element from planes (csg of planes) + virtual CPhysConvex *ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance ) = 0; + // calculate volume of a convex element + virtual float ConvexVolume( CPhysConvex *pConvex ) = 0; + + virtual float ConvexSurfaceArea( CPhysConvex *pConvex ) = 0; + // store game-specific data in a convex solid + virtual void SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData ) = 0; + // If not converted, free the convex elements with this call + virtual void ConvexFree( CPhysConvex *pConvex ) = 0; + virtual CPhysConvex *BBoxToConvex( const Vector &mins, const Vector &maxs ) = 0; + // produce a convex element from a convex polyhedron + virtual CPhysConvex *ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron ) = 0; + // produce a set of convex triangles from a convex polygon, normal is assumed to be on the side with forward point ordering, which should be clockwise, output will need to be able to hold exactly (iPointCount-2) convexes + virtual void ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput ) = 0; + + // concave objects + // create a triangle soup + virtual CPhysPolysoup *PolysoupCreate( void ) = 0; + // destroy the container and memory + virtual void PolysoupDestroy( CPhysPolysoup *pSoup ) = 0; + // add a triangle to the soup + virtual void PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits ) = 0; + // convert the convex into a compiled collision model + virtual CPhysCollide *ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP ) = 0; + + // Convert an array of convex elements to a compiled collision model (this deletes the convex elements) + virtual CPhysCollide *ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount ) = 0; + virtual CPhysCollide *ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams ) = 0; + // Free a collide that was created with ConvertConvexToCollide() + virtual void DestroyCollide( CPhysCollide *pCollide ) = 0; + + // Get the memory size in bytes of the collision model for serialization + virtual int CollideSize( CPhysCollide *pCollide ) = 0; + // serialize the collide to a block of memory + virtual int CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap = false ) = 0; + // unserialize the collide from a block of memory + virtual CPhysCollide *UnserializeCollide( char *pBuffer, int size, int index ) = 0; + + // compute the volume of a collide + virtual float CollideVolume( CPhysCollide *pCollide ) = 0; + // compute surface area for tools + virtual float CollideSurfaceArea( CPhysCollide *pCollide ) = 0; + + // Get the support map for a collide in the given direction + virtual Vector CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction ) = 0; + + // Get an AABB for an oriented collision model + virtual void CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles ) = 0; + + virtual void CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter ) = 0; + virtual void CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter ) = 0; + // get the approximate cross-sectional area projected orthographically on the bbox of the collide + // NOTE: These are fractional areas - unitless. Basically this is the fraction of the OBB on each axis that + // would be visible if the object were rendered orthographically. + // NOTE: This has been precomputed when the collide was built or this function will return 1,1,1 + virtual Vector CollideGetOrthographicAreas( const CPhysCollide *pCollide ) = 0; + virtual void CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas ) = 0; + + // query the vcollide index in the physics model for the instance + virtual int CollideIndex( const CPhysCollide *pCollide ) = 0; + + // Convert a bbox to a collide + virtual CPhysCollide *BBoxToCollide( const Vector &mins, const Vector &maxs ) = 0; + virtual int GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit ) = 0; + + + // Trace an AABB against a collide + virtual void TraceBox( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) = 0; + virtual void TraceBox( const Ray_t &ray, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) = 0; + virtual void TraceBox( const Ray_t &ray, unsigned int contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) = 0; + + // Trace one collide against another + virtual void TraceCollide( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) = 0; + + // relatively slow test for box vs. truncated cone + virtual bool IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone ) = 0; + + // loads a set of solids into a vcollide_t + virtual void VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int size, bool swap = false ) = 0; + // destroyts the set of solids created by VCollideLoad + virtual void VCollideUnload( vcollide_t *pVCollide ) = 0; + + // begins parsing a vcollide. NOTE: This keeps pointers to the text + // If you free the text and call members of IVPhysicsKeyParser, it will crash + virtual IVPhysicsKeyParser *VPhysicsKeyParserCreate( const char *pKeyData ) = 0; + virtual IVPhysicsKeyParser *VPhysicsKeyParserCreate( vcollide_t *pVCollide ) = 0; + // Free the parser created by VPhysicsKeyParserCreate + virtual void VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser ) = 0; + + // creates a list of verts from a collision mesh + virtual int CreateDebugMesh( CPhysCollide const *pCollisionModel, Vector **outVerts ) = 0; + // destroy the list of verts created by CreateDebugMesh + virtual void DestroyDebugMesh( int vertCount, Vector *outVerts ) = 0; + + // create a queryable version of the collision model + virtual ICollisionQuery *CreateQueryModel( CPhysCollide *pCollide ) = 0; + // destroy the queryable version + virtual void DestroyQueryModel( ICollisionQuery *pQuery ) = 0; + + virtual IPhysicsCollision *ThreadContextCreate( void ) = 0; + virtual void ThreadContextDestroy( IPhysicsCollision *pThreadContex ) = 0; + + virtual CPhysCollide *CreateVirtualMesh( const virtualmeshparams_t ¶ms ) = 0; + virtual bool SupportsVirtualMesh() = 0; + + + virtual bool GetBBoxCacheSize( int *pCachedSize, int *pCachedCount ) = 0; + + + // extracts a polyhedron that defines a CPhysConvex's shape + virtual CPolyhedron *PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron ) = 0; + + // dumps info about the collide to Msg() + virtual void OutputDebugInfo( const CPhysCollide *pCollide ) = 0; + virtual unsigned int ReadStat( int statID ) = 0; + + // Get an AABB for an oriented collision model + virtual float CollideGetRadius( const CPhysCollide *pCollide ) = 0; + + virtual void *VCollideAllocUserData( vcollide_t *pVCollide, size_t userDataSize ) = 0; + virtual void VCollideFreeUserData( vcollide_t *pVCollide ) = 0; + virtual void VCollideCheck( vcollide_t *pVCollide, const char *pName ) = 0; +}; + + + +// this can be used to post-process a collision model +abstract_class ICollisionQuery +{ +public: + virtual ~ICollisionQuery() {} + // number of convex pieces in the whole solid + virtual int ConvexCount( void ) = 0; + // triangle count for this convex piece + virtual int TriangleCount( int convexIndex ) = 0; + // get the stored game data + virtual unsigned int GetGameData( int convexIndex ) = 0; + // Gets the triangle's verts to an array + virtual void GetTriangleVerts( int convexIndex, int triangleIndex, Vector *verts ) = 0; + + // UNDONE: This doesn't work!!! + virtual void SetTriangleVerts( int convexIndex, int triangleIndex, const Vector *verts ) = 0; + + // returns the 7-bit material index + virtual int GetTriangleMaterialIndex( int convexIndex, int triangleIndex ) = 0; + // sets a 7-bit material index for this triangle + virtual void SetTriangleMaterialIndex( int convexIndex, int triangleIndex, int index7bits ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Ray traces from game engine. +//----------------------------------------------------------------------------- +abstract_class IPhysicsGameTrace +{ +public: + virtual void VehicleTraceRay( const Ray_t &ray, void *pVehicle, trace_t *pTrace ) = 0; + virtual void VehicleTraceRayWithWater( const Ray_t &ray, void *pVehicle, trace_t *pTrace ) = 0; + virtual bool VehiclePointInWater( const Vector &vecPoint ) = 0; +}; + +// The caller should implement this to return contents masks per convex on a collide +abstract_class IConvexInfo +{ +public: + virtual unsigned int GetContents( int convexGameData ) = 0; +}; + +class CPhysicsEventHandler; +abstract_class IPhysicsCollisionData +{ +public: + virtual void GetSurfaceNormal( Vector &out ) = 0; // normal points toward second object (object index 1) + virtual void GetContactPoint( Vector &out ) = 0; // contact point of collision (in world space) + virtual void GetContactSpeed( Vector &out ) = 0; // speed of surface 1 relative to surface 0 (in world space) +}; + + +struct vcollisionevent_t +{ + IPhysicsObject *pObjects[2]; + int surfaceProps[2]; + bool isCollision; + bool isShadowCollision; + float deltaCollisionTime; + + float collisionSpeed; // only valid at postCollision + IPhysicsCollisionData *pInternalData; // may change pre/post collision +}; + +abstract_class IPhysicsCollisionEvent +{ +public: + // returns the two objects that collided, time between last collision of these objects + // and an opaque data block of collision information + // NOTE: PreCollision/PostCollision ALWAYS come in matched pairs!!! + virtual void PreCollision( vcollisionevent_t *pEvent ) = 0; + virtual void PostCollision( vcollisionevent_t *pEvent ) = 0; + + // This is a scrape event. The object has scraped across another object consuming the indicated energy + virtual void Friction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit, IPhysicsCollisionData *pData ) = 0; + + virtual void StartTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData ) = 0; + virtual void EndTouch( IPhysicsObject *pObject1, IPhysicsObject *pObject2, IPhysicsCollisionData *pTouchData ) = 0; + + virtual void FluidStartTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid ) = 0; + virtual void FluidEndTouch( IPhysicsObject *pObject, IPhysicsFluidController *pFluid ) = 0; + + virtual void PostSimulationFrame() = 0; + + virtual void ObjectEnterTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {} + virtual void ObjectLeaveTrigger( IPhysicsObject *pTrigger, IPhysicsObject *pObject ) {} +}; + + +abstract_class IPhysicsObjectEvent +{ +public: + // these can be used to optimize out queries on sleeping objects + // Called when an object is woken after sleeping + virtual void ObjectWake( IPhysicsObject *pObject ) = 0; + // called when an object goes to sleep (no longer simulating) + virtual void ObjectSleep( IPhysicsObject *pObject ) = 0; +}; + +abstract_class IPhysicsConstraintEvent +{ +public: + // the constraint is now inactive, the game code is required to delete it or re-activate it. + virtual void ConstraintBroken( IPhysicsConstraint * ) = 0; +}; + +struct hlshadowcontrol_params_t +{ + Vector targetPosition; + QAngle targetRotation; + float maxAngular; + float maxDampAngular; + float maxSpeed; + float maxDampSpeed; + float dampFactor; + float teleportDistance; +}; + +// UNDONE: At some point allow this to be parameterized using hlshadowcontrol_params_t. +// All of the infrastructure is in place to do that. +abstract_class IPhysicsShadowController +{ +public: + virtual ~IPhysicsShadowController( void ) {} + + virtual void Update( const Vector &position, const QAngle &angles, float timeOffset ) = 0; + virtual void MaxSpeed( float maxSpeed, float maxAngularSpeed ) = 0; + virtual void StepUp( float height ) = 0; + + // If the teleport distance is non-zero, the object will be teleported to + // the target location when the error exceeds this quantity. + virtual void SetTeleportDistance( float teleportDistance ) = 0; + virtual bool AllowsTranslation() = 0; + virtual bool AllowsRotation() = 0; + + // There are two classes of shadow objects: + // 1) Game physics controlled, shadow follows game physics (this is the default) + // 2) Physically controlled - shadow position is a target, but the game hasn't guaranteed that the space can be occupied by this object + virtual void SetPhysicallyControlled( bool isPhysicallyControlled ) = 0; + virtual bool IsPhysicallyControlled() = 0; + virtual void GetLastImpulse( Vector *pOut ) = 0; + virtual void UseShadowMaterial( bool bUseShadowMaterial ) = 0; + virtual void ObjectMaterialChanged( int materialIndex ) = 0; + + + //Basically get the last inputs to IPhysicsShadowController::Update(), returns last input to timeOffset in Update() + virtual float GetTargetPosition( Vector *pPositionOut, QAngle *pAnglesOut ) = 0; + + virtual float GetTeleportDistance( void ) = 0; + virtual void GetMaxSpeed( float *pMaxSpeedOut, float *pMaxAngularSpeedOut ) = 0; +}; + +class CPhysicsSimObject; +class IPhysicsMotionController; + +// Callback for simulation +class IMotionEvent +{ +public: + // These constants instruct the simulator as to how to apply the values copied to linear & angular + // GLOBAL/LOCAL refer to the coordinate system of the values, whereas acceleration/force determine whether or not + // mass is divided out (forces must be divided by mass to compute acceleration) + enum simresult_e { SIM_NOTHING = 0, SIM_LOCAL_ACCELERATION, SIM_LOCAL_FORCE, SIM_GLOBAL_ACCELERATION, SIM_GLOBAL_FORCE }; + virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) = 0; +}; + + + +abstract_class IPhysicsMotionController +{ +public: + virtual ~IPhysicsMotionController( void ) {} + virtual void SetEventHandler( IMotionEvent *handler ) = 0; + virtual void AttachObject( IPhysicsObject *pObject, bool checkIfAlreadyAttached ) = 0; + virtual void DetachObject( IPhysicsObject *pObject ) = 0; + + // returns the number of objects currently attached to the controller + virtual int CountObjects( void ) = 0; + // NOTE: pObjectList is an array with at least CountObjects() allocated + virtual void GetObjects( IPhysicsObject **pObjectList ) = 0; + // detaches all attached objects + virtual void ClearObjects( void ) = 0; + // wakes up all attached objects + virtual void WakeObjects( void ) = 0; + + enum priority_t + { + LOW_PRIORITY = 0, + MEDIUM_PRIORITY = 1, + HIGH_PRIORITY = 2, + }; + virtual void SetPriority( priority_t priority ) = 0; +}; + +// ------------------- +// Collision filter function. Return 0 if objects should not be tested for collisions, nonzero otherwise +// Install with IPhysicsEnvironment::SetCollisionFilter() +// ------------------- +abstract_class IPhysicsCollisionSolver +{ +public: + virtual int ShouldCollide( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1 ) = 0; + virtual int ShouldSolvePenetration( IPhysicsObject *pObj0, IPhysicsObject *pObj1, void *pGameData0, void *pGameData1, float dt ) = 0; + + // pObject has already done the max number of collisions this tick, should we freeze it to save CPU? + virtual bool ShouldFreezeObject( IPhysicsObject *pObject ) = 0; + + // The system has already done too many collision checks, performance will suffer. + // How many more should it do? + virtual int AdditionalCollisionChecksThisTick( int currentChecksDone ) = 0; + + // This list of objects is in a connected contact graph that is too large to solve quickly + // return true to freeze the system, false to solve it + virtual bool ShouldFreezeContacts( IPhysicsObject **pObjectList, int objectCount ) = 0; +}; + +enum PhysicsTraceType_t +{ + VPHYSICS_TRACE_EVERYTHING = 0, + VPHYSICS_TRACE_STATIC_ONLY, + VPHYSICS_TRACE_MOVING_ONLY, + VPHYSICS_TRACE_TRIGGERS_ONLY, + VPHYSICS_TRACE_STATIC_AND_MOVING, +}; + +abstract_class IPhysicsTraceFilter +{ +public: + virtual bool ShouldHitObject( IPhysicsObject *pObject, int contentsMask ) = 0; + virtual PhysicsTraceType_t GetTraceType() const = 0; +}; + +abstract_class IPhysicsEnvironment +{ +public: + virtual ~IPhysicsEnvironment( void ) {} + + virtual void SetDebugOverlay( CreateInterfaceFn debugOverlayFactory ) = 0; + virtual IVPhysicsDebugOverlay *GetDebugOverlay( void ) = 0; + + // gravity is a 3-vector in in/s^2 + virtual void SetGravity( const Vector &gravityVector ) = 0; + virtual void GetGravity( Vector *pGravityVector ) const = 0; + + // air density is in kg / m^3 (water is 1000) + // This controls drag, air that is more dense has more drag. + virtual void SetAirDensity( float density ) = 0; + virtual float GetAirDensity( void ) const = 0; + + // object creation + // create a polygonal object. pCollisionModel was created by the physics builder DLL in a pre-process. + virtual IPhysicsObject *CreatePolyObject( const CPhysCollide *pCollisionModel, int materialIndex, const Vector &position, const QAngle &angles, objectparams_t *pParams ) = 0; + // same as above, but this one cannot move or rotate (infinite mass/inertia) + virtual IPhysicsObject *CreatePolyObjectStatic( const CPhysCollide *pCollisionModel, int materialIndex, const Vector &position, const QAngle &angles, objectparams_t *pParams ) = 0; + // Create a perfectly spherical object + virtual IPhysicsObject *CreateSphereObject( float radius, int materialIndex, const Vector &position, const QAngle &angles, objectparams_t *pParams, bool isStatic ) = 0; + // destroy an object created with CreatePolyObject() or CreatePolyObjectStatic() + virtual void DestroyObject( IPhysicsObject * ) = 0; + + // Create a polygonal fluid body out of the specified collision model + // This object will affect any other objects that collide with the collision model + virtual IPhysicsFluidController *CreateFluidController( IPhysicsObject *pFluidObject, fluidparams_t *pParams ) = 0; + // Destroy an object created with CreateFluidController() + virtual void DestroyFluidController( IPhysicsFluidController * ) = 0; + + // Create a simulated spring that connects 2 objects + virtual IPhysicsSpring *CreateSpring( IPhysicsObject *pObjectStart, IPhysicsObject *pObjectEnd, springparams_t *pParams ) = 0; + virtual void DestroySpring( IPhysicsSpring * ) = 0; + + // Create a constraint in the space of pReferenceObject which is attached by the constraint to pAttachedObject + virtual IPhysicsConstraint *CreateRagdollConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_ragdollparams_t &ragdoll ) = 0; + virtual IPhysicsConstraint *CreateHingeConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_hingeparams_t &hinge ) = 0; + virtual IPhysicsConstraint *CreateFixedConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_fixedparams_t &fixed ) = 0; + virtual IPhysicsConstraint *CreateSlidingConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_slidingparams_t &sliding ) = 0; + virtual IPhysicsConstraint *CreateBallsocketConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_ballsocketparams_t &ballsocket ) = 0; + virtual IPhysicsConstraint *CreatePulleyConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_pulleyparams_t &pulley ) = 0; + virtual IPhysicsConstraint *CreateLengthConstraint( IPhysicsObject *pReferenceObject, IPhysicsObject *pAttachedObject, IPhysicsConstraintGroup *pGroup, const constraint_lengthparams_t &length ) = 0; + + virtual void DestroyConstraint( IPhysicsConstraint * ) = 0; + + virtual IPhysicsConstraintGroup *CreateConstraintGroup( const constraint_groupparams_t &groupParams ) = 0; + virtual void DestroyConstraintGroup( IPhysicsConstraintGroup *pGroup ) = 0; + + virtual IPhysicsShadowController *CreateShadowController( IPhysicsObject *pObject, bool allowTranslation, bool allowRotation ) = 0; + virtual void DestroyShadowController( IPhysicsShadowController * ) = 0; + + virtual IPhysicsPlayerController *CreatePlayerController( IPhysicsObject *pObject ) = 0; + virtual void DestroyPlayerController( IPhysicsPlayerController * ) = 0; + + virtual IPhysicsMotionController *CreateMotionController( IMotionEvent *pHandler ) = 0; + virtual void DestroyMotionController( IPhysicsMotionController *pController ) = 0; + + virtual IPhysicsVehicleController *CreateVehicleController( IPhysicsObject *pVehicleBodyObject, const vehicleparams_t ¶ms, unsigned int nVehicleType, IPhysicsGameTrace *pGameTrace ) = 0; + virtual void DestroyVehicleController( IPhysicsVehicleController * ) = 0; + + // install a function to filter collisions/penentration + virtual void SetCollisionSolver( IPhysicsCollisionSolver *pSolver ) = 0; + + // run the simulator for deltaTime seconds + virtual void Simulate( float deltaTime ) = 0; + // true if currently running the simulator (i.e. in a callback during physenv->Simulate()) + virtual bool IsInSimulation() const = 0; + + // Manage the timestep (period) of the simulator. The main functions are all integrated with + // this period as dt. + virtual float GetSimulationTimestep() const = 0; + virtual void SetSimulationTimestep( float timestep ) = 0; + + // returns the current simulation clock's value. This is an absolute time. + virtual float GetSimulationTime() const = 0; + virtual void ResetSimulationClock() = 0; + // returns the current simulation clock's value at the next frame. This is an absolute time. + virtual float GetNextFrameTime( void ) const = 0; + + // Collision callbacks (game code collision response) + virtual void SetCollisionEventHandler( IPhysicsCollisionEvent *pCollisionEvents ) = 0; + virtual void SetObjectEventHandler( IPhysicsObjectEvent *pObjectEvents ) = 0; + virtual void SetConstraintEventHandler( IPhysicsConstraintEvent *pConstraintEvents ) = 0; + + virtual void SetQuickDelete( bool bQuick ) = 0; + + virtual int GetActiveObjectCount() const = 0; + virtual void GetActiveObjects( IPhysicsObject **pOutputObjectList ) const = 0; + virtual const IPhysicsObject **GetObjectList( int *pOutputObjectCount ) const = 0; + virtual bool TransferObject( IPhysicsObject *pObject, IPhysicsEnvironment *pDestinationEnvironment ) = 0; + + virtual void CleanupDeleteList( void ) = 0; + virtual void EnableDeleteQueue( bool enable ) = 0; + + // Save/Restore methods + virtual bool Save( const physsaveparams_t ¶ms ) = 0; + virtual void PreRestore( const physprerestoreparams_t ¶ms ) = 0; + virtual bool Restore( const physrestoreparams_t ¶ms ) = 0; + virtual void PostRestore() = 0; + + // Debugging: + virtual bool IsCollisionModelUsed( CPhysCollide *pCollide ) const = 0; + + // Physics world version of the enginetrace API: + virtual void TraceRay( const Ray_t &ray, unsigned int fMask, IPhysicsTraceFilter *pTraceFilter, trace_t *pTrace ) = 0; + virtual void SweepCollideable( const CPhysCollide *pCollide, const Vector &vecAbsStart, const Vector &vecAbsEnd, + const QAngle &vecAngles, unsigned int fMask, IPhysicsTraceFilter *pTraceFilter, trace_t *pTrace ) = 0; + + // performance tuning + virtual void GetPerformanceSettings( physics_performanceparams_t *pOutput ) const = 0; + virtual void SetPerformanceSettings( const physics_performanceparams_t *pSettings ) = 0; + + // perf/cost statistics + virtual void ReadStats( physics_stats_t *pOutput ) = 0; + virtual void ClearStats() = 0; + + virtual unsigned int GetObjectSerializeSize( IPhysicsObject *pObject ) const = 0; + virtual void SerializeObjectToBuffer( IPhysicsObject *pObject, unsigned char *pBuffer, unsigned int bufferSize ) = 0; + virtual IPhysicsObject *UnserializeObjectFromBuffer( void *pGameData, unsigned char *pBuffer, unsigned int bufferSize, bool enableCollisions ) = 0; + + + virtual void EnableConstraintNotify( bool bEnable ) = 0; + virtual void DebugCheckContacts(void) = 0; + + virtual void SetAlternateGravity( const Vector &gravityVector ) = 0; + virtual void GetAlternateGravity( Vector *pGravityVector ) const = 0; + + virtual float GetDeltaFrameTime( int maxTicks ) const = 0; + virtual void ForceObjectsToSleep( IPhysicsObject **pList, int listCount ) = 0; +}; + +enum callbackflags +{ + CALLBACK_GLOBAL_COLLISION = 0x0001, + CALLBACK_GLOBAL_FRICTION = 0x0002, + CALLBACK_GLOBAL_TOUCH = 0x0004, + CALLBACK_GLOBAL_TOUCH_STATIC = 0x0008, + CALLBACK_SHADOW_COLLISION = 0x0010, + CALLBACK_GLOBAL_COLLIDE_STATIC = 0x0020, + CALLBACK_IS_VEHICLE_WHEEL = 0x0040, + CALLBACK_FLUID_TOUCH = 0x0100, + CALLBACK_NEVER_DELETED = 0x0200, // HACKHACK: This means this object will never be deleted (set on the world) + CALLBACK_MARKED_FOR_DELETE = 0x0400, // This allows vphysics to skip some work for this object since it will be + // deleted later this frame. (Set automatically by destroy calls) + CALLBACK_ENABLING_COLLISION = 0x0800, // This is active during the time an object is enabling collisions + // allows us to skip collisions between "new" objects and objects marked for delete + CALLBACK_DO_FLUID_SIMULATION = 0x1000, // remove this to opt out of fluid simulations + CALLBACK_IS_PLAYER_CONTROLLER= 0x2000, // HACKHACK: Set this on players until player cotrollers are unified with shadow controllers + CALLBACK_CHECK_COLLISION_DISABLE = 0x4000, + CALLBACK_MARKED_FOR_TEST = 0x8000, // debug -- marked object is being debugged +}; + +enum collisionhints +{ + COLLISION_HINT_DEBRIS = 0x0001, + COLLISION_HINT_STATICSOLID = 0x0002, +}; + +abstract_class IPhysicsObject +{ +public: + virtual ~IPhysicsObject( void ) {} + + // returns true if this object is static/unmoveable + // NOTE: returns false for objects that are not created static, but set EnableMotion(false); + // Call IsMoveable() to find if the object is static OR has motion disabled + virtual bool IsStatic() const = 0; + virtual bool IsAsleep() const = 0; + virtual bool IsTrigger() const = 0; + virtual bool IsFluid() const = 0; // fluids are special triggers with fluid controllers attached, they return true to IsTrigger() as well! + virtual bool IsHinged() const = 0; + virtual bool IsCollisionEnabled() const = 0; + virtual bool IsGravityEnabled() const = 0; + virtual bool IsDragEnabled() const = 0; + virtual bool IsMotionEnabled() const = 0; + virtual bool IsMoveable() const = 0; // legacy: IsMotionEnabled() && !IsStatic() + virtual bool IsAttachedToConstraint(bool bExternalOnly) const = 0; + + // Enable / disable collisions for this object + virtual void EnableCollisions( bool enable ) = 0; + // Enable / disable gravity for this object + virtual void EnableGravity( bool enable ) = 0; + // Enable / disable air friction / drag for this object + virtual void EnableDrag( bool enable ) = 0; + // Enable / disable motion (pin / unpin the object) + virtual void EnableMotion( bool enable ) = 0; + + // Game can store data in each object (link back to game object) + virtual void SetGameData( void *pGameData ) = 0; + virtual void *GetGameData( void ) const = 0; + // This flags word can be defined by the game as well + virtual void SetGameFlags( unsigned short userFlags ) = 0; + virtual unsigned short GetGameFlags( void ) const = 0; + virtual void SetGameIndex( unsigned short gameIndex ) = 0; + virtual unsigned short GetGameIndex( void ) const = 0; + + // setup various callbacks for this object + virtual void SetCallbackFlags( unsigned short callbackflags ) = 0; + // get the current callback state for this object + virtual unsigned short GetCallbackFlags( void ) const = 0; + + // "wakes up" an object + // NOTE: ALL OBJECTS ARE "Asleep" WHEN CREATED + virtual void Wake( void ) = 0; + virtual void Sleep( void ) = 0; + // call this when the collision filter conditions change due to this + // object's state (e.g. changing solid type or collision group) + virtual void RecheckCollisionFilter() = 0; + // NOTE: Contact points aren't updated when collision rules change, call this to force an update + // UNDONE: Force this in RecheckCollisionFilter() ? + virtual void RecheckContactPoints() = 0; + + // mass accessors + virtual void SetMass( float mass ) = 0; + virtual float GetMass( void ) const = 0; + // get 1/mass (it's cached) + virtual float GetInvMass( void ) const = 0; + virtual Vector GetInertia( void ) const = 0; + virtual Vector GetInvInertia( void ) const = 0; + virtual void SetInertia( const Vector &inertia ) = 0; + + virtual void SetDamping( const float *speed, const float *rot ) = 0; + virtual void GetDamping( float *speed, float *rot ) const = 0; + + // coefficients are optional, pass either + virtual void SetDragCoefficient( float *pDrag, float *pAngularDrag ) = 0; + virtual void SetBuoyancyRatio( float ratio ) = 0; // Override bouyancy + + // material index + virtual int GetMaterialIndex() const = 0; + virtual void SetMaterialIndex( int materialIndex ) = 0; + + // contents bits + virtual unsigned int GetContents() const = 0; + virtual void SetContents( unsigned int contents ) = 0; + + // Get the radius if this is a sphere object (zero if this is a polygonal mesh) + virtual float GetSphereRadius() const = 0; + // Set the radius on a sphere. May need to force recalculation of contact points + virtual void SetSphereRadius(float radius) = 0; + virtual float GetEnergy() const = 0; + virtual Vector GetMassCenterLocalSpace() const = 0; + + // NOTE: This will teleport the object + virtual void SetPosition( const Vector &worldPosition, const QAngle &angles, bool isTeleport ) = 0; + virtual void SetPositionMatrix( const matrix3x4_t&matrix, bool isTeleport ) = 0; + + virtual void GetPosition( Vector *worldPosition, QAngle *angles ) const = 0; + virtual void GetPositionMatrix( matrix3x4_t *positionMatrix ) const = 0; + // force the velocity to a new value + // NOTE: velocity is in worldspace, angularVelocity is relative to the object's + // local axes (just like pev->velocity, pev->avelocity) + virtual void SetVelocity( const Vector *velocity, const AngularImpulse *angularVelocity ) = 0; + + // like the above, but force the change into the simulator immediately + virtual void SetVelocityInstantaneous( const Vector *velocity, const AngularImpulse *angularVelocity ) = 0; + + // NOTE: velocity is in worldspace, angularVelocity is relative to the object's + // local axes (just like pev->velocity, pev->avelocity) + virtual void GetVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const = 0; + + // NOTE: These are velocities, not forces. i.e. They will have the same effect regardless of + // the object's mass or inertia + virtual void AddVelocity( const Vector *velocity, const AngularImpulse *angularVelocity ) = 0; + // gets a velocity in the object's local frame of reference at a specific point + virtual void GetVelocityAtPoint( const Vector &worldPosition, Vector *pVelocity ) const = 0; + // gets the velocity actually moved by the object in the last simulation update + virtual void GetImplicitVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const = 0; + // NOTE: These are here for convenience, but you can do them yourself by using the matrix + // returned from GetPositionMatrix() + // convenient coordinate system transformations (params - dest, src) + virtual void LocalToWorld( Vector *worldPosition, const Vector &localPosition ) const = 0; + virtual void WorldToLocal( Vector *localPosition, const Vector &worldPosition ) const = 0; + + // transforms a vector (no translation) from object-local to world space + virtual void LocalToWorldVector( Vector *worldVector, const Vector &localVector ) const = 0; + // transforms a vector (no translation) from world to object-local space + virtual void WorldToLocalVector( Vector *localVector, const Vector &worldVector ) const = 0; + + // push on an object + // force vector is direction & magnitude of impulse kg in / s + virtual void ApplyForceCenter( const Vector &forceVector ) = 0; + virtual void ApplyForceOffset( const Vector &forceVector, const Vector &worldPosition ) = 0; + // apply torque impulse. This will change the angular velocity on the object. + // HL Axes, kg degrees / s + virtual void ApplyTorqueCenter( const AngularImpulse &torque ) = 0; + + // Calculates the force/torque on the center of mass for an offset force impulse (pass output to ApplyForceCenter / ApplyTorqueCenter) + virtual void CalculateForceOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerForce, AngularImpulse *centerTorque ) const = 0; + // Calculates the linear/angular velocities on the center of mass for an offset force impulse (pass output to AddVelocity) + virtual void CalculateVelocityOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerVelocity, AngularImpulse *centerAngularVelocity ) const = 0; + // calculate drag scale + virtual float CalculateLinearDrag( const Vector &unitDirection ) const = 0; + virtual float CalculateAngularDrag( const Vector &objectSpaceRotationAxis ) const = 0; + + // returns true if the object is in contact with another object + // if true, puts a point on the contact surface in contactPoint, and + // a pointer to the object in contactObject + // NOTE: You can pass NULL for either to avoid computations + // BUGBUG: Use CreateFrictionSnapshot instead of this - this is a simple hack + virtual bool GetContactPoint( Vector *contactPoint, IPhysicsObject **contactObject ) const = 0; + + // refactor this a bit - move some of this to IPhysicsShadowController + virtual void SetShadow( float maxSpeed, float maxAngularSpeed, bool allowPhysicsMovement, bool allowPhysicsRotation ) = 0; + virtual void UpdateShadow( const Vector &targetPosition, const QAngle &targetAngles, bool tempDisableGravity, float timeOffset ) = 0; + + // returns number of ticks since last Update() call + virtual int GetShadowPosition( Vector *position, QAngle *angles ) const = 0; + virtual IPhysicsShadowController *GetShadowController( void ) const = 0; + virtual void RemoveShadowController() = 0; + // applies the math of the shadow controller to this object. + // for use in your own controllers + // returns the new value of secondsToArrival with dt time elapsed + virtual float ComputeShadowControl( const hlshadowcontrol_params_t ¶ms, float secondsToArrival, float dt ) = 0; + + + virtual const CPhysCollide *GetCollide( void ) const = 0; + virtual const char *GetName() const = 0; + + virtual void BecomeTrigger() = 0; + virtual void RemoveTrigger() = 0; + + // sets the object to be hinged. Fixed it place, but able to rotate around one axis. + virtual void BecomeHinged( int localAxis ) = 0; + // resets the object to original state + virtual void RemoveHinged() = 0; + + // used to iterate the contact points of an object + virtual IPhysicsFrictionSnapshot *CreateFrictionSnapshot() = 0; + virtual void DestroyFrictionSnapshot( IPhysicsFrictionSnapshot *pSnapshot ) = 0; + + // dumps info about the object to Msg() + virtual void OutputDebugInfo() const = 0; + +#if OBJECT_WELDING + virtual void WeldToObject( IPhysicsObject *pParent ) = 0; + virtual void RemoveWeld( IPhysicsObject *pOther ) = 0; + virtual void RemoveAllWelds( void ) = 0; +#endif + + // EnableGravity still determines whether to apply gravity + // This flag determines which gravity constant to use for an alternate gravity effect + virtual void SetUseAlternateGravity( bool bSet ) = 0; + virtual void SetCollisionHints( uint32 collisionHints ) = 0; + virtual uint32 GetCollisionHints() const = 0; +}; + + +abstract_class IPhysicsSpring +{ +public: + virtual ~IPhysicsSpring( void ) {} + virtual void GetEndpoints( Vector *worldPositionStart, Vector *worldPositionEnd ) = 0; + virtual void SetSpringConstant( float flSpringContant) = 0; + virtual void SetSpringDamping( float flSpringDamping) = 0; + virtual void SetSpringLength( float flSpringLenght) = 0; + + // Get the starting object + virtual IPhysicsObject *GetStartObject( void ) = 0; + + // Get the end object + virtual IPhysicsObject *GetEndObject( void ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: These properties are defined per-material. This is accessible at +// each triangle in a collision mesh +//----------------------------------------------------------------------------- +struct surfacephysicsparams_t +{ +// vphysics physical properties + float friction; + float elasticity; // collision elasticity - used to compute coefficient of restitution + float density; // physical density (in kg / m^3) + float thickness; // material thickness if not solid (sheet materials) in inches + float dampening; +}; + +struct surfaceaudioparams_t +{ +// sounds / audio data + float reflectivity; // like elasticity, but how much sound should be reflected by this surface + float hardnessFactor; // like elasticity, but only affects impact sound choices + float roughnessFactor; // like friction, but only affects scrape sound choices + +// audio thresholds + float roughThreshold; // surface roughness > this causes "rough" scrapes, < this causes "smooth" scrapes + float hardThreshold; // surface hardness > this causes "hard" impacts, < this causes "soft" impacts + float hardVelocityThreshold; // collision velocity > this causes "hard" impacts, < this causes "soft" impacts + // NOTE: Hard impacts must meet both hardnessFactor AND velocity thresholds +}; + +struct surfacesoundnames_t +{ + unsigned short walkStepLeft; + unsigned short walkStepRight; + unsigned short runStepLeft; + unsigned short runStepRight; + + unsigned short impactSoft; + unsigned short impactHard; + + unsigned short scrapeSmooth; + unsigned short scrapeRough; + + unsigned short bulletImpact; + unsigned short rolling; + + unsigned short breakSound; + unsigned short strainSound; +}; + +struct surfacesoundhandles_t +{ + short walkStepLeft; + short walkStepRight; + short runStepLeft; + short runStepRight; + + short impactSoft; + short impactHard; + + short scrapeSmooth; + short scrapeRough; + + short bulletImpact; + short rolling; + + short breakSound; + short strainSound; +}; + +struct surfacegameprops_t +{ +// game movement data + float maxSpeedFactor; // Modulates player max speed when walking on this surface + float jumpFactor; // Indicates how much higher the player should jump when on the surface +// Game-specific data + unsigned short material; + // Indicates whether or not the player is on a ladder. + unsigned char climbable; + unsigned char pad; +}; + +//----------------------------------------------------------------------------- +// Purpose: Each different material has an entry like this +//----------------------------------------------------------------------------- +struct surfacedata_t +{ + surfacephysicsparams_t physics; // physics parameters + surfaceaudioparams_t audio; // audio parameters + surfacesoundnames_t sounds; // names of linked sounds + surfacegameprops_t game; // Game data / properties + + surfacesoundhandles_t soundhandles; +}; + +#define VPHYSICS_SURFACEPROPS_INTERFACE_VERSION "VPhysicsSurfaceProps001" +abstract_class IPhysicsSurfaceProps +{ +public: + virtual ~IPhysicsSurfaceProps( void ) {} + + // parses a text file containing surface prop keys + virtual int ParseSurfaceData( const char *pFilename, const char *pTextfile ) = 0; + // current number of entries in the database + virtual int SurfacePropCount( void ) const = 0; + + virtual int GetSurfaceIndex( const char *pSurfacePropName ) const = 0; + virtual void GetPhysicsProperties( int surfaceDataIndex, float *density, float *thickness, float *friction, float *elasticity ) const = 0; + + virtual surfacedata_t *GetSurfaceData( int surfaceDataIndex ) = 0; + virtual const char *GetString( unsigned short stringTableIndex ) const = 0; + + + virtual const char *GetPropName( int surfaceDataIndex ) const = 0; + + // sets the global index table for world materials + // UNDONE: Make this per-CPhysCollide + virtual void SetWorldMaterialIndexTable( int *pMapArray, int mapSize ) = 0; + + // NOTE: Same as GetPhysicsProperties, but maybe more convenient + virtual void GetPhysicsParameters( int surfaceDataIndex, surfacephysicsparams_t *pParamsOut ) const = 0; +}; + +abstract_class IPhysicsFluidController +{ +public: + virtual ~IPhysicsFluidController( void ) {} + + virtual void SetGameData( void *pGameData ) = 0; + virtual void *GetGameData( void ) const = 0; + + virtual void GetSurfacePlane( Vector *pNormal, float *pDist ) const = 0; + virtual float GetDensity() const = 0; + virtual void WakeAllSleepingObjects() = 0; + virtual int GetContents() const = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: parameter block for creating fluid dynamic motion +// UNDONE: Expose additional fluid model paramters? +//----------------------------------------------------------------------------- +struct fluidparams_t +{ + Vector4D surfacePlane; // x,y,z normal, dist (plane constant) fluid surface + Vector currentVelocity; // velocity of the current in inches/second + float damping; // damping factor for buoyancy (tweak) + float torqueFactor; + float viscosityFactor; + void *pGameData; + bool useAerodynamics;// true if this controller should calculate surface pressure + int contents; + + fluidparams_t() {} + fluidparams_t( fluidparams_t const& src ) + { + Vector4DCopy( src.surfacePlane, surfacePlane ); + VectorCopy( src.currentVelocity, currentVelocity ); + damping = src.damping; + torqueFactor = src.torqueFactor; + viscosityFactor = src.viscosityFactor; + contents = src.contents; + } +}; + +//----------------------------------------------------------------------------- +// Purpose: parameter block for creating linear springs +// UNDONE: Expose additional spring model paramters? +//----------------------------------------------------------------------------- +struct springparams_t +{ + springparams_t() + { + memset( this, 0, sizeof(*this) ); + } + float constant; // spring constant + float naturalLength;// relaxed length + float damping; // damping factor + float relativeDamping; // relative damping (damping proportional to the change in the relative position of the objects) + Vector startPosition; + Vector endPosition; + bool useLocalPositions; // start & end Position are in local space to start and end objects if this is true + bool onlyStretch; // only apply forces when the length is greater than the natural length +}; + +//----------------------------------------------------------------------------- +// Purpose: parameter block for creating polygonal objects +//----------------------------------------------------------------------------- +struct objectparams_t +{ + Vector *massCenterOverride; + float mass; + float inertia; + float damping; + float rotdamping; + float rotInertiaLimit; + const char *pName; // used only for debugging + void *pGameData; + float volume; + float dragCoefficient; + bool enableCollisions; +}; + +struct convertconvexparams_t +{ + bool buildOuterConvexHull; + bool buildDragAxisAreas; + bool buildOptimizedTraceTables; + bool checkOptimalTracing; + float dragAreaEpsilon; + CPhysConvex *pForcedOuterHull; + + void Defaults() + { + dragAreaEpsilon = 0.25f; // 0.5in x 0.5in square + buildOuterConvexHull = false; + buildDragAxisAreas = false; + buildOptimizedTraceTables = false; + checkOptimalTracing = false; + pForcedOuterHull = NULL; + } +}; + +//----------------------------------------------------------------------------- +// Physics interface IDs +// +// Note that right now the order of the enum also defines the order of save/load + + +//----------------------------------------------------------------------------- +// Purpose: parameter blocks for save and load operations +//----------------------------------------------------------------------------- +struct physsaveparams_t +{ + ISave *pSave; + void *pObject; + PhysInterfaceId_t type; +}; + +struct physrestoreparams_t +{ + IRestore *pRestore; + void **ppObject; + PhysInterfaceId_t type; + void *pGameData; + const char *pName; // used only for debugging + const CPhysCollide *pCollisionModel; + IPhysicsEnvironment *pEnvironment; + IPhysicsGameTrace *pGameTrace; +}; + +struct physrecreateparams_t +{ + void *pOldObject; + void *pNewObject; +}; + +struct physprerestoreparams_t +{ + int recreatedObjectCount; + physrecreateparams_t recreatedObjectList[1]; +}; + +//------------------------------------- + +#define DEFINE_PIID( type, enumval ) \ + template <> inline PhysInterfaceId_t GetPhysIID( type ** ) { return enumval; } + +template inline PhysInterfaceId_t GetPhysIID(PHYSPTR **); // will get link error if no match + +DEFINE_PIID( IPhysicsObject, PIID_IPHYSICSOBJECT ); +DEFINE_PIID( IPhysicsFluidController, PIID_IPHYSICSFLUIDCONTROLLER ); +DEFINE_PIID( IPhysicsSpring, PIID_IPHYSICSSPRING ); +DEFINE_PIID( IPhysicsConstraintGroup, PIID_IPHYSICSCONSTRAINTGROUP ); +DEFINE_PIID( IPhysicsConstraint, PIID_IPHYSICSCONSTRAINT ); +DEFINE_PIID( IPhysicsShadowController, PIID_IPHYSICSSHADOWCONTROLLER ); +DEFINE_PIID( IPhysicsPlayerController, PIID_IPHYSICSPLAYERCONTROLLER ); +DEFINE_PIID( IPhysicsMotionController, PIID_IPHYSICSMOTIONCONTROLLER ); +DEFINE_PIID( IPhysicsVehicleController, PIID_IPHYSICSVEHICLECONTROLLER ); +DEFINE_PIID( IPhysicsGameTrace, PIID_IPHYSICSGAMETRACE ); + +//----------------------------------------------------------------------------- + +#endif // VPHYSICS_INTERFACE_H diff --git a/public/vpklib/packedstore.h b/public/vpklib/packedstore.h new file mode 100644 index 0000000..4b9cb2c --- /dev/null +++ b/public/vpklib/packedstore.h @@ -0,0 +1,203 @@ +//===== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef PACKEDSTORE_H +#define PACKEDSTORE_H +#ifdef _WIN32 +#pragma once +#endif + + +#include +#include +#include + +#include "filesystem.h" +#include "tier1/utlintrusivelist.h" +#include "tier1/utlvector.h" +#include "tier1/utlmap.h" + +class CPackedStore; + + + + +struct CPackedStoreFileHandle +{ +public: + int m_nFileNumber; + int m_nFileOffset; + int m_nFileSize; + int m_nCurrentFileOffset; + void const *m_pMetaData; + uint16 m_nMetaDataSize; + CPackedStore *m_pOwner; + struct CFileHeaderFixedData *m_pHeaderData; + uint8 *m_pDirFileNamePtr; // pointer to basename in dir block + + FORCEINLINE operator bool( void ) const + { + return ( m_nFileNumber != -1 ); + } + + FORCEINLINE int Read( void *pOutData, int nNumBytes ); + + CPackedStoreFileHandle( void ) + { + m_nFileNumber = -1; + } + + int Seek( int nOffset, int nWhence ) + { + switch( nWhence ) + { + case SEEK_CUR: + nOffset = m_nFileOffset + nOffset ; + break; + + case SEEK_END: + nOffset = m_nFileSize - 1 + nOffset; + break; + } + m_nCurrentFileOffset = MAX( 0, MIN( m_nFileSize - 1, nOffset ) ); + return m_nCurrentFileOffset; + } + + int Tell( void ) const + { + return m_nCurrentFileOffset; + } +}; + +#define MAX_ARCHIVE_FILES_TO_KEEP_OPEN_AT_ONCE 128 + +#define PACKEDFILE_EXT_HASH_SIZE 15 + + +#ifdef _WIN32 +typedef HANDLE PackDataFileHandle_t; +#else +typedef FileHandle_t PackDataFileHandle_t; +#endif + +struct FileHandleTracker_t +{ + int m_nFileNumber; + PackDataFileHandle_t m_hFileHandle; + int m_nCurOfs; + CThreadFastMutex m_Mutex; + + FileHandleTracker_t( void ) + { + m_nFileNumber = -1; + } +}; + +enum ePackedStoreAddResultCode +{ + EPADD_NEWFILE, // the file was added and is new + EPADD_ADDSAMEFILE, // the file was already present, and the contents are the same as what you passed. + EPADD_UPDATEFILE, // the file was alreayd present and its contents have been updated + EPADD_ERROR, // some error has resulted +}; + + +class CPackedStore +{ +public: + CPackedStore( char const *pFileBasename, IBaseFileSystem *pFS, bool bOpenForWrite = false ); + + CPackedStoreFileHandle OpenFile( char const *pFile ); + + ePackedStoreAddResultCode AddFile( char const *pFile, int nNumDataParts, uint16 nMetaDataSize, void *pFileData, uint32 nFullFileSize, bool bMultiChunk, uint32 const *pCrcToUse = NULL ); + + // write out the file directory + void Write( void ); + + int ReadData( CPackedStoreFileHandle &handle, void *pOutData, int nNumBytes ); + + ~CPackedStore( void ); + + FORCEINLINE void *DirectoryData( void ) + { + return m_DirectoryData.Base(); + } + + // Get a list of all the files in the zip You are responsible for freeing the contents of + // outFilenames (call outFilenames.PurgeAndDeleteElements). + int GetFileList( CUtlStringList &outFilenames, bool bFormattedOutput, bool bSortedOutput ); + + // Get a list of all files that match the given wildcard string + int GetFileList( const char *pWildCard, CUtlStringList &outFilenames, bool bFormattedOutput, bool bSortedOutput ); + + // Get a list of all directories of the given wildcard + int GetFileAndDirLists( const char *pWildCard, CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput ); + int GetFileAndDirLists( CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput ); + + bool IsEmpty( void ) const; + + + char const *FullPathName( void ) + { + return m_pszFullPathName; + } + + void SetWriteChunkSize( int nWriteChunkSize ) + { + m_nWriteChunkSize = nWriteChunkSize; + } + +private: + char m_pszFileBaseName[MAX_PATH]; + char m_pszFullPathName[MAX_PATH]; + int m_nWriteOffset; + int m_nChunkWriteIndex; + int m_nWriteChunkFileIndex; + int m_nDirectoryDataSize; + int m_nWriteChunkSize; + + FileHandle_t m_ChunkWriteHandle; + IBaseFileSystem *m_pFileSystem; + CThreadFastMutex m_Mutex; + + + CUtlIntrusiveList m_pExtensionData[PACKEDFILE_EXT_HASH_SIZE]; + + CUtlVector m_DirectoryData; + CUtlBlockVector m_EmbeddedChunkData; + + int m_nHighestChunkFileIndex; + + FileHandleTracker_t m_FileHandles[MAX_ARCHIVE_FILES_TO_KEEP_OPEN_AT_ONCE]; + + void Init( void ); + + struct CFileHeaderFixedData *FindFileEntry( + char const *pDirname, char const *pBaseName, char const *pExtension, + uint8 **pExtBaseOut = NULL, uint8 **pNameBaseOut = NULL ); + + void BuildHashTables( void ); + void GetDataFileName( char *pszOutName, int nFileNumber ); + + FileHandleTracker_t &GetFileHandle( int nFileNumber ); + + FileHandle_t GetWriteHandle( int nChunkIdx ); + void CloseWriteHandle( void ); + + // For cache-ing directory and contents data + CUtlStringList m_directoryList; // The index of this list of directories... + CUtlMap m_dirContents; // ...is the key to this map of filenames + void BuildFindFirstCache(); + +}; + +FORCEINLINE int CPackedStoreFileHandle::Read( void *pOutData, int nNumBytes ) +{ + return m_pOwner->ReadData( *this, pOutData, nNumBytes ); +} + + +#endif // packedtsore_h diff --git a/public/vscript/ivscript.h b/public/vscript/ivscript.h new file mode 100644 index 0000000..7003efa --- /dev/null +++ b/public/vscript/ivscript.h @@ -0,0 +1,1412 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: VScript +// +// Overview +// -------- +// VScript is an abstract binding layer that allows code to expose itself to +// multiple scripting languages in a uniform format. Code can expose +// functions, classes, and data to the scripting languages, and can also +// call functions that reside in scripts. +// +// Initializing +// ------------ +// +// To create a script virtual machine (VM), grab the global instance of +// IScriptManager, call CreateVM, then call Init on the returned VM. Right +// now you can have multiple VMs, but only VMs for a specific language. +// +// Exposing functions and classes +// ------------------------------ +// +// To expose a C++ function to the scripting system, you just need to fill out a +// description block. Using templates, the system will automatically deduce +// all of the binding requirements (parameters and return values). Functions +// are limited as to what the types of the parameters can be. See ScriptVariant_t. +// +// extern IScriptVM *pScriptVM; +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// +// void RegisterFuncs() +// { +// ScriptRegisterFunction( pScriptVM, Foo ); +// ScriptRegisterFunction( pScriptVM, Bar ); +// ScriptRegisterFunction( pScriptVM, FooBar ); +// ScriptRegisterFunctionNamed( pScriptVM, OverlyTechnicalName, "SimpleName" ); +// } +// +// class CMyClass +// { +// public: +// bool Foo( int ); +// void Bar(); +// float FooBar( int, const char * ); +// float OverlyTechnicalName( bool ); +// }; +// +// BEGIN_SCRIPTDESC_ROOT( CMyClass ) +// DEFINE_SCRIPTFUNC( Foo ) +// DEFINE_SCRIPTFUNC( Bar ) +// DEFINE_SCRIPTFUNC( FooBar ) +// DEFINE_SCRIPTFUNC_NAMED( OverlyTechnicalName, "SimpleMemberName" ) +// END_SCRIPTDESC(); +// +// class CMyDerivedClass : public CMyClass +// { +// public: +// float DerivedFunc() const; +// }; +// +// BEGIN_SCRIPTDESC( CMyDerivedClass, CMyClass ) +// DEFINE_SCRIPTFUNC( DerivedFunc ) +// END_SCRIPTDESC(); +// +// CMyDerivedClass derivedInstance; +// +// void AnotherFunction() +// { +// // Manual class exposure +// pScriptVM->RegisterClass( GetScriptDescForClass( CMyClass ) ); +// +// // Auto registration by instance +// pScriptVM->RegisterInstance( &derivedInstance, "theInstance" ); +// } +// +// Classes with "DEFINE_SCRIPT_CONSTRUCTOR()" in their description can be instanced within scripts +// +// Scopes +// ------ +// Scripts can either be run at the global scope, or in a user defined scope. In the latter case, +// all "globals" within the script are actually in the scope. This can be used to bind private +// data spaces with C++ objects. +// +// Calling a function on a script +// ------------------------------ +// Generally, use the "Call" functions. This example is the equivalent of DoIt("Har", 6.0, 99). +// +// hFunction = pScriptVM->LookupFunction( "DoIt", hScope ); +// pScriptVM->Call( hFunction, hScope, true, NULL, "Har", 6.0, 99 ); +// +//============================================================================= + +#ifndef IVSCRIPT_H +#define IVSCRIPT_H + +#include "platform.h" +#include "datamap.h" +#include "appframework/IAppSystem.h" +#include "tier1/functors.h" +#include "tier0/memdbgon.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#ifdef VSCRIPT_DLL_EXPORT +#define VSCRIPT_INTERFACE DLL_EXPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_EXPORT +#define VSCRIPT_CLASS DLL_CLASS_EXPORT +#else +#define VSCRIPT_INTERFACE DLL_IMPORT +#define VSCRIPT_OVERLOAD DLL_GLOBAL_IMPORT +#define VSCRIPT_CLASS DLL_CLASS_IMPORT +#endif + +class CUtlBuffer; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define VSCRIPT_INTERFACE_VERSION "VScriptManager009" + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class IScriptVM; + +enum ScriptLanguage_t +{ + SL_NONE, + SL_GAMEMONKEY, + SL_SQUIRREL, + SL_LUA, + SL_PYTHON, + + SL_DEFAULT = SL_SQUIRREL +}; + +class IScriptManager : public IAppSystem +{ +public: + virtual IScriptVM *CreateVM( ScriptLanguage_t language = SL_DEFAULT ) = 0; + virtual void DestroyVM( IScriptVM * ) = 0; +}; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +DECLARE_POINTER_HANDLE( HSCRIPT ); +#define INVALID_HSCRIPT ((HSCRIPT)-1) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ExtendedFieldType +{ + FIELD_TYPEUNKNOWN = FIELD_TYPECOUNT, + FIELD_CSTRING, + FIELD_HSCRIPT, + FIELD_VARIANT, +}; + +typedef int ScriptDataType_t; +struct ScriptVariant_t; + +template struct ScriptDeducer { /*enum { FIELD_TYPE = FIELD_TYPEUNKNOWN };*/ }; +#define DECLARE_DEDUCE_FIELDTYPE( fieldType, type ) template<> struct ScriptDeducer { enum { FIELD_TYPE = fieldType }; }; + +DECLARE_DEDUCE_FIELDTYPE( FIELD_VOID, void ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_FLOAT, float ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, const char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CSTRING, char * ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, Vector ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VECTOR, const Vector &); +DECLARE_DEDUCE_FIELDTYPE( FIELD_INTEGER, int ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_BOOLEAN, bool ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_CHARACTER, char ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_HSCRIPT, HSCRIPT ); +DECLARE_DEDUCE_FIELDTYPE( FIELD_VARIANT, ScriptVariant_t ); + +#define ScriptDeduceType( T ) ScriptDeducer::FIELD_TYPE + +template +inline const char * ScriptFieldTypeName() +{ + T::using_unknown_script_type(); +} + +#define DECLARE_NAMED_FIELDTYPE( fieldType, strName ) template <> inline const char * ScriptFieldTypeName() { return strName; } +DECLARE_NAMED_FIELDTYPE( void, "void" ); +DECLARE_NAMED_FIELDTYPE( float, "float" ); +DECLARE_NAMED_FIELDTYPE( const char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( char *, "cstring" ); +DECLARE_NAMED_FIELDTYPE( Vector, "vector" ); +DECLARE_NAMED_FIELDTYPE( const Vector&, "vector" ); +DECLARE_NAMED_FIELDTYPE( int, "integer" ); +DECLARE_NAMED_FIELDTYPE( bool, "boolean" ); +DECLARE_NAMED_FIELDTYPE( char, "character" ); +DECLARE_NAMED_FIELDTYPE( HSCRIPT, "hscript" ); +DECLARE_NAMED_FIELDTYPE( ScriptVariant_t, "variant" ); + +inline const char * ScriptFieldTypeName( int16 eType) +{ + switch( eType ) + { + case FIELD_VOID: return "void"; + case FIELD_FLOAT: return "float"; + case FIELD_CSTRING: return "cstring"; + case FIELD_VECTOR: return "vector"; + case FIELD_INTEGER: return "integer"; + case FIELD_BOOLEAN: return "boolean"; + case FIELD_CHARACTER: return "character"; + case FIELD_HSCRIPT: return "hscript"; + case FIELD_VARIANT: return "variant"; + default: return "unknown_script_type"; + } +} + +//--------------------------------------------------------- + +struct ScriptFuncDescriptor_t +{ + ScriptFuncDescriptor_t() + { + m_pszFunction = NULL; + m_ReturnType = FIELD_TYPEUNKNOWN; + m_pszDescription = NULL; + } + + const char *m_pszScriptName; + const char *m_pszFunction; + const char *m_pszDescription; + ScriptDataType_t m_ReturnType; + CUtlVector m_Parameters; +}; + + +//--------------------------------------------------------- + +// Prefix a script description with this in order to not show the function or class in help +#define SCRIPT_HIDE "@" + +// Prefix a script description of a class to indicate it is a singleton and the single instance should be in the help +#define SCRIPT_SINGLETON "!" + +// Prefix a script description with this to indicate it should be represented using an alternate name +#define SCRIPT_ALIAS( alias, description ) "#" alias ":" description + +//--------------------------------------------------------- + +enum ScriptFuncBindingFlags_t +{ + SF_MEMBER_FUNC = 0x01, +}; + +typedef bool (*ScriptBindingFunc_t)( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ); + +struct ScriptFunctionBinding_t +{ + ScriptFuncDescriptor_t m_desc; + ScriptBindingFunc_t m_pfnBinding; + void * m_pFunction; + unsigned m_flags; +}; + +//--------------------------------------------------------- +class IScriptInstanceHelper +{ +public: + virtual void *GetProxied( void *p ) { return p; } + virtual bool ToString( void *p, char *pBuf, int bufSize ) { return false; } + virtual void *BindOnRead( HSCRIPT hInstance, void *pOld, const char *pszId ) { return NULL; } +}; + +//--------------------------------------------------------- + +struct ScriptClassDesc_t +{ + ScriptClassDesc_t() : m_pszScriptName( 0 ), m_pszClassname( 0 ), m_pszDescription( 0 ), m_pBaseDesc( 0 ), m_pfnConstruct( 0 ), m_pfnDestruct( 0 ), pHelper(NULL) {} + + const char * m_pszScriptName; + const char * m_pszClassname; + const char * m_pszDescription; + ScriptClassDesc_t * m_pBaseDesc; + CUtlVector m_FunctionBindings; + + void *(*m_pfnConstruct)(); + void (*m_pfnDestruct)( void *); + IScriptInstanceHelper * pHelper; // optional helper +}; + +//--------------------------------------------------------- +// A simple variant type. Intentionally not full featured (no implicit conversion, no memory management) +//--------------------------------------------------------- + +enum SVFlags_t +{ + SV_FREE = 0x01, +}; + +#pragma warning(push) +#pragma warning(disable:4800) +struct ScriptVariant_t +{ + ScriptVariant_t() : m_flags( 0 ), m_type( FIELD_VOID ) { m_pVector = 0; } + ScriptVariant_t( int val ) : m_flags( 0 ), m_type( FIELD_INTEGER ) { m_int = val;} + ScriptVariant_t( float val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = val; } + ScriptVariant_t( double val ) : m_flags( 0 ), m_type( FIELD_FLOAT ) { m_float = (float)val; } + ScriptVariant_t( char val ) : m_flags( 0 ), m_type( FIELD_CHARACTER ) { m_char = val; } + ScriptVariant_t( bool val ) : m_flags( 0 ), m_type( FIELD_BOOLEAN ) { m_bool = val; } + ScriptVariant_t( HSCRIPT val ) : m_flags( 0 ), m_type( FIELD_HSCRIPT ) { m_hScript = val; } + + ScriptVariant_t( const Vector &val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = &val; } else { m_pVector = new Vector( val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const Vector *val, bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_VECTOR ) { if ( !bCopy ) { m_pVector = val; } else { m_pVector = new Vector( *val ); m_flags |= SV_FREE; } } + ScriptVariant_t( const char *val , bool bCopy = false ) : m_flags( 0 ), m_type( FIELD_CSTRING ) { if ( !bCopy ) { m_pszString = val; } else { m_pszString = strdup( val ); m_flags |= SV_FREE; } } + + bool IsNull() const { return (m_type == FIELD_VOID ); } + + operator int() const { Assert( m_type == FIELD_INTEGER ); return m_int; } + operator float() const { Assert( m_type == FIELD_FLOAT ); return m_float; } + operator const char *() const { Assert( m_type == FIELD_CSTRING ); return ( m_pszString ) ? m_pszString : ""; } + operator const Vector &() const { Assert( m_type == FIELD_VECTOR ); static Vector vecNull(0, 0, 0); return (m_pVector) ? *m_pVector : vecNull; } + operator char() const { Assert( m_type == FIELD_CHARACTER ); return m_char; } + operator bool() const { Assert( m_type == FIELD_BOOLEAN ); return m_bool; } + operator HSCRIPT() const { Assert( m_type == FIELD_HSCRIPT ); return m_hScript; } + + void operator=( int i ) { m_type = FIELD_INTEGER; m_int = i; } + void operator=( float f ) { m_type = FIELD_FLOAT; m_float = f; } + void operator=( double f ) { m_type = FIELD_FLOAT; m_float = (float)f; } + void operator=( const Vector &vec ) { m_type = FIELD_VECTOR; m_pVector = &vec; } + void operator=( const Vector *vec ) { m_type = FIELD_VECTOR; m_pVector = vec; } + void operator=( const char *psz ) { m_type = FIELD_CSTRING; m_pszString = psz; } + void operator=( char c ) { m_type = FIELD_CHARACTER; m_char = c; } + void operator=( bool b ) { m_type = FIELD_BOOLEAN; m_bool = b; } + void operator=( HSCRIPT h ) { m_type = FIELD_HSCRIPT; m_hScript = h; } + + void Free() { if ( ( m_flags & SV_FREE ) && ( m_type == FIELD_HSCRIPT || m_type == FIELD_VECTOR || m_type == FIELD_CSTRING ) ) delete m_pszString; } // Generally only needed for return results + + template + T Get() + { + T value; + AssignTo( &value ); + return value; + } + + template + bool AssignTo( T *pDest ) + { + ScriptDataType_t destType = ScriptDeduceType( T ); + if ( destType == FIELD_TYPEUNKNOWN ) + { + DevWarning( "Unable to convert script variant to unknown type\n" ); + } + if ( destType == m_type ) + { + *pDest = *this; + return true; + } + + if ( m_type != FIELD_VECTOR && m_type != FIELD_CSTRING && destType != FIELD_VECTOR && destType != FIELD_CSTRING ) + { + switch ( m_type ) + { + case FIELD_VOID: *pDest = 0; break; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_CHARACTER: *pDest = m_char; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + case FIELD_HSCRIPT: *pDest = m_hScript; return true; + } + } + else + { + DevWarning( "No free conversion of %s script variant to %s right now\n", + ScriptFieldTypeName( m_type ), ScriptFieldTypeName() ); + if ( destType != FIELD_VECTOR ) + { + *pDest = 0; + } + } + return false; + } + + bool AssignTo( float *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to float now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( int *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to int now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( bool *pDest ) + { + switch( m_type ) + { + case FIELD_VOID: *pDest = 0; return false; + case FIELD_INTEGER: *pDest = m_int; return true; + case FIELD_FLOAT: *pDest = m_float; return true; + case FIELD_BOOLEAN: *pDest = m_bool; return true; + default: + DevWarning( "No conversion from %s to bool now\n", ScriptFieldTypeName( m_type ) ); + return false; + } + } + + bool AssignTo( char **pDest ) + { + DevWarning( "No free conversion of string or vector script variant right now\n" ); + // If want to support this, probably need to malloc string and require free on other side [3/24/2008 tom] + *pDest = (char*)""; + return false; + } + + bool AssignTo( ScriptVariant_t *pDest ) + { + pDest->m_type = m_type; + if ( m_type == FIELD_VECTOR ) + { + pDest->m_pVector = new Vector; + ((Vector *)(pDest->m_pVector))->Init( m_pVector->x, m_pVector->y, m_pVector->z ); + pDest->m_flags |= SV_FREE; + } + else if ( m_type == FIELD_CSTRING ) + { + pDest->m_pszString = strdup( m_pszString ); + pDest->m_flags |= SV_FREE; + } + else + { + pDest->m_int = m_int; + } + return false; + } + + union + { + int m_int; + float m_float; + const char * m_pszString; + const Vector * m_pVector; + char m_char; + bool m_bool; + HSCRIPT m_hScript; + }; + + int16 m_type; + int16 m_flags; + +private: +}; + +#define SCRIPT_VARIANT_NULL ScriptVariant_t() + +#pragma warning(pop) + + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#include "vscript_templates.h" + +// Lower level macro primitives +#define ScriptInitFunctionBinding( pScriptFunction, func ) ScriptInitFunctionBindingNamed( pScriptFunction, func, #func ) +#define ScriptInitFunctionBindingNamed( pScriptFunction, func, scriptName ) do { ScriptInitFuncDescriptorNamed( (&(pScriptFunction)->m_desc), func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( &func ); (pScriptFunction)->m_pFunction = (void *)&func; } while (0) + +#define ScriptInitMemberFunctionBinding( pScriptFunction, class, func ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, #func ) +#define ScriptInitMemberFunctionBindingNamed( pScriptFunction, class, func, scriptName ) ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) +#define ScriptInitMemberFunctionBinding_( pScriptFunction, class, func, scriptName ) do { ScriptInitMemberFuncDescriptor_( (&(pScriptFunction)->m_desc), class, func, scriptName ); (pScriptFunction)->m_pfnBinding = ScriptCreateBinding( ((class *)0), &class::func ); (pScriptFunction)->m_pFunction = ScriptConvertFuncPtrToVoid( &class::func ); (pScriptFunction)->m_flags = SF_MEMBER_FUNC; } while (0) + +#define ScriptInitClassDesc( pClassDesc, class, pBaseClassDesc ) ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, #class ) +#define ScriptInitClassDescNamed( pClassDesc, class, pBaseClassDesc, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) +#define ScriptInitClassDescNoBase( pClassDesc, class ) ScriptInitClassDescNoBaseNamed( pClassDesc, class, #class ) +#define ScriptInitClassDescNoBaseNamed( pClassDesc, class, scriptName ) ScriptInitClassDescNamed_( pClassDesc, class, NULL, scriptName ) +#define ScriptInitClassDescNamed_( pClassDesc, class, pBaseClassDesc, scriptName ) do { (pClassDesc)->m_pszScriptName = scriptName; (pClassDesc)->m_pszClassname = #class; (pClassDesc)->m_pBaseDesc = pBaseClassDesc; } while ( 0 ) + +#define ScriptAddFunctionToClassDesc( pClassDesc, class, func, description ) ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, #func, description ) +#define ScriptAddFunctionToClassDescNamed( pClassDesc, class, func, scriptName, description ) do { ScriptFunctionBinding_t *pBinding = &((pClassDesc)->m_FunctionBindings[(pClassDesc)->m_FunctionBindings.AddToTail()]); pBinding->m_desc.m_pszDescription = description; ScriptInitMemberFunctionBindingNamed( pBinding, class, func, scriptName ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ScriptRegisterFunction( pVM, func, description ) ScriptRegisterFunctionNamed( pVM, func, #func, description ) +#define ScriptRegisterFunctionNamed( pVM, func, scriptName, description ) do { static ScriptFunctionBinding_t binding; binding.m_desc.m_pszDescription = description; binding.m_desc.m_Parameters.RemoveAll(); ScriptInitFunctionBindingNamed( &binding, func, scriptName ); pVM->RegisterFunction( &binding ); } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) + +#ifdef MSVC + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + ScriptClassDesc_t * GetScriptDesc( className * ) +#else + #define DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + template <> ScriptClassDesc_t * GetScriptDesc( baseClass *); \ + template <> ScriptClassDesc_t * GetScriptDesc( className *) +#endif + +#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ + ScriptClassDesc_t g_##className##_ScriptDesc; \ + DEFINE_SCRIPTDESC_FUNCTION( className, baseClass ) \ + { \ + static bool bInitialized; \ + if ( bInitialized ) \ + { \ + return &g_##className##_ScriptDesc; \ + } \ + \ + bInitialized = true; \ + \ + typedef className _className; \ + ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ + pDesc->m_pszDescription = description; \ + ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ + ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ + while ( pInstanceHelperBase ) \ + { \ + if ( pInstanceHelperBase->pHelper ) \ + { \ + pDesc->pHelper = pInstanceHelperBase->pHelper; \ + break; \ + } \ + pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ + } + + +#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ + BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + +#define END_SCRIPTDESC() \ + return pDesc; \ + } + +#define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) +#define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); +#define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); + +template ScriptClassDesc_t *GetScriptDesc(T *); + +struct ScriptNoBase_t; +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } + +#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +class CScriptConstructor +{ +public: + static void *Construct() { return new T; } + static void Destruct( void *p ) { delete (T *)p; } +}; + +#define ScriptAddConstructorToClassDesc( pClassDesc, class ) do { (pClassDesc)->m_pfnConstruct = &CScriptConstructor::Construct; (pClassDesc)->m_pfnDestruct = &CScriptConstructor::Destruct; } while (0) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +enum ScriptErrorLevel_t +{ + SCRIPT_LEVEL_WARNING = 0, + SCRIPT_LEVEL_ERROR, +}; + +typedef void ( *ScriptOutputFunc_t )( const char *pszText ); +typedef bool ( *ScriptErrorFunc_t )( ScriptErrorLevel_t eLevel, const char *pszText ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef RegisterClass +#undef RegisterClass +#endif + +enum ScriptStatus_t +{ + SCRIPT_ERROR = -1, + SCRIPT_DONE, + SCRIPT_RUNNING, +}; + +class IScriptVM +{ +public: + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual bool ConnectDebugger() = 0; + virtual void DisconnectDebugger() = 0; + + virtual ScriptLanguage_t GetLanguage() = 0; + virtual const char *GetLanguageName() = 0; + + virtual void AddSearchPath( const char *pszSearchPath ) = 0; + + //-------------------------------------------------------- + + virtual bool Frame( float simTime ) = 0; + + //-------------------------------------------------------- + // Simple script usage + //-------------------------------------------------------- + virtual ScriptStatus_t Run( const char *pszScript, bool bWait = true ) = 0; + inline ScriptStatus_t Run( const unsigned char *pszScript, bool bWait = true ) { return Run( (char *)pszScript, bWait ); } + + //-------------------------------------------------------- + // Compilation + //-------------------------------------------------------- + virtual HSCRIPT CompileScript( const char *pszScript, const char *pszId = NULL ) = 0; + inline HSCRIPT CompileScript( const unsigned char *pszScript, const char *pszId = NULL ) { return CompileScript( (char *)pszScript, pszId ); } + virtual void ReleaseScript( HSCRIPT ) = 0; + + //-------------------------------------------------------- + // Execution of compiled + //-------------------------------------------------------- + virtual ScriptStatus_t Run( HSCRIPT hScript, HSCRIPT hScope = NULL, bool bWait = true ) = 0; + virtual ScriptStatus_t Run( HSCRIPT hScript, bool bWait ) = 0; + + //-------------------------------------------------------- + // Scope + //-------------------------------------------------------- + virtual HSCRIPT CreateScope( const char *pszScope, HSCRIPT hParent = NULL ) = 0; + virtual void ReleaseScope( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions + //-------------------------------------------------------- + virtual HSCRIPT LookupFunction( const char *pszFunction, HSCRIPT hScope = NULL ) = 0; + virtual void ReleaseFunction( HSCRIPT hScript ) = 0; + + //-------------------------------------------------------- + // Script functions (raw, use Call()) + //-------------------------------------------------------- + virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; + + //-------------------------------------------------------- + // External functions + //-------------------------------------------------------- + virtual void RegisterFunction( ScriptFunctionBinding_t *pScriptFunction ) = 0; + + //-------------------------------------------------------- + // External classes + //-------------------------------------------------------- + virtual bool RegisterClass( ScriptClassDesc_t *pClassDesc ) = 0; + + //-------------------------------------------------------- + // External instances. Note class will be auto-registered. + //-------------------------------------------------------- + + virtual HSCRIPT RegisterInstance( ScriptClassDesc_t *pDesc, void *pInstance ) = 0; + virtual void SetInstanceUniqeId( HSCRIPT hInstance, const char *pszId ) = 0; + template HSCRIPT RegisterInstance( T *pInstance ) { return RegisterInstance( GetScriptDesc( pInstance ), pInstance ); } + template HSCRIPT RegisterInstance( T *pInstance, const char *pszInstance, HSCRIPT hScope = NULL) { HSCRIPT hInstance = RegisterInstance( GetScriptDesc( pInstance ), pInstance ); SetValue( hScope, pszInstance, hInstance ); return hInstance; } + virtual void RemoveInstance( HSCRIPT ) = 0; + void RemoveInstance( HSCRIPT hInstance, const char *pszInstance, HSCRIPT hScope = NULL ) { ClearValue( hScope, pszInstance ); RemoveInstance( hInstance ); } + void RemoveInstance( const char *pszInstance, HSCRIPT hScope = NULL ) { ScriptVariant_t val; if ( GetValue( hScope, pszInstance, &val ) ) { if ( val.m_type == FIELD_HSCRIPT ) { RemoveInstance( val, pszInstance, hScope ); } ReleaseValue( val ); } } + + virtual void *GetInstanceValue( HSCRIPT hInstance, ScriptClassDesc_t *pExpectedType = NULL ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool GenerateUniqueKey( const char *pszRoot, char *pBuf, int nBufSize ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool ValueExists( HSCRIPT hScope, const char *pszKey ) = 0; + bool ValueExists( const char *pszKey ) { return ValueExists( NULL, pszKey ); } + + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const char *pszValue ) = 0; + virtual bool SetValue( HSCRIPT hScope, const char *pszKey, const ScriptVariant_t &value ) = 0; + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return SetValue(NULL, pszKey, value ); } + + virtual void CreateTable( ScriptVariant_t &Table ) = 0; + virtual int GetNumTableEntries( HSCRIPT hScope ) = 0; + virtual int GetKeyValue( HSCRIPT hScope, int nIterator, ScriptVariant_t *pKey, ScriptVariant_t *pValue ) = 0; + + virtual bool GetValue( HSCRIPT hScope, const char *pszKey, ScriptVariant_t *pValue ) = 0; + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetValue(NULL, pszKey, pValue ); } + virtual void ReleaseValue( ScriptVariant_t &value ) = 0; + + virtual bool ClearValue( HSCRIPT hScope, const char *pszKey ) = 0; + bool ClearValue( const char *pszKey) { return ClearValue( NULL, pszKey ); } + + //---------------------------------------------------------------------------- + + virtual void WriteState( CUtlBuffer *pBuffer ) = 0; + virtual void ReadState( CUtlBuffer *pBuffer ) = 0; + virtual void RemoveOrphanInstances() = 0; + + virtual void DumpState() = 0; + + virtual void SetOutputCallback( ScriptOutputFunc_t pFunc ) = 0; + virtual void SetErrorCallback( ScriptErrorFunc_t pFunc ) = 0; + + //---------------------------------------------------------------------------- + + virtual bool RaiseException( const char *pszExceptionText ) = 0; + + //---------------------------------------------------------------------------- + // Call API + // + // Note for string and vector return types, the caller must delete the pointed to memory + //---------------------------------------------------------------------------- + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope = NULL, bool bWait = true, ScriptVariant_t *pReturn = NULL ) + { + return ExecuteFunction( hFunction, NULL, 0, pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, HSCRIPT hScope, bool bWait, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, hScope, bWait ); + } + +}; + + +//----------------------------------------------------------------------------- +// Script scope helper class +//----------------------------------------------------------------------------- + +class CDefScriptScopeBase +{ +public: + static IScriptVM *GetVM() + { + extern IScriptVM *g_pScriptVM; + return g_pScriptVM; + } +}; + +template +class CScriptScopeT : public CDefScriptScopeBase +{ +public: + CScriptScopeT() : + m_hScope( INVALID_HSCRIPT ), + m_flags( 0 ) + { + } + + ~CScriptScopeT() + { + Term(); + } + + bool IsInitialized() + { + return m_hScope != INVALID_HSCRIPT; + } + + bool Init( const char *pszName ) + { + m_hScope = GetVM()->CreateScope( pszName ); + return ( m_hScope != NULL ); + } + + bool Init( HSCRIPT hScope, bool bExternal = true ) + { + if ( bExternal ) + { + m_flags |= EXTERNAL; + } + m_hScope = hScope; + return ( m_hScope != NULL ); + } + + bool InitGlobal() + { + Assert( 0 ); // todo [3/24/2008 tom] + m_hScope = GetVM()->CreateScope( "" ); + return ( m_hScope != NULL ); + } + + void Term() + { + if ( m_hScope != INVALID_HSCRIPT ) + { + IScriptVM *pVM = GetVM(); + if ( pVM ) + { + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + pVM->ReleaseFunction( *m_FuncHandles[i] ); + } + } + m_FuncHandles.Purge(); + if ( m_hScope && pVM && !(m_flags & EXTERNAL) ) + { + pVM->ReleaseScope( m_hScope ); + } + m_hScope = INVALID_HSCRIPT; + } + m_flags = 0; + } + + void InvalidateCachedValues() + { + IScriptVM *pVM = GetVM(); + for ( int i = 0; i < m_FuncHandles.Count(); i++ ) + { + if ( *m_FuncHandles[i] ) + pVM->ReleaseFunction( *m_FuncHandles[i] ); + *m_FuncHandles[i] = INVALID_HSCRIPT; + } + m_FuncHandles.RemoveAll(); + } + + operator HSCRIPT() + { + return ( m_hScope != INVALID_HSCRIPT ) ? m_hScope : NULL; + } + + bool ValueExists( const char *pszKey ) { return GetVM()->ValueExists( m_hScope, pszKey ); } + bool SetValue( const char *pszKey, const ScriptVariant_t &value ) { return GetVM()->SetValue(m_hScope, pszKey, value ); } + bool GetValue( const char *pszKey, ScriptVariant_t *pValue ) { return GetVM()->GetValue(m_hScope, pszKey, pValue ); } + void ReleaseValue( ScriptVariant_t &value ) { GetVM()->ReleaseValue( value ); } + bool ClearValue( const char *pszKey) { return GetVM()->ClearValue( m_hScope, pszKey ); } + + ScriptStatus_t Run( HSCRIPT hScript ) + { + InvalidateCachedValues(); + return GetVM()->Run( hScript, m_hScope ); + } + + ScriptStatus_t Run( const char *pszScriptText, const char *pszScriptName = NULL ) + { + InvalidateCachedValues(); + HSCRIPT hScript = GetVM()->CompileScript( pszScriptText, pszScriptName ); + if ( hScript ) + { + ScriptStatus_t result = GetVM()->Run( hScript, m_hScope ); + GetVM()->ReleaseScript( hScript ); + return result; + } + return SCRIPT_ERROR; + } + + ScriptStatus_t Run( const unsigned char *pszScriptText, const char *pszScriptName = NULL ) + { + return Run( (const char *)pszScriptText, pszScriptName); + } + + HSCRIPT LookupFunction( const char *pszFunction ) + { + return GetVM()->LookupFunction( pszFunction, m_hScope ); + } + + void ReleaseFunction( HSCRIPT hScript ) + { + GetVM()->ReleaseFunction( hScript ); + } + + bool FunctionExists( const char *pszFunction ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + GetVM()->ReleaseFunction( hFunction ); + return ( hFunction != NULL ) ; + } + + //----------------------------------------------------- + + enum Flags_t + { + EXTERNAL = 0x01, + }; + + //----------------------------------------------------- + + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn = NULL ) + { + return GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + template + ScriptStatus_t Call( HSCRIPT hFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + return GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + } + + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn = NULL ) + { + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, NULL, 0, pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1 ) + { + ScriptVariant_t args[1]; args[0] = arg1; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2 ) + { + ScriptVariant_t args[2]; args[0] = arg1; args[1] = arg2; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3 ) + { + ScriptVariant_t args[3]; args[0] = arg1; args[1] = arg2; args[2] = arg3; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4 ) + { + ScriptVariant_t args[4]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5 ) + { + ScriptVariant_t args[5]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6 ) + { + ScriptVariant_t args[6]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7 ) + { + ScriptVariant_t args[7]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8 ) + { + ScriptVariant_t args[8]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9 ) + { + ScriptVariant_t args[9]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10 ) + { + ScriptVariant_t args[10]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11 ) + { + ScriptVariant_t args[11]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12 ) + { + ScriptVariant_t args[12]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13 ) + { + ScriptVariant_t args[13]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + + template + ScriptStatus_t Call( const char *pszFunction, ScriptVariant_t *pReturn, ARG_TYPE_1 arg1, ARG_TYPE_2 arg2, ARG_TYPE_3 arg3, ARG_TYPE_4 arg4, ARG_TYPE_5 arg5, ARG_TYPE_6 arg6, ARG_TYPE_7 arg7, ARG_TYPE_8 arg8, ARG_TYPE_9 arg9, ARG_TYPE_10 arg10, ARG_TYPE_11 arg11, ARG_TYPE_12 arg12, ARG_TYPE_13 arg13, ARG_TYPE_14 arg14 ) + { + ScriptVariant_t args[14]; args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8; args[8] = arg9; args[9] = arg10; args[10] = arg11; args[11] = arg12; args[12] = arg13; args[13] = arg14; + HSCRIPT hFunction = GetVM()->LookupFunction( pszFunction, m_hScope ); + if ( !hFunction ) + return SCRIPT_ERROR; + ScriptStatus_t status = GetVM()->ExecuteFunction( hFunction, args, ARRAYSIZE(args), pReturn, m_hScope, true ); + GetVM()->ReleaseFunction( hFunction ); + return status; + } + +protected: + HSCRIPT m_hScope; + int m_flags; + CUtlVectorConservative m_FuncHandles; +}; + +typedef CScriptScopeT<> CScriptScope; + +#define VScriptAddEnumToScope_( scope, enumVal, scriptName ) (scope).SetValue( scriptName, (int)enumVal ) +#define VScriptAddEnumToScope( scope, enumVal ) VScriptAddEnumToScope_( scope, enumVal, #enumVal ) + +#define VScriptAddEnumToRoot( enumVal ) g_pScriptVM->SetValue( #enumVal, (int)enumVal ) + +//----------------------------------------------------------------------------- +// Script function proxy support +//----------------------------------------------------------------------------- + +class CScriptFuncHolder +{ +public: + CScriptFuncHolder() : hFunction( INVALID_HSCRIPT ) {} + bool IsValid() { return ( hFunction != INVALID_HSCRIPT ); } + bool IsNull() { return ( !hFunction ); } + HSCRIPT hFunction; +}; + +#define DEFINE_SCRIPT_PROXY_GUTS( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < typename RET_TYPE FUNC_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( RET_TYPE *pRetVal FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptVariant_t returnVal; \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, &returnVal, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + returnVal.AssignTo( pRetVal ); \ + returnVal.Free(); \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, N ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + template < FUNC_SOLO_TEMPLATE_ARG_PARAMS_##N> \ + bool FuncName( FUNC_PROXY_ARG_FORMAL_PARAMS_##N ) \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL, FUNC_CALL_ARGS_##N ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0V( FuncName ) \ + CScriptFuncHolder m_hScriptFunc_##FuncName; \ + bool FuncName() \ + { \ + if ( !m_hScriptFunc_##FuncName.IsValid() ) \ + { \ + m_hScriptFunc_##FuncName.hFunction = LookupFunction( #FuncName ); \ + m_FuncHandles.AddToTail( &m_hScriptFunc_##FuncName.hFunction ); \ + } \ + \ + if ( !m_hScriptFunc_##FuncName.IsNull() ) \ + { \ + ScriptStatus_t result = Call( m_hScriptFunc_##FuncName.hFunction, NULL ); \ + if ( result != SCRIPT_ERROR ) \ + { \ + return true; \ + } \ + } \ + return false; \ + } + +#define DEFINE_SCRIPT_PROXY_0( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 0 ) +#define DEFINE_SCRIPT_PROXY_1( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14( FuncName ) DEFINE_SCRIPT_PROXY_GUTS( FuncName, 14 ) + +#define DEFINE_SCRIPT_PROXY_1V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 1 ) +#define DEFINE_SCRIPT_PROXY_2V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 2 ) +#define DEFINE_SCRIPT_PROXY_3V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 3 ) +#define DEFINE_SCRIPT_PROXY_4V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 4 ) +#define DEFINE_SCRIPT_PROXY_5V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 5 ) +#define DEFINE_SCRIPT_PROXY_6V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 6 ) +#define DEFINE_SCRIPT_PROXY_7V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 7 ) +#define DEFINE_SCRIPT_PROXY_8V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 8 ) +#define DEFINE_SCRIPT_PROXY_9V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 9 ) +#define DEFINE_SCRIPT_PROXY_10V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 10 ) +#define DEFINE_SCRIPT_PROXY_11V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 11 ) +#define DEFINE_SCRIPT_PROXY_12V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 12 ) +#define DEFINE_SCRIPT_PROXY_13V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 13 ) +#define DEFINE_SCRIPT_PROXY_14V( FuncName ) DEFINE_SCRIPT_PROXY_GUTS_NO_RETVAL( FuncName, 14 ) + +//----------------------------------------------------------------------------- + +#include "tier0/memdbgoff.h" + +#endif // IVSCRIPT_H diff --git a/public/vscript/vscript_templates.h b/public/vscript/vscript_templates.h new file mode 100644 index 0000000..aa83fbc --- /dev/null +++ b/public/vscript/vscript_templates.h @@ -0,0 +1,414 @@ +//========== Copyright © 2008, Valve Corporation, All rights reserved. ======== +// +// Purpose: +// +//============================================================================= + +#ifndef VSCRIPT_TEMPLATES_H +#define VSCRIPT_TEMPLATES_H + +#include "tier0/basetypes.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +#define FUNC_APPEND_PARAMS_0 +#define FUNC_APPEND_PARAMS_1 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 1 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); +#define FUNC_APPEND_PARAMS_2 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 2 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); +#define FUNC_APPEND_PARAMS_3 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 3 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); +#define FUNC_APPEND_PARAMS_4 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 4 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); +#define FUNC_APPEND_PARAMS_5 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 5 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); +#define FUNC_APPEND_PARAMS_6 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 6 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); +#define FUNC_APPEND_PARAMS_7 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 7 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); +#define FUNC_APPEND_PARAMS_8 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 8 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); +#define FUNC_APPEND_PARAMS_9 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 9 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); +#define FUNC_APPEND_PARAMS_10 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 10 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); +#define FUNC_APPEND_PARAMS_11 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 11 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); +#define FUNC_APPEND_PARAMS_12 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 12 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); +#define FUNC_APPEND_PARAMS_13 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 13 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); +#define FUNC_APPEND_PARAMS_14 pDesc->m_Parameters.SetGrowSize( 1 ); pDesc->m_Parameters.EnsureCapacity( 14 ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_1 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_2 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_3 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_4 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_5 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_6 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_7 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_8 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_9 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_10 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_11 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_12 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_13 ) ); pDesc->m_Parameters.AddToTail( ScriptDeduceType( FUNC_ARG_TYPE_14 ) ); + +#define DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_NONMEMBER_FUNC_TYPE_DEDUCER ); + +#define DEFINE_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); + +//------------------------------------- + +#define DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER(N) \ + template \ + inline void ScriptDeduceFunctionSignature(ScriptFuncDescriptor_t *pDesc, OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + pDesc->m_ReturnType = ScriptDeduceType(FUNCTION_RETTYPE); \ + FUNC_APPEND_PARAMS_##N \ + } + +FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); + +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } + +#define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } +#define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) +#define ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, scriptName ) ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) +#define ScriptInitMemberFuncDescriptor( pDesc, class, func ) ScriptInitMemberFuncDescriptorNamed( pDesc, class, func, #func ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +template +inline void *ScriptConvertFuncPtrToVoid( FUNCPTR_TYPE pFunc ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.pFunc = pFunc; + return convert.p; + } +#if MSVC + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.pFunc = pFunc; + if ( convert.mfp.m_delta == 0 ) + { + return convert.mfp.p; + } + AssertMsg( 0, "Function pointer must be from primary vtable" ); + } +#elif defined( GNUC ) + else if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + AssertMsg( 0, "Note: This path has not been verified yet. See comments below in #else case." ); + + struct GnuMFP + { + union + { + void *funcadr; // If vtable_index_2 is even, then this is the function pointer. + int vtable_index_2; // If vtable_index_2 is odd, then this = vindex*2+1. + }; + int delta; + }; + + GnuMFP *p = (GnuMFP*)&pFunc; + if ( p->vtable_index_2 & 1 ) + { + char **delta = (char**)p->delta; + char *pCur = *delta + (p->vtable_index_2+1)/2; + return (void*)( pCur + 4 ); + } + else + { + return p->funcadr; + } + } +#else +#error "Need to implement code to crack non-offset member function pointer case" + // For gcc, see: http://www.codeproject.com/KB/cpp/FastDelegate.aspx + // + // Current versions of the GNU compiler use a strange and tricky + // optimization. It observes that, for virtual inheritance, you have to look + // up the vtable in order to get the voffset required to calculate the this + // pointer. While you're doing that, you might as well store the function + // pointer in the vtable. By doing this, they combine the m_func_address and + // m_vtable_index fields into one, and they distinguish between them by + // ensuring that function pointers always point to even addresses but vtable + // indices are always odd: + // + // // GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC. + // struct GnuMFP { + // union { + // CODEPTR funcadr; // always even + // int vtable_index_2; // = vindex*2+1, always odd + // }; + // int delta; + // }; + // adjustedthis = this + delta + // if (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4) + // else CALL funcadr + // + // The G++ method is well documented, so it has been adopted by many other + // vendors, including IBM's VisualAge and XLC compilers, recent versions of + // Open64, Pathscale EKO, and Metrowerks' 64-bit compilers. A simpler scheme + // used by earlier versions of GCC is also very common. SGI's now + // discontinued MIPSPro and Pro64 compilers, and Apple's ancient MrCpp + // compiler used this method. (Note that the Pro64 compiler has become the + // open source Open64 compiler). + +#endif + else + AssertMsg( 0, "Member function pointer not supported. Why on earth are you using virtual inheritance!?" ); + return NULL; +} + +template +inline FUNCPTR_TYPE ScriptConvertFuncPtrFromVoid( void *p ) +{ + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) ) ) + { + union FuncPtrConvert + { + void *p; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvert convert; + convert.p = p; + return convert.pFunc; + } + +#if MSVC + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + sizeof( int ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } + if ( ( sizeof( FUNCPTR_TYPE ) == sizeof( void * ) + ( sizeof( int ) * 3 ) ) ) + { + struct MicrosoftUnknownMFP + { + void *p; + int m_delta; + int m_vtordisp; + int m_vtable_index; + }; + + union FuncPtrConvertMI + { + MicrosoftUnknownMFP mfp; + FUNCPTR_TYPE pFunc; + }; + + FuncPtrConvertMI convert; + convert.mfp.p = p; + convert.mfp.m_delta = 0; + return convert.pFunc; + } +#elif defined( POSIX ) + AssertMsg( 0, "Note: This path has not been implemented yet." ); +#else +#error "Need to implement code to crack non-offset member function pointer case" +#endif + Assert( 0 ); + return NULL; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_0 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_1 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_1 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_2 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_2 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_3 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_3 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_4 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_4 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_5 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_5 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_6 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_6 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_7 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_7 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_8 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_8 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_9 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_9 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_10 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_10 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_11 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_11 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_12 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_12 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_13 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_13 +#define FUNC_BASE_TEMPLATE_FUNC_PARAMS_PASSTHRU_14 , FUNC_BASE_TEMPLATE_FUNC_PARAMS_14 + +#define SCRIPT_BINDING_ARGS_0 +#define SCRIPT_BINDING_ARGS_1 pArguments[0] +#define SCRIPT_BINDING_ARGS_2 pArguments[0], pArguments[1] +#define SCRIPT_BINDING_ARGS_3 pArguments[0], pArguments[1], pArguments[2] +#define SCRIPT_BINDING_ARGS_4 pArguments[0], pArguments[1], pArguments[2], pArguments[3] +#define SCRIPT_BINDING_ARGS_5 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4] +#define SCRIPT_BINDING_ARGS_6 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5] +#define SCRIPT_BINDING_ARGS_7 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6] +#define SCRIPT_BINDING_ARGS_8 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7] +#define SCRIPT_BINDING_ARGS_9 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8] +#define SCRIPT_BINDING_ARGS_10 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9] +#define SCRIPT_BINDING_ARGS_11 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10] +#define SCRIPT_BINDING_ARGS_12 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11] +#define SCRIPT_BINDING_ARGS_13 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12] +#define SCRIPT_BINDING_ARGS_14 pArguments[0], pArguments[1], pArguments[2], pArguments[3], pArguments[4], pArguments[5], pArguments[6], pArguments[7], pArguments[8], pArguments[9], pArguments[10], pArguments[11], pArguments[12], pArguments[13] + + +#define DEFINE_SCRIPT_BINDINGS(N) \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || !pReturn || pContext ) \ + { \ + return false; \ + } \ + *pReturn = ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CNonMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( !pContext ); \ + \ + if ( nArguments != N || pReturn || pContext ) \ + { \ + return false; \ + } \ + ((FUNC_TYPE)pFunction)( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || !pReturn || !pContext ) \ + { \ + return false; \ + } \ + *pReturn = (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + if ( pReturn->m_type == FIELD_VECTOR ) \ + pReturn->m_pVector = new Vector(*pReturn->m_pVector); \ + return true; \ + } \ + }; \ + \ + template \ + class CMemberScriptBinding##N \ + { \ + public: \ + static bool Call( void *pFunction, void *pContext, ScriptVariant_t *pArguments, int nArguments, ScriptVariant_t *pReturn ) \ + { \ + Assert( nArguments == N ); \ + Assert( !pReturn ); \ + Assert( pContext ); \ + \ + if ( nArguments != N || pReturn || !pContext ) \ + { \ + return false; \ + } \ + (((OBJECT_TYPE_PTR)(pContext))->*ScriptConvertFuncPtrFromVoid(pFunction))( SCRIPT_BINDING_ARGS_##N ); \ + return true; \ + } \ + }; \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CNonMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } \ + \ + template \ + inline ScriptBindingFunc_t ScriptCreateBinding(OBJECT_TYPE_PTR pObject, FUNCTION_RETTYPE (FUNCTION_CLASS::*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const ) \ + { \ + typedef FUNCTION_RETTYPE (FUNCTION_CLASS::*Func_t)(FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N); \ + return &CMemberScriptBinding##N::Call; \ + } + +FUNC_GENERATE_ALL( DEFINE_SCRIPT_BINDINGS ); + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#endif // VSCRIPT_TEMPLATES_H diff --git a/public/vstdlib/IKeyValuesSystem.h b/public/vstdlib/IKeyValuesSystem.h new file mode 100644 index 0000000..3477ed3 --- /dev/null +++ b/public/vstdlib/IKeyValuesSystem.h @@ -0,0 +1,56 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef VSTDLIB_IKEYVALUESSYSTEM_H +#define VSTDLIB_IKEYVALUESSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vstdlib/vstdlib.h" + +// handle to a KeyValues key name symbol +typedef int HKeySymbol; +#define INVALID_KEY_SYMBOL (-1) + +//----------------------------------------------------------------------------- +// Purpose: Interface to shared data repository for KeyValues (included in vgui_controls.lib) +// allows for central data storage point of KeyValues symbol table +//----------------------------------------------------------------------------- +class IKeyValuesSystem +{ +public: + // registers the size of the KeyValues in the specified instance + // so it can build a properly sized memory pool for the KeyValues objects + // the sizes will usually never differ but this is for versioning safety + virtual void RegisterSizeofKeyValues(int size) = 0; + + // allocates/frees a KeyValues object from the shared mempool + virtual void *AllocKeyValuesMemory(int size) = 0; + virtual void FreeKeyValuesMemory(void *pMem) = 0; + + // symbol table access (used for key names) + virtual HKeySymbol GetSymbolForString( const char *name, bool bCreate = true ) = 0; + virtual const char *GetStringForSymbol(HKeySymbol symbol) = 0; + + // for debugging, adds KeyValues record into global list so we can track memory leaks + virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name) = 0; + virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem) = 0; + + // set/get a value for keyvalues resolution symbol + // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] + virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue ) = 0; + virtual bool GetKeyValuesExpressionSymbol( const char *name ) = 0; + + // symbol table access from code with case-preserving requirements (used for key names) + virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true ) = 0; +}; + +VSTDLIB_INTERFACE IKeyValuesSystem *KeyValuesSystem(); + +// #define KEYVALUESSYSTEM_INTERFACE_VERSION "KeyValuesSystem002" + +#endif // VSTDLIB_IKEYVALUESSYSTEM_H diff --git a/public/vstdlib/cvar.h b/public/vstdlib/cvar.h new file mode 100644 index 0000000..1f26e0d --- /dev/null +++ b/public/vstdlib/cvar.h @@ -0,0 +1,25 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined( CVAR_H ) +#define CVAR_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vstdlib/vstdlib.h" +#include "icvar.h" + + +//----------------------------------------------------------------------------- +// Returns a CVar dictionary for tool usage +//----------------------------------------------------------------------------- +VSTDLIB_INTERFACE CreateInterfaceFn VStdLib_GetICVarFactory(); + + +#endif // CVAR_H diff --git a/public/vstdlib/iprocessutils.h b/public/vstdlib/iprocessutils.h new file mode 100644 index 0000000..1ee5a39 --- /dev/null +++ b/public/vstdlib/iprocessutils.h @@ -0,0 +1,131 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IPROCESSUTILS_H +#define IPROCESSUTILS_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "appframework/iappsystem.h" +#include "appframework/IAppSystem.h" +#include "tier1/utlstring.h" +#include "tier1/utlbuffer.h" + + +//----------------------------------------------------------------------------- +// Handle to a process. This is only for b/w compatibility. +//----------------------------------------------------------------------------- +class IProcess; + + +//----------------------------------------------------------------------------- +// Interface version +//----------------------------------------------------------------------------- +#define PIPEREAD_INFINITE INT32_MAX + +abstract_class IPipeRead +{ +public: + // NONBLOCKING FUNCTIONS + + // Returns how much you can Read() without blocking. + virtual int GetNumBytesAvailable() = 0; + + // Read whatever is available without blocking. + // This is the same as Read( sStr, GetNumBytesAvailable() ). + virtual void ReadAvailable( CUtlString &sStr, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; + virtual void ReadAvailable( CUtlBuffer* pOutBuffer, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; + + + // (POTENTIALLY) BLOCKING FUNCTIONS + + // Read one line of output (also returns when the process quits). + // sStr will not include the \n (or \r\n) at the end of the line. + virtual void ReadLine( CUtlString &sStr ) = 0; + + // This will block the calling thread until it gets the number of bytes specified + // or until the process exits. If sStr.Length() != nBytes, then you know the process exited. + // + // The returned string will always be null-terminated. + // If you call with nBytes=PIPEREAD_INFINITE, it'll read until the process exits. + virtual void Read( CUtlString &sStr, int32 nBytes=PIPEREAD_INFINITE ) = 0; +}; + + +abstract_class IProcess +{ +public: + // Note: If the process is still running, this will auto kill it unless you started the process with + // STARTPROCESS_NOAUTOKILL. + virtual void Release() = 0; + + // Kill the running process. You still must call IProcess::Release to free the resources. + virtual void Abort() = 0; + + // Returns true if a process is complete + virtual bool IsComplete() = 0; + + // Waits until a process is complete. + // Returns the return value from the process. + virtual int WaitUntilComplete() = 0; + + // Write to the process' stdin. + // This blocks until the process has read it. + virtual int WriteStdin( char *pBuf, int nBufLen ) = 0; + + // Get stuff to read the outputs. + virtual IPipeRead* GetStdout() = 0; + virtual IPipeRead* GetStderr() = 0; // NOTE: Only valid if you used STARTPROCESS_SEPARATE_STDERR. + + // Returns the exit code for the process. Doesn't work unless the process is complete. + // Returns -1 on error or if the process isn't complete. + virtual int GetExitCode() = 0; +}; + + +// Flags to IProcessUtils::StartProcess. +#define STARTPROCESS_CONNECTSTDPIPES 0x01 // Necessary to use the stdin/stdout/stderr io functions. +#define STARTPROCESS_SHARE_CONSOLE 0x02 // The process writes directly to your console. The pipe objects returned by + // IProcess::GetStdout and GetStderr won't do anything. +#define STARTPROCESS_SEPARATE_STDERR 0x04 // Rather than having to read stdout and stderr to get the output, the default is to put the stderr output into stdout. + // This flag can change that behavior so you can get that output separately. + // Warning: There may be deadlock problems with this, specifically in CProcessPipeRead::GetActualProcessOutput if + // it's blocked reading stdout's pipe but the process is blocked waiting for us to flush stderr's pipe first. + // To fully support that case, we'd need threads, overlapped IO, or a more careful (and slower) GetActualProcessOutput call + // that bounces between the two pipes and never stalls. + // + // You can also get around this on the client side by reading the pipes from threads. +#define STARTPROCESS_NOAUTOKILL 0x08 // Prevents the process from being auto-terminated in IProcess::Release() + // or when IProcessUtils' Shutdown function is called. +#define STARTPROCESS_FATPIPES 0x10 // Use I/O pipes larger than the default size for processes that do lots of stdio + // (Only works with STARTPROCESS_CONNECTSTDPIPES) + +//----------------------------------------------------------------------------- +// Interface for makefiles to build differently depending on where they are run from +//----------------------------------------------------------------------------- +abstract_class IProcessUtils : public IAppSystem +{ +public: + // Starts, stops a process. + // If pWorkingDir is left at NULL, it'll use this process' working directory. + virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir=NULL )= 0; + virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir=NULL ) = 0; + + // Run a process and get its output. + // If pStdout is set, then stdout AND stderr are put into pStdout. + // If not, then the text output is ignored. + // + // Returns -1 if it was unable to run the process. Otherwise, returns the exit code from the process. + virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir=NULL, CUtlString *pStdout=NULL ) = 0; +}; + +DECLARE_TIER1_INTERFACE( IProcessUtils, g_pProcessUtils ); + + +#endif // IPROCESSUTILS_H diff --git a/public/vstdlib/jobthread.h b/public/vstdlib/jobthread.h new file mode 100644 index 0000000..546a34b --- /dev/null +++ b/public/vstdlib/jobthread.h @@ -0,0 +1,1439 @@ +//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: A utility for a discrete job-oriented worker thread. +// +// The class CThreadPool is both the job queue, and the +// worker thread. Except when the main thread attempts to +// synchronously execute a job, most of the inter-thread locking +// on the queue. +// +// The queue threading model uses a manual reset event for optimal +// throughput. Adding to the queue is guarded by a semaphore that +// will block the inserting thread if the queue has overflown. +// This prevents the worker thread from being starved out even if +// not running at a higher priority than the master thread. +// +// The thread function waits for jobs, services jobs, and manages +// communication between the worker and master threads. The nature +// of the work is opaque to the Executer. +// +// CJob instances actually do the work. The base class +// calls virtual methods for job primitives, so derivations don't +// need to worry about threading models. All of the variants of +// job and OS can be expressed in this hierarchy. Instances of +// CJob are the items placed in the queue, and by +// overriding the job primitives they are the manner by which +// users of the Executer control the state of the job. +// +//============================================================================= + +#include +#include "tier0/threadtools.h" +#include "tier1/refcount.h" +#include "tier1/utllinkedlist.h" +#include "tier1/utlvector.h" +#include "tier1/functors.h" + +#include "vstdlib/vstdlib.h" + +#ifndef JOBTHREAD_H +#define JOBTHREAD_H + +#ifdef AddJob // windows.h print function collisions +#undef AddJob +#undef GetJob +#endif + +#ifdef VSTDLIB_DLL_EXPORT +#define JOB_INTERFACE DLL_EXPORT +#define JOB_OVERLOAD DLL_GLOBAL_EXPORT +#define JOB_CLASS DLL_CLASS_EXPORT +#else +#define JOB_INTERFACE DLL_IMPORT +#define JOB_OVERLOAD DLL_GLOBAL_IMPORT +#define JOB_CLASS DLL_CLASS_IMPORT +#endif + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class CJob; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +enum JobStatusEnum_t +{ + // Use negative for errors + JOB_OK, // operation is successful + JOB_STATUS_PENDING, // file is properly queued, waiting for service + JOB_STATUS_INPROGRESS, // file is being accessed + JOB_STATUS_ABORTED, // file was aborted by caller + JOB_STATUS_UNSERVICED, // file is not yet queued +}; + +typedef int JobStatus_t; + +enum JobFlags_t +{ + JF_IO = ( 1 << 0 ), // The job primarily blocks on IO or hardware + JF_BOOST_THREAD = ( 1 << 1 ), // Up the thread priority to max allowed while processing task + JF_SERIAL = ( 1 << 2 ), // Job cannot be executed out of order relative to other "strict" jobs + JF_QUEUE = ( 1 << 3 ), // Queue it, even if not an IO job +}; + +enum JobPriority_t +{ + JP_LOW, + JP_NORMAL, + JP_HIGH +}; + +#define TP_MAX_POOL_THREADS 64 +struct ThreadPoolStartParams_t +{ + ThreadPoolStartParams_t( bool bIOThreads = false, unsigned nThreads = -1, int *pAffinities = NULL, ThreeState_t fDistribute = TRS_NONE, unsigned nStackSize = -1, int iThreadPriority = SHRT_MIN ) + : bIOThreads( bIOThreads ), nThreads( nThreads ), fDistribute( fDistribute ), nStackSize( nStackSize ), iThreadPriority( iThreadPriority ) + { + bUseAffinityTable = ( pAffinities != NULL ) && ( fDistribute == TRS_TRUE ) && ( nThreads != -1 ); + if ( bUseAffinityTable ) + { + // user supplied an optional 1:1 affinity mapping to override normal distribute behavior + nThreads = MIN( TP_MAX_POOL_THREADS, nThreads ); + for ( unsigned int i = 0; i < nThreads; i++ ) + { + iAffinityTable[i] = pAffinities[i]; + } + } + } + + int nThreads; + ThreeState_t fDistribute; + int nStackSize; + int iThreadPriority; + int iAffinityTable[TP_MAX_POOL_THREADS]; + + bool bIOThreads : 1; + bool bUseAffinityTable : 1; +}; + +//----------------------------------------------------------------------------- +// +// IThreadPool +// +//----------------------------------------------------------------------------- + +typedef bool (*JobFilter_t)( CJob * ); + +//--------------------------------------------------------- +// Messages supported through the CallWorker() method +//--------------------------------------------------------- +enum ThreadPoolMessages_t +{ + TPM_EXIT, // Exit the thread + TPM_SUSPEND, // Suspend after next operation +}; + +//--------------------------------------------------------- + +#ifdef Yield +#undef Yield +#endif + +abstract_class IThreadPool : public IRefCounted +{ +public: + virtual ~IThreadPool() {}; + + //----------------------------------------------------- + // Thread functions + //----------------------------------------------------- + virtual bool Start( const ThreadPoolStartParams_t &startParams = ThreadPoolStartParams_t() ) = 0; + virtual bool Stop( int timeout = TT_INFINITE ) = 0; + + //----------------------------------------------------- + // Functions for any thread + //----------------------------------------------------- + virtual unsigned GetJobCount() = 0; + virtual int NumThreads() = 0; + virtual int NumIdleThreads() = 0; + + //----------------------------------------------------- + // Pause/resume processing jobs + //----------------------------------------------------- + virtual int SuspendExecution() = 0; + virtual int ResumeExecution() = 0; + + //----------------------------------------------------- + // Offer the current thread to the pool + //----------------------------------------------------- + virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; + virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; + virtual void Yield( unsigned timeout ) = 0; + + bool YieldWait( CThreadEvent &event, unsigned timeout = TT_INFINITE ); + bool YieldWait( CJob *, unsigned timeout = TT_INFINITE ); + + //----------------------------------------------------- + // Add a native job to the queue (master thread) + // See AddPerFrameJob below if you want to add a job that + // wants to be run before the end of the frame + //----------------------------------------------------- + virtual void AddJob( CJob * ) = 0; + + //----------------------------------------------------- + // Add an function object to the queue (master thread) + //----------------------------------------------------- + virtual void AddFunctor( CFunctor *pFunctor, CJob **ppJob = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) { AddFunctorInternal( RetAddRef( pFunctor ), ppJob, pszDescription, flags ); } + + //----------------------------------------------------- + // Change the priority of an active job + //----------------------------------------------------- + virtual void ChangePriority( CJob *p, JobPriority_t priority ) = 0; + + //----------------------------------------------------- + // Bulk job manipulation (blocking) + //----------------------------------------------------- + int ExecuteAll( JobFilter_t pfnFilter = NULL ) { return ExecuteToPriority( JP_LOW, pfnFilter ); } + virtual int ExecuteToPriority( JobPriority_t toPriority, JobFilter_t pfnFilter = NULL ) = 0; + virtual int AbortAll() = 0; + + //----------------------------------------------------- + // Add a native job to the queue (master thread) + // Call YieldWaitPerFrameJobs() to wait only until all per-frame jobs are done + //----------------------------------------------------- + virtual void AddPerFrameJob( CJob * ) = 0; + + //----------------------------------------------------- + // Add an arbitrary call to the queue (master thread) + // + // Avert thy eyes! Imagine rather: + // + // CJob *AddCall( , [args1, [arg2,]...] + // CJob *AddCall( , , [args1, [arg2,]...] + // CJob *AddRefCall( , , [args1, [arg2,]...] + // CJob *QueueCall( , [args1, [arg2,]...] + // CJob *QueueCall( , , [args1, [arg2,]...] + //----------------------------------------------------- + + #define DEFINE_NONMEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_CONST_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //----------------------------------------------------------------------------- + + #define DEFINE_NONMEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + \ + return pJob; \ + } + + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL ); + + #undef DEFINE_NONMEMBER_ADD_CALL + #undef DEFINE_MEMBER_ADD_CALL + #undef DEFINE_CONST_MEMBER_ADD_CALL + #undef DEFINE_REF_COUNTING_MEMBER_ADD_CALL + #undef DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL + #undef DEFINE_NONMEMBER_QUEUE_CALL + #undef DEFINE_MEMBER_QUEUE_CALL + #undef DEFINE_CONST_MEMBER_QUEUE_CALL + #undef DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL + #undef DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL + +private: + virtual void AddFunctorInternal( CFunctor *, CJob ** = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) = 0; + + //----------------------------------------------------- + // Services for internal use by job instances + //----------------------------------------------------- + friend class CJob; + + virtual CJob *GetDummyJob() = 0; + +public: + virtual void Distribute( bool bDistribute = true, int *pAffinityTable = NULL ) = 0; + + virtual bool Start( const ThreadPoolStartParams_t &startParams, const char *pszNameOverride ) = 0; + + virtual int YieldWaitPerFrameJobs( ) = 0; +}; + +//----------------------------------------------------------------------------- + +JOB_INTERFACE IThreadPool *CreateNewThreadPool(); +JOB_INTERFACE void DestroyThreadPool( IThreadPool *pPool ); + +//------------------------------------- + +JOB_INTERFACE void RunThreadPoolTests(); + +//----------------------------------------------------------------------------- + +JOB_INTERFACE IThreadPool *g_pThreadPool; +#ifdef _X360 +JOB_INTERFACE IThreadPool *g_pAlternateThreadPool; +#endif + +//----------------------------------------------------------------------------- +// Class to combine the metadata for an operation and the ability to perform +// the operation. Meant for inheritance. All functions inline, defers to executor +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( ThreadPoolData_t ); +#define JOB_NO_DATA ((ThreadPoolData_t)-1) + +class CJob : public CRefCounted1 +{ +public: + CJob( JobPriority_t priority = JP_NORMAL ) + : m_status( JOB_STATUS_UNSERVICED ), + m_ThreadPoolData( JOB_NO_DATA ), + m_priority( priority ), + m_flags( 0 ), + m_pThreadPool( NULL ), + m_CompleteEvent( true ), + m_iServicingThread( -1 ) + { + } + + //----------------------------------------------------- + // Priority (not thread safe) + //----------------------------------------------------- + void SetPriority( JobPriority_t priority ) { m_priority = priority; } + JobPriority_t GetPriority() const { return m_priority; } + + //----------------------------------------------------- + + void SetFlags( unsigned flags ) { m_flags = flags; } + unsigned GetFlags() const { return m_flags; } + + //----------------------------------------------------- + + void SetServiceThread( int iServicingThread ) { m_iServicingThread = (char)iServicingThread; } + int GetServiceThread() const { return m_iServicingThread; } + void ClearServiceThread() { m_iServicingThread = -1; } + + //----------------------------------------------------- + // Fast queries + //----------------------------------------------------- + bool Executed() const { return ( m_status == JOB_OK ); } + bool CanExecute() const { return ( m_status == JOB_STATUS_PENDING || m_status == JOB_STATUS_UNSERVICED ); } + bool IsFinished() const { return ( m_status != JOB_STATUS_PENDING && m_status != JOB_STATUS_INPROGRESS && m_status != JOB_STATUS_UNSERVICED ); } + JobStatus_t GetStatus() const { return m_status; } + + //----------------------------------------------------- + // Try to acquire ownership (to satisfy). If you take the lock, you must either execute or abort. + //----------------------------------------------------- + bool TryLock() volatile { return m_mutex.TryLock(); } + void Lock() volatile { m_mutex.Lock(); } + void Unlock() volatile { m_mutex.Unlock(); } + + //----------------------------------------------------- + // Thread event support (safe for NULL this to simplify code ) + //----------------------------------------------------- + bool WaitForFinish( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; return ( !IsFinished() ) ? g_pThreadPool->YieldWait( this, dwTimeout ) : true; } + bool WaitForFinishAndRelease( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; bool bResult = WaitForFinish( dwTimeout); Release(); return bResult; } + CThreadEvent *AccessEvent() { return &m_CompleteEvent; } + + //----------------------------------------------------- + // Perform the job + //----------------------------------------------------- + JobStatus_t Execute(); + JobStatus_t TryExecute(); + JobStatus_t ExecuteAndRelease() { JobStatus_t status = Execute(); Release(); return status; } + JobStatus_t TryExecuteAndRelease() { JobStatus_t status = TryExecute(); Release(); return status; } + + //----------------------------------------------------- + // Terminate the job, discard if partially or wholly fulfilled + //----------------------------------------------------- + JobStatus_t Abort( bool bDiscard = true ); + + virtual char const *Describe() { return "Job"; } + +private: + //----------------------------------------------------- + friend class CThreadPool; + + JobStatus_t m_status; + JobPriority_t m_priority; + CThreadFastMutex m_mutex; + unsigned char m_flags; + char m_iServicingThread; + short m_reserved; + ThreadPoolData_t m_ThreadPoolData; + IThreadPool * m_pThreadPool; + CThreadEvent m_CompleteEvent; + +private: + //----------------------------------------------------- + CJob( const CJob &fromRequest ); + void operator=(const CJob &fromRequest ); + + virtual JobStatus_t DoExecute() = 0; + virtual JobStatus_t DoAbort( bool bDiscard ) { return JOB_STATUS_ABORTED; } + virtual void DoCleanup() {} +}; + +//----------------------------------------------------------------------------- + +class CFunctorJob : public CJob +{ +public: + CFunctorJob( CFunctor *pFunctor, const char *pszDescription = NULL ) + : m_pFunctor( pFunctor ) + { + if ( pszDescription ) + { + Q_strncpy( m_szDescription, pszDescription, sizeof(m_szDescription) ); + } + else + { + m_szDescription[0] = 0; + } + } + + virtual JobStatus_t DoExecute() + { + (*m_pFunctor)(); + return JOB_OK; + } + + const char *Describe() + { + return m_szDescription; + } + +private: + CRefPtr m_pFunctor; + char m_szDescription[16]; +}; + +//----------------------------------------------------------------------------- +// Utility for managing multiple jobs +//----------------------------------------------------------------------------- + +class CJobSet +{ +public: + CJobSet( CJob *pJob = NULL ) + { + if ( pJob ) + { + m_jobs.AddToTail( pJob ); + } + } + + CJobSet( CJob **ppJobs, int nJobs ) + { + if ( ppJobs ) + { + m_jobs.AddMultipleToTail( nJobs, ppJobs ); + } + } + + ~CJobSet() + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Release(); + } + } + + void operator+=( CJob *pJob ) + { + m_jobs.AddToTail( pJob ); + } + + void operator-=( CJob *pJob ) + { + m_jobs.FindAndRemove( pJob ); + } + + void Execute( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Execute(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void Abort( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Abort(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void WaitForFinish( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->WaitForFinish(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void WaitForFinish( IThreadPool *pPool, bool bRelease = true ) + { + pPool->YieldWait( m_jobs.Base(), m_jobs.Count() ); + + if ( bRelease ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Release(); + } + + m_jobs.RemoveAll(); + } + } + +private: + CUtlVectorFixed m_jobs; +}; + +//----------------------------------------------------------------------------- +// Job helpers +//----------------------------------------------------------------------------- + +#define ThreadExecute g_pThreadPool->QueueCall +#define ThreadExecuteRef g_pThreadPool->QueueRefCall + +#define BeginExecuteParallel() do { CJobSet jobSet +#define EndExecuteParallel() jobSet.WaitForFinish( g_pThreadPool ); } while (0) + +#define ExecuteParallel jobSet += g_pThreadPool->QueueCall +#define ExecuteRefParallel jobSet += g_pThreadPool->QueueCallRef + + +//----------------------------------------------------------------------------- +// Work splitting: array split, best when cost per item is roughly equal +//----------------------------------------------------------------------------- + +#pragma warning(push) +#pragma warning(disable:4389) +#pragma warning(disable:4018) +#pragma warning(disable:4701) + +#define DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL(N) \ + template \ + void IterRangeParallel(FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + const int MAX_THREADS = 16; \ + int nIdle = g_pThreadPool->NumIdleThreads(); \ + ITERTYPE1 range = to - from; \ + int nThreads = min( nIdle + 1, range ); \ + if ( nThreads > MAX_THREADS ) \ + { \ + nThreads = MAX_THREADS; \ + } \ + if ( nThreads < 2 ) \ + { \ + FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + ITERTYPE1 nIncrement = range / nThreads; \ + \ + CJobSet jobSet; \ + while ( --nThreads ) \ + { \ + ITERTYPE2 thisTo = from + nIncrement; \ + jobSet += g_pThreadPool->AddCall( pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ + from = thisTo; \ + } \ + FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + jobSet.WaitForFinish( g_pThreadPool ); \ + } \ + \ + } + +FUNC_GENERATE_ALL( DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL ); + +#define DEFINE_MEMBER_ITER_RANGE_PARALLEL(N) \ + template \ + void IterRangeParallel(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + const int MAX_THREADS = 16; \ + int nIdle = g_pThreadPool->NumIdleThreads(); \ + ITERTYPE1 range = to - from; \ + int nThreads = min( nIdle + 1, range ); \ + if ( nThreads > MAX_THREADS ) \ + { \ + nThreads = MAX_THREADS; \ + } \ + if ( nThreads < 2 ) \ + { \ + FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + ITERTYPE1 nIncrement = range / nThreads; \ + \ + CJobSet jobSet; \ + while ( --nThreads ) \ + { \ + ITERTYPE2 thisTo = from + nIncrement; \ + jobSet += g_pThreadPool->AddCall( pObject, pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ + from = thisTo; \ + } \ + FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + jobSet.WaitForFinish( g_pThreadPool ); \ + } \ + \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_ITER_RANGE_PARALLEL ); + + +//----------------------------------------------------------------------------- +// Work splitting: competitive, best when cost per item varies a lot +//----------------------------------------------------------------------------- + +template +class CJobItemProcessor +{ +public: + typedef T ItemType_t; + void Begin() {} + // void Process( ItemType_t & ) {} + void End() {} +}; + +template +class CFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init(void (*pfnProcess)( T & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) + { + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + //CFuncJobItemProcessor(OBJECT_TYPE_PTR pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ); + void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } + void Process( T &item ) { (*m_pfnProcess)( item ); } + void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } + +protected: + void (*m_pfnProcess)( T & ); + void (*m_pfnBegin)(); + void (*m_pfnEnd)(); +}; + +template +class CMemberFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) + { + m_pObject = pObject; + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } + void Process( T &item ) { ((*m_pObject).*m_pfnProcess)( item ); } + void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } + +protected: + OBJECT_TYPE *m_pObject; + + void (FUNCTION_CLASS::*m_pfnProcess)( T & ); + void (FUNCTION_CLASS::*m_pfnBegin)(); + void (FUNCTION_CLASS::*m_pfnEnd)(); +}; + +template +class CLoopFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init(void (*pfnProcess)( T*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) + { + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } + void Process( T* pContext, int nFirst, int nCount ) { (*m_pfnProcess)( pContext, nFirst, nCount ); } + void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } + +protected: + void (*m_pfnProcess)( T*, int, int ); + void (*m_pfnBegin)(); + void (*m_pfnEnd)(); +}; + +template +class CLoopMemberFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) + { + m_pObject = pObject; + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } + void Process( T *item, int nFirst, int nCount ) { ((*m_pObject).*m_pfnProcess)( item, nFirst, nCount ); } + void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } + +protected: + OBJECT_TYPE *m_pObject; + + void (FUNCTION_CLASS::*m_pfnProcess)( T*, int, int ); + void (FUNCTION_CLASS::*m_pfnBegin)(); + void (FUNCTION_CLASS::*m_pfnEnd)(); +}; + + +#pragma warning(push) +#pragma warning(disable:4189) + +template +class CParallelProcessor +{ +public: + CParallelProcessor() + { + m_pItems = m_pLimit= 0; + } + + void Run( ITEM_TYPE *pItems, unsigned nItems, int nChunkSize = 1, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) + { + if ( nItems == 0 ) + return; + +#if defined(_X360) + volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; +#endif + + m_nChunkSize = nChunkSize; + if ( !pThreadPool ) + { + pThreadPool = g_pThreadPool; + } + + m_pItems = pItems; + m_pLimit = pItems + nItems; + + int nJobs = nItems - 1; + + if ( nJobs > nMaxParallel ) + { + nJobs = nMaxParallel; + } + + if (! pThreadPool ) // only possible on linux + { + DoExecute( ); + return; + } + + int nThreads = pThreadPool->NumThreads(); + if ( nJobs > nThreads ) + { + nJobs = nThreads; + } + + if ( nJobs > 0 ) + { + CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); + int i = nJobs; + + while( i-- ) + { + jobs[i] = pThreadPool->QueueCall( this, &CParallelProcessor::DoExecute ); + } + + DoExecute(); + + for ( i = 0; i < nJobs; i++ ) + { + jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did + jobs[i]->Release(); + } + } + else + { + DoExecute(); + } + } + + ITEM_PROCESSOR_TYPE m_ItemProcessor; + +private: + void DoExecute() + { + if ( m_pItems < m_pLimit ) + { +#if defined(_X360) + volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; +#endif + m_ItemProcessor.Begin(); + + ITEM_TYPE *pLimit = m_pLimit; + + int nChunkSize = m_nChunkSize; + for (;;) + { + ITEM_TYPE *pCurrent = m_pItems.AtomicAdd( nChunkSize ); + ITEM_TYPE *pLast = MIN( pLimit, pCurrent + nChunkSize ); + while( pCurrent < pLast ) + { + m_ItemProcessor.Process( *pCurrent ); + pCurrent++; + } + if ( pCurrent >= pLimit ) + { + break; + } + } + m_ItemProcessor.End(); + } + } + CInterlockedPtr m_pItems; + ITEM_TYPE * m_pLimit; + int m_nChunkSize; + +}; + +#pragma warning(pop) + +template +inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel ); +} + +template +inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel ); +} + +// Parallel Process that lets you specify threadpool +template +inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); +} + +template +inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); +} + +// ParallelProcessChunks lets you specify a minimum # of items to process per job. Use this when +// you may have a large set of work items which only take a small amount of time per item, and so +// need to reduce dispatch overhead. +template +inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); +} + +template +inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); +} + +template +inline void ParallelProcessChunks( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel, pPool ); +} + + +template +class CParallelLoopProcessor +{ +public: + CParallelLoopProcessor() + { + m_nIndex = m_nLimit = 0; + m_nChunkCount = 0; + m_nActive = 0; + } + + void Run( CONTEXT_TYPE *pContext, int nBegin, int nItems, int nChunkCount, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) + { + if ( !nItems ) + return; + + if ( !pThreadPool ) + { + pThreadPool = g_pThreadPool; + } + + m_pContext = pContext; + m_nIndex = nBegin; + m_nLimit = nBegin + nItems; + nChunkCount = MAX( MIN( nItems, nChunkCount ), 1 ); + m_nChunkCount = ( nItems + nChunkCount - 1 ) / nChunkCount; + int nJobs = ( nItems + m_nChunkCount - 1 ) / m_nChunkCount; + if ( nJobs > nMaxParallel ) + { + nJobs = nMaxParallel; + } + + if ( !pThreadPool ) // only possible on linux + { + DoExecute( ); + return; + } + + int nThreads = pThreadPool->NumThreads(); + if ( nJobs > nThreads ) + { + nJobs = nThreads; + } + + if ( nJobs > 0 ) + { + CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); + int i = nJobs; + + while( i-- ) + { + jobs[i] = pThreadPool->QueueCall( this, &CParallelLoopProcessor::DoExecute ); + } + + DoExecute(); + + for ( i = 0; i < nJobs; i++ ) + { + jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did + jobs[i]->Release(); + } + } + else + { + DoExecute(); + } + } + + ITEM_PROCESSOR_TYPE m_ItemProcessor; + +private: + void DoExecute() + { + m_ItemProcessor.Begin(); + for (;;) + { + int nIndex = m_nIndex.AtomicAdd( m_nChunkCount ); + if ( nIndex < m_nLimit ) + { + int nCount = MIN( m_nChunkCount, m_nLimit - nIndex ); + m_ItemProcessor.Process( m_pContext, nIndex, nCount ); + } + else + { + break; + } + } + m_ItemProcessor.End(); + --m_nActive; + } + + CONTEXT_TYPE *m_pContext; + CInterlockedInt m_nIndex; + int m_nLimit; + int m_nChunkCount; + CInterlockedInt m_nActive; +}; + +template < typename CONTEXT_TYPE > +inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > +inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE > +inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > +inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); +} + +template +class CParallelProcessorBase +{ +protected: + typedef CParallelProcessorBase ThisParallelProcessorBase_t; + typedef Derived ThisParallelProcessorDerived_t; + +public: + CParallelProcessorBase() + { + m_nActive = 0; + } + +protected: + void Run( int nMaxParallel = INT_MAX, int threadOverride = -1 ) + { + int i = g_pThreadPool->NumIdleThreads(); + + if ( nMaxParallel < i) + { + i = nMaxParallel; + } + + while( i -- > 0 ) + { + if ( threadOverride == -1 || i == threadOverride - 1 ) + { + ++ m_nActive; + ThreadExecute( this, &ThisParallelProcessorBase_t::DoExecute )->Release(); + } + } + + if ( threadOverride == -1 || threadOverride == 0 ) + { + ++ m_nActive; + DoExecute(); + } + + while ( m_nActive ) + { + ThreadPause(); + } + } + +protected: + void OnBegin() {} + bool OnProcess() { return false; } + void OnEnd() {} + +private: + void DoExecute() + { + static_cast( this )->OnBegin(); + + while ( static_cast( this )->OnProcess() ) + continue; + + static_cast(this)->OnEnd(); + + -- m_nActive; + } + + CInterlockedInt m_nActive; +}; + + + + +//----------------------------------------------------------------------------- +// Raw thread launching +//----------------------------------------------------------------------------- + +inline unsigned FunctorExecuteThread( void *pParam ) +{ + CFunctor *pFunctor = (CFunctor *)pParam; + (*pFunctor)(); + pFunctor->Release(); + return 0; +} + +inline ThreadHandle_t ThreadExecuteSoloImpl( CFunctor *pFunctor, const char *pszName = NULL ) +{ + ThreadHandle_t hThread; + hThread = CreateSimpleThread( FunctorExecuteThread, pFunctor ); + if ( pszName ) + { + ThreadSetDebugName( hThread, pszName ); + } + return hThread; +} + +inline ThreadHandle_t ThreadExecuteSolo( CJob *pJob ) { return ThreadExecuteSoloImpl( CreateFunctor( pJob, &CJob::Execute ), pJob->Describe() ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } + +//----------------------------------------------------------------------------- + +inline bool IThreadPool::YieldWait( CThreadEvent &theEvent, unsigned timeout ) +{ + CThreadEvent *pEvent = &theEvent; + return ( YieldWait( &pEvent, 1, true, timeout ) != TW_TIMEOUT ); +} + +inline bool IThreadPool::YieldWait( CJob *pJob, unsigned timeout ) +{ + return ( YieldWait( &pJob, 1, true, timeout ) != TW_TIMEOUT ); +} + +//----------------------------------------------------------------------------- + +inline JobStatus_t CJob::Execute() +{ + if ( IsFinished() ) + { + return m_status; + } + + AUTO_LOCK( m_mutex ); + AddRef(); + + JobStatus_t result; + + switch ( m_status ) + { + case JOB_STATUS_UNSERVICED: + case JOB_STATUS_PENDING: + { + // Service it + m_status = JOB_STATUS_INPROGRESS; + result = m_status = DoExecute(); + DoCleanup(); + m_CompleteEvent.Set(); + break; + } + + case JOB_STATUS_INPROGRESS: + AssertMsg(0, "Mutex Should have protected use while processing"); + // fall through... + + case JOB_OK: + case JOB_STATUS_ABORTED: + result = m_status; + break; + + default: + AssertMsg( m_status < JOB_OK, "Unknown job state"); + result = m_status; + } + + Release(); + + return result; +} + + +//--------------------------------------------------------- + +inline JobStatus_t CJob::TryExecute() +{ + // TryLock() would only fail if another thread has entered + // Execute() or Abort() + if ( !IsFinished() && TryLock() ) + { + // ...service the request + Execute(); + Unlock(); + } + return m_status; +} + +//--------------------------------------------------------- + +inline JobStatus_t CJob::Abort( bool bDiscard ) +{ + if ( IsFinished() ) + { + return m_status; + } + + AUTO_LOCK( m_mutex ); + AddRef(); + + JobStatus_t result; + + switch ( m_status ) + { + case JOB_STATUS_UNSERVICED: + case JOB_STATUS_PENDING: + { + result = m_status = DoAbort( bDiscard ); + if ( bDiscard ) + DoCleanup(); + m_CompleteEvent.Set(); + } + break; + + case JOB_STATUS_ABORTED: + case JOB_STATUS_INPROGRESS: + case JOB_OK: + result = m_status; + break; + + default: + AssertMsg( m_status < JOB_OK, "Unknown job state"); + result = m_status; + } + + Release(); + + return result; +} + +//----------------------------------------------------------------------------- + +#endif // JOBTHREAD_H diff --git a/public/vstdlib/pch_vstdlib.h b/public/vstdlib/pch_vstdlib.h new file mode 100644 index 0000000..03754dd --- /dev/null +++ b/public/vstdlib/pch_vstdlib.h @@ -0,0 +1,51 @@ +//======== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ======== +// +// The copyright to the contents herein is the property of Valve, L.L.C. +// The contents may be used and/or copied only with the written permission of +// Valve, L.L.C., or in accordance with the terms and conditions stipulated in +// the agreement/contract under which the contents have been supplied. +// +// Purpose: +// +// $Workfile: $ +// $NoKeywords: $ +//============================================================================= + + +#pragma warning(disable: 4514) + +// First include standard libraries +#include +#include +#include +#include +#include +#include + +// Next, include public +#include "tier0/basetypes.h" +#include "tier0/dbg.h" +#include "tier0/valobject.h" + +// Next, include vstdlib +#include "vstdlib/vstdlib.h" +#include "tier1/strtools.h" +#include "vstdlib/random.h" +#include "tier1/keyvalues.h" +#include "tier1/utlmemory.h" +#include "tier1/utlrbtree.h" +#include "tier1/utlvector.h" +#include "tier1/utllinkedlist.h" +#include "tier1/utlmultilist.h" +#include "tier1/utlsymbol.h" +#include "tier0/icommandline.h" +#include "tier1/netadr.h" +#include "tier1/mempool.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlstring.h" +#include "tier1/utlmap.h" + +#include "tier0/memdbgon.h" + + + diff --git a/public/vstdlib/random.h b/public/vstdlib/random.h new file mode 100644 index 0000000..e507147 --- /dev/null +++ b/public/vstdlib/random.h @@ -0,0 +1,111 @@ +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Random number generator +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef VSTDLIB_RANDOM_H +#define VSTDLIB_RANDOM_H + +#include "vstdlib/vstdlib.h" +#include "tier0/basetypes.h" +#include "tier0/threadtools.h" +#include "tier1/interface.h" + +#define NTAB 32 + +#pragma warning(push) +#pragma warning( disable:4251 ) + +//----------------------------------------------------------------------------- +// A generator of uniformly distributed random numbers +//----------------------------------------------------------------------------- +class IUniformRandomStream +{ +public: + // Sets the seed of the random number generator + virtual void SetSeed( int iSeed ) = 0; + + // Generates random numbers + virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ) = 0; + virtual int RandomInt( int iMinVal, int iMaxVal ) = 0; + virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ) = 0; +}; + + +//----------------------------------------------------------------------------- +// The standard generator of uniformly distributed random numbers +//----------------------------------------------------------------------------- +class VSTDLIB_CLASS CUniformRandomStream : public IUniformRandomStream +{ +public: + CUniformRandomStream(); + + // Sets the seed of the random number generator + virtual void SetSeed( int iSeed ); + + // Generates random numbers + virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); + virtual int RandomInt( int iMinVal, int iMaxVal ); + virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); + +private: + int GenerateRandomNumber(); + + int m_idum; + int m_iy; + int m_iv[NTAB]; + + CThreadFastMutex m_mutex; +}; + + +//----------------------------------------------------------------------------- +// A generator of gaussian distributed random numbers +//----------------------------------------------------------------------------- +class VSTDLIB_CLASS CGaussianRandomStream +{ +public: + // Passing in NULL will cause the gaussian stream to use the + // installed global random number generator + CGaussianRandomStream( IUniformRandomStream *pUniformStream = NULL ); + + // Attaches to a random uniform stream + void AttachToStream( IUniformRandomStream *pUniformStream = NULL ); + + // Generates random numbers + float RandomFloat( float flMean = 0.0f, float flStdDev = 1.0f ); + +private: + IUniformRandomStream *m_pUniformStream; + bool m_bHaveValue; + float m_flRandomValue; + + CThreadFastMutex m_mutex; +}; + + +//----------------------------------------------------------------------------- +// A couple of convenience functions to access the library's global uniform stream +//----------------------------------------------------------------------------- +VSTDLIB_INTERFACE void RandomSeed( int iSeed ); +VSTDLIB_INTERFACE float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); +VSTDLIB_INTERFACE float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); +VSTDLIB_INTERFACE int RandomInt( int iMinVal, int iMaxVal ); +VSTDLIB_INTERFACE float RandomGaussianFloat( float flMean = 0.0f, float flStdDev = 1.0f ); + + +//----------------------------------------------------------------------------- +// Installs a global random number generator, which will affect the Random functions above +//----------------------------------------------------------------------------- +VSTDLIB_INTERFACE void InstallUniformRandomStream( IUniformRandomStream *pStream ); + + +#pragma warning(pop) + +#endif // VSTDLIB_RANDOM_H + + + diff --git a/public/vstdlib/vcover.h b/public/vstdlib/vcover.h new file mode 100644 index 0000000..cb62790 --- /dev/null +++ b/public/vstdlib/vcover.h @@ -0,0 +1,125 @@ +//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: A simple tool for coverage tests +// +//============================================================================= + +#ifndef VCOVER_H +#define VCOVER_H + +#include "tier1/utlrbtree.h" +#include "vstdlib.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +class CVCoverage +{ +public: + CVCoverage() : + m_bActive( false ), + m_depth( 0 ), + m_token( 1 ) + { + } + + bool IsActive() const + { + return m_bActive; + } + + void SetActive( bool bActive ) + { + Assert( bActive != m_bActive ); + m_bActive = bActive; + if ( bActive ) + ++m_token; + } + + void Begin() + { + ++m_depth; + } + + void End() + { + --m_depth; + } + + void Reset() + { + m_locations.RemoveAll(); + } + + bool ShouldCover( unsigned token ) const + { + return ( m_bActive && m_depth > 0 && token != m_token ); + } + + unsigned Cover( const char *pszFile, int line ) + { + Location_t location = { pszFile, line }; + + m_locations.Insert( location ); + + return m_token; + } + + void Report() + { + for ( int i = m_locations.FirstInorder(); i != m_locations.InvalidIndex(); i = m_locations.NextInorder( i ) ) + { + Msg( "%s(%d) :\n", m_locations[i].pszFile, m_locations[i].line ); + } + } + +private: + struct Location_t + { + const char *pszFile; + int line; + + }; + + class CLocationLess + { + public: + CLocationLess( int ignored ) {} + bool operator!() { return false; } + + bool operator()( const Location_t &lhs, const Location_t &rhs ) const + { + if ( lhs.line < rhs.line ) + { + return true; + } + + return CaselessStringLessThan( lhs.pszFile, rhs.pszFile ); + } + }; + + bool m_bActive; + int m_depth; + unsigned m_token; + + CUtlRBTree< Location_t, unsigned short, CLocationLess > m_locations; +}; + +VSTDLIB_INTERFACE CVCoverage g_VCoverage; + +#ifdef VCOVER_ENABLED +#define VCOVER() \ + do \ + { \ + static token; \ + if ( g_VCoverage.ShouldCover( token ) ) \ + { \ + token = g_VCoverage.Cover( __FILE__, __LINE__ ); \ + } \ + } while( 0 ) +#else +#define VCOVER() ((void)0) +#endif + +#endif // VCOVER_H diff --git a/public/vstdlib/vstdlib.h b/public/vstdlib/vstdlib.h new file mode 100644 index 0000000..e5ef3db --- /dev/null +++ b/public/vstdlib/vstdlib.h @@ -0,0 +1,33 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef VSTDLIB_H +#define VSTDLIB_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +//----------------------------------------------------------------------------- +// dll export stuff +//----------------------------------------------------------------------------- +#ifdef VSTDLIB_DLL_EXPORT +#define VSTDLIB_INTERFACE DLL_EXPORT +#define VSTDLIB_OVERLOAD DLL_GLOBAL_EXPORT +#define VSTDLIB_CLASS DLL_CLASS_EXPORT +#define VSTDLIB_GLOBAL DLL_GLOBAL_EXPORT +#else +#define VSTDLIB_INTERFACE DLL_IMPORT +#define VSTDLIB_OVERLOAD DLL_GLOBAL_IMPORT +#define VSTDLIB_CLASS DLL_CLASS_IMPORT +#define VSTDLIB_GLOBAL DLL_GLOBAL_IMPORT +#endif + +#endif // VSTDLIB_H diff --git a/public/vtf/vtf.h b/public/vtf/vtf.h new file mode 100644 index 0000000..bb07564 --- /dev/null +++ b/public/vtf/vtf.h @@ -0,0 +1,618 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VTF_H +#define VTF_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "bitmap/imageformat.h" +#include "tier0/platform.h" + +// #define VTF_FILE_FORMAT_ONLY to just include the vtf header and none of the code declaration +#ifndef VTF_FILE_FORMAT_ONLY + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; +class Vector; +struct Rect_t; +class IFileSystem; + +//----------------------------------------------------------------------------- +// Texture flags +//----------------------------------------------------------------------------- +enum CompiledVtfFlags +{ + // Flags from the *.txt config file + TEXTUREFLAGS_POINTSAMPLE = 0x00000001, + TEXTUREFLAGS_TRILINEAR = 0x00000002, + TEXTUREFLAGS_CLAMPS = 0x00000004, + TEXTUREFLAGS_CLAMPT = 0x00000008, + TEXTUREFLAGS_ANISOTROPIC = 0x00000010, + TEXTUREFLAGS_HINT_DXT5 = 0x00000020, + TEXTUREFLAGS_PWL_CORRECTED = 0x00000040, + TEXTUREFLAGS_NORMAL = 0x00000080, + TEXTUREFLAGS_NOMIP = 0x00000100, + TEXTUREFLAGS_NOLOD = 0x00000200, + TEXTUREFLAGS_ALL_MIPS = 0x00000400, + TEXTUREFLAGS_PROCEDURAL = 0x00000800, + + // These are automatically generated by vtex from the texture data. + TEXTUREFLAGS_ONEBITALPHA = 0x00001000, + TEXTUREFLAGS_EIGHTBITALPHA = 0x00002000, + + // Newer flags from the *.txt config file + TEXTUREFLAGS_ENVMAP = 0x00004000, + TEXTUREFLAGS_RENDERTARGET = 0x00008000, + TEXTUREFLAGS_DEPTHRENDERTARGET = 0x00010000, + TEXTUREFLAGS_NODEBUGOVERRIDE = 0x00020000, + TEXTUREFLAGS_SINGLECOPY = 0x00040000, + + TEXTUREFLAGS_PRE_SRGB = 0x00080000, //SRGB correction has already been applied to this texture. + + TEXTUREFLAGS_UNUSED_00100000 = 0x00100000, + TEXTUREFLAGS_UNUSED_00200000 = 0x00200000, + TEXTUREFLAGS_UNUSED_00400000 = 0x00400000, + + TEXTUREFLAGS_NODEPTHBUFFER = 0x00800000, + + TEXTUREFLAGS_UNUSED_01000000 = 0x01000000, + + TEXTUREFLAGS_CLAMPU = 0x02000000, + TEXTUREFLAGS_VERTEXTEXTURE = 0x04000000, // Usable as a vertex texture + TEXTUREFLAGS_SSBUMP = 0x08000000, + + TEXTUREFLAGS_UNUSED_10000000 = 0x10000000, + + TEXTUREFLAGS_BORDER = 0x20000000, // Clamp to border color on all texture coordinates + + TEXTUREFLAGS_UNUSED_40000000 = 0x40000000, + TEXTUREFLAGS_UNUSED_80000000 = 0x80000000, +}; + +enum VersionedVtfFlags +{ + VERSIONED_VTF_FLAGS_MASK_7_3 = ~0xD1780400, // For a ver 7.3 or earlier only these flags are valid +}; + + +struct VtfProcessingOptions +{ + uint32 cbSize; // Set to sizeof( VtfProcessingOptions ) + + // + // Flags0 + // + enum Flags0 + { + // Have a channel decaying to a given decay goal for the given last number of mips + OPT_DECAY_R = 0x00000001, // Red decays + OPT_DECAY_G = 0x00000002, // Green decays + OPT_DECAY_B = 0x00000004, // Blue decays + OPT_DECAY_A = 0x00000008, // Alpha decays + + OPT_DECAY_EXP_R = 0x00000010, // Channel R decays exponentially (otherwise linearly) + OPT_DECAY_EXP_G = 0x00000020, // Channel G decays exponentially (otherwise linearly) + OPT_DECAY_EXP_B = 0x00000040, // Channel B decays exponentially (otherwise linearly) + OPT_DECAY_EXP_A = 0x00000080, // Channel A decays exponentially (otherwise linearly) + + OPT_NOCOMPRESS = 0x00000100, // Use uncompressed image format + OPT_NORMAL_DUDV = 0x00000200, // dU dV normal map + OPT_FILTER_NICE = 0x00000400, // Use nice filtering + OPT_SRGB_PC_TO_360 = 0x00000800, // Perform srgb correction for colormaps + + OPT_SET_ALPHA_ONEOVERMIP = 0x00001000, // Alpha = 1/miplevel + OPT_PREMULT_COLOR_ONEOVERMIP = 0x00002000, // Color *= 1/miplevel + OPT_MIP_ALPHATEST = 0x00004000, // Alpha-tested mip generation + OPT_NORMAL_GA = 0x00008000, // Use Green-Alpha normal compression + OPT_PREMULT_ALPHA_MIPFRACTION = 0x00010000, // Alpha *= miplevel/mipcount + }; + + uint32 flags0; // A combination of "Flags0" + + // + // Decay settings + // + uint8 clrDecayGoal[4]; // Goal colors for R G B A + uint8 numNotDecayMips[4]; // Number of first mips unaffected by decay (0 means all below mip0) + float fDecayExponentBase[4]; // For exponential decay the base number (e.g. 0.75) + + // + // Alpha fading with mip levels + // + uint8 fullAlphaAtMipLevel; + uint8 minAlpha; +}; + + +//----------------------------------------------------------------------------- +// Cubemap face indices +//----------------------------------------------------------------------------- +enum CubeMapFaceIndex_t +{ + CUBEMAP_FACE_RIGHT = 0, + CUBEMAP_FACE_LEFT, + CUBEMAP_FACE_BACK, // NOTE: This face is in the +y direction?!?!? + CUBEMAP_FACE_FRONT, // NOTE: This face is in the -y direction!?!? + CUBEMAP_FACE_UP, + CUBEMAP_FACE_DOWN, + + CUBEMAP_FACE_COUNT, + + // This is the fallback for low-end (obsolete, here for backward compat!) + CUBEMAP_FACE_SPHEREMAP = CUBEMAP_FACE_COUNT, + +}; + + +//----------------------------------------------------------------------------- +// Enumeration used for spheremap generation +//----------------------------------------------------------------------------- +enum LookDir_t +{ + LOOK_DOWN_X = 0, + LOOK_DOWN_NEGX, + LOOK_DOWN_Y, + LOOK_DOWN_NEGY, + LOOK_DOWN_Z, + LOOK_DOWN_NEGZ, +}; + + +//----------------------------------------------------------------------------- +// Use this image format if you want to perform tool operations on the texture +//----------------------------------------------------------------------------- +#define IMAGE_FORMAT_DEFAULT ((ImageFormat)-2) + +//----------------------------------------------------------------------------- +// Interface to get at various bits of a VTF texture +//----------------------------------------------------------------------------- +class IVTFTexture +{ +public: + // Initializes the texture and allocates space for the bits + // In most cases, you shouldn't force the mip count. + virtual bool Init( int nWidth, int nHeight, int nDepth, ImageFormat fmt, int nFlags, int iFrameCount, int nForceMipCount = -1 ) = 0; + + // Methods to set other texture fields + virtual void SetBumpScale( float flScale ) = 0; + virtual void SetReflectivity( const Vector &vecReflectivity ) = 0; + + // Methods to initialize the low-res image + virtual void InitLowResImage( int nWidth, int nHeight, ImageFormat fmt ) = 0; + + // set the resource data (for writers). pass size=0 to delete data. if pdata is not null, + // the resource data will be copied from *pData + virtual void *SetResourceData( uint32 eType, void const *pData, size_t nDataSize ) = 0; + + // find the resource data and return a pointer to it. The data pointed to by this pointer will + // go away when the ivtftexture does. retruns null if resource not present + virtual void *GetResourceData( uint32 eType, size_t *pDataSize ) const = 0; + + // Locates the resource entry info if it's present, easier than crawling array types + virtual bool HasResourceEntry( uint32 eType ) const = 0; + + // Retrieve available resource types of this IVTFTextures + // arrTypesBuffer buffer to be filled with resource types available. + // numTypesBufferElems how many resource types the buffer can accomodate. + // Returns: + // number of resource types available (can be greater than "numTypesBufferElems" + // in which case only first "numTypesBufferElems" are copied to "arrTypesBuffer") + virtual unsigned int GetResourceTypes( uint32 *arrTypesBuffer, int numTypesBufferElems ) const = 0; + + // When unserializing, we can skip a certain number of mip levels, + // and we also can just load everything but the image data + // NOTE: If you load only the buffer header, you'll need to use the + // VTFBufferHeaderSize() method below to only read that much from the file + // NOTE: If you skip mip levels, the height + width of the texture will + // change to reflect the size of the largest read in mip level + virtual bool Unserialize( CUtlBuffer &buf, bool bHeaderOnly = false, int nSkipMipLevels = 0 ) = 0; + virtual bool Serialize( CUtlBuffer &buf ) = 0; + + // These are methods to help with optimization: + // Once the header is read in, they indicate where to start reading + // other data (measured from file start), and how many bytes to read.... + virtual void LowResFileInfo( int *pStartLocation, int *pSizeInBytes) const = 0; + virtual void ImageFileInfo( int nFrame, int nFace, int nMip, int *pStartLocation, int *pSizeInBytes) const = 0; + virtual int FileSize( int nMipSkipCount = 0 ) const = 0; + + // Attributes... + virtual int Width() const = 0; + virtual int Height() const = 0; + virtual int Depth() const = 0; + virtual int MipCount() const = 0; + + // returns the size of one row of a particular mip level + virtual int RowSizeInBytes( int nMipLevel ) const = 0; + + // returns the size of one face of a particular mip level + virtual int FaceSizeInBytes( int nMipLevel ) const = 0; + + virtual ImageFormat Format() const = 0; + virtual int FaceCount() const = 0; + virtual int FrameCount() const = 0; + virtual int Flags() const = 0; + + virtual float BumpScale() const = 0; + + virtual int LowResWidth() const = 0; + virtual int LowResHeight() const = 0; + virtual ImageFormat LowResFormat() const = 0; + + // NOTE: reflectivity[0] = blue, [1] = greem, [2] = red + virtual const Vector &Reflectivity() const = 0; + + virtual bool IsCubeMap() const = 0; + virtual bool IsNormalMap() const = 0; + virtual bool IsVolumeTexture() const = 0; + + // Computes the dimensions of a particular mip level + virtual void ComputeMipLevelDimensions( int iMipLevel, int *pMipWidth, int *pMipHeight, int *pMipDepth ) const = 0; + + // Computes the size (in bytes) of a single mipmap of a single face of a single frame + virtual int ComputeMipSize( int iMipLevel ) const = 0; + + // Computes the size of a subrect (specified at the top mip level) at a particular lower mip level + virtual void ComputeMipLevelSubRect( Rect_t* pSrcRect, int nMipLevel, Rect_t *pSubRect ) const = 0; + + // Computes the size (in bytes) of a single face of a single frame + // All mip levels starting at the specified mip level are included + virtual int ComputeFaceSize( int iStartingMipLevel = 0 ) const = 0; + + // Computes the total size (in bytes) of all faces, all frames + virtual int ComputeTotalSize() const = 0; + + // Returns the base address of the image data + virtual uint8 *ImageData() = 0; + + // Returns a pointer to the data associated with a particular frame, face, and mip level + virtual uint8 *ImageData( int iFrame, int iFace, int iMipLevel ) = 0; + + // Returns a pointer to the data associated with a particular frame, face, mip level, and offset + virtual uint8 *ImageData( int iFrame, int iFace, int iMipLevel, int x, int y, int z = 0 ) = 0; + + // Returns the base address of the low-res image data + virtual uint8 *LowResImageData() = 0; + + // Converts the textures image format. Use IMAGE_FORMAT_DEFAULT + // if you want to be able to use various tool functions below + virtual void ConvertImageFormat( ImageFormat fmt, bool bNormalToDUDV, bool bNormalToDXT5GA = false ) = 0; + + // NOTE: The following methods only work on textures using the + // IMAGE_FORMAT_DEFAULT! + + // Generate spheremap based on the current cube faces (only works for cubemaps) + // The look dir indicates the direction of the center of the sphere + // NOTE: Only call this *after* cube faces have been correctly + // oriented (using FixCubemapFaceOrientation) + virtual void GenerateSpheremap( LookDir_t lookDir = LOOK_DOWN_Z ) = 0; + + // Generate spheremap based on the current cube faces (only works for cubemaps) + // The look dir indicates the direction of the center of the sphere + // NOTE: Only call this *after* cube faces have been correctly + // oriented (using FixCubemapFaceOrientation) + virtual void GenerateHemisphereMap( uint8 *pSphereMapBitsRGBA, int targetWidth, + int targetHeight, LookDir_t lookDir, int iFrame ) = 0; + + // Fixes the cubemap faces orientation from our standard to the + // standard the material system needs. + virtual void FixCubemapFaceOrientation( ) = 0; + + // Generates mipmaps from the base mip levels + virtual void GenerateMipmaps() = 0; + + // Put 1/miplevel (1..n) into alpha. + virtual void PutOneOverMipLevelInAlpha() = 0; + + // Scale alpha by miplevel/mipcount + virtual void PremultAlphaWithMipFraction() = 0; + + // Computes the reflectivity + virtual void ComputeReflectivity( ) = 0; + + // Computes the alpha flags + virtual void ComputeAlphaFlags() = 0; + + // Generate the low-res image bits + virtual bool ConstructLowResImage() = 0; + + // Gets the texture all internally consistent assuming you've loaded + // mip 0 of all faces of all frames + virtual void PostProcess(bool bGenerateSpheremap, LookDir_t lookDir = LOOK_DOWN_Z, bool bAllowFixCubemapOrientation = true, bool bLoadedMiplevels = false) = 0; + + // Blends adjacent pixels on cubemap borders, since the card doesn't do it. If the texture + // is S3TC compressed, then it has to do it AFTER the texture has been compressed to prevent + // artifacts along the edges. + // + // If bSkybox is true, it assumes the faces are oriented in the way the engine draws the skybox + // (which happens to be different from the way cubemaps have their faces). + virtual void MatchCubeMapBorders( int iStage, ImageFormat finalFormat, bool bSkybox ) = 0; + + // Sets threshhold values for alphatest mipmapping + virtual void SetAlphaTestThreshholds( float flBase, float flHighFreq ) = 0; + + virtual bool IsPreTiled() const = 0; + +#if defined( _X360 ) + virtual int UpdateOrCreate( const char *pFilename, const char *pPathID = NULL, bool bForce = false ) = 0; + virtual bool UnserializeFromBuffer( CUtlBuffer &buf, bool bBufferIsVolatile, bool bHeaderOnly, bool bPreloadOnly, int nMipSkipCount ) = 0; + virtual int FileSize( bool bPreloadOnly, int nMipSkipCount ) const = 0; + virtual int MappingWidth() const = 0; + virtual int MappingHeight() const = 0; + virtual int MappingDepth() const = 0; + virtual int MipSkipCount() const = 0; + virtual uint8 *LowResImageSample() = 0; + virtual void ReleaseImageMemory() = 0; +#endif + + // Sets post-processing flags (settings are copied, pointer passed to distinguish between structure versions) + virtual void SetPostProcessingSettings( VtfProcessingOptions const *pOptions ) = 0; +}; + +//----------------------------------------------------------------------------- +// Class factory +//----------------------------------------------------------------------------- +IVTFTexture *CreateVTFTexture(); +void DestroyVTFTexture( IVTFTexture *pTexture ); + +//----------------------------------------------------------------------------- +// Allows us to only load in the first little bit of the VTF file to get info +// Clients should read this much into a UtlBuffer and then pass it in to +// Unserialize +//----------------------------------------------------------------------------- +int VTFFileHeaderSize( int nMajorVersion = -1, int nMinorVersion = -1 ); + +//----------------------------------------------------------------------------- +// 360 Conversion +//----------------------------------------------------------------------------- +typedef bool (*CompressFunc_t)( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer ); +bool ConvertVTFTo360Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc ); +bool SRGBCorrectImage( uint8 *pImage,int imageSize, ImageFormat imageFormat); + +//----------------------------------------------------------------------------- +// 360 Preload +//----------------------------------------------------------------------------- +bool GetVTFPreload360Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut ); + +#include "mathlib/vector.h" + +#endif // VTF_FILE_FORMAT_ONLY + +//----------------------------------------------------------------------------- +// Disk format for VTF files ver. 7.2 and earlier +// +// NOTE: After the header is the low-res image data +// Then follows image data, which is sorted in the following manner +// +// for each mip level (starting with 1x1, and getting larger) +// for each animation frame +// for each face +// store the image data for the face +// +// NOTE: In memory, we store the data in the following manner: +// for each animation frame +// for each face +// for each mip level (starting with the largest, and getting smaller) +// store the image data for the face +// +// This is done because the various image manipulation function we have +// expect this format +//----------------------------------------------------------------------------- +// Disk format for VTF files ver. 7.3 +// +// NOTE: After the header is the array of ResourceEntryInfo structures, +// number of elements in the array is defined by "numResources". +// there are entries for: +// eRsrcLowResImage = low-res image data +// eRsrcSheet = sheet data +// eRsrcImage = image data +// { +// for each mip level (starting with 1x1, and getting larger) +// for each animation frame +// for each face +// store the image data for the face +// +// NOTE: In memory, we store the data in the following manner: +// for each animation frame +// for each face +// for each mip level (starting with the largest, and getting smaller) +// store the image data for the face +// } +// +//----------------------------------------------------------------------------- + + +#include "datamap.h" + +#pragma pack(1) + +// version number for the disk texture cache +#define VTF_MAJOR_VERSION 7 +#define VTF_MINOR_VERSION 5 + +//----------------------------------------------------------------------------- +// !!!!CRITICAL!!!! BEFORE YOU CHANGE THE FORMAT +// +// The structure sizes ARE NOT what they appear, regardless of Pack(1). +// The "VectorAligned" causes invisible padding in the FINAL derived structure. +// +// Each VTF format has been silently plagued by this. +// +// LOOK AT A 7.3 FILE. The 7.3 structure ends at 0x48 as you would expect by +// counting structure bytes. But, the "Infos" start at 0x50! because the PC +// compiler pads, the 360 compiler does NOT. +//----------------------------------------------------------------------------- + +struct VTFFileBaseHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + char fileTypeString[4]; // "VTF" Valve texture file + int version[2]; // version[0].version[1] + int headerSize; +}; + +struct VTFFileHeaderV7_1_t : public VTFFileBaseHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + uint16 width; + uint16 height; + uint32 flags; + uint16 numFrames; + uint16 startFrame; +#if !defined( _X360 ) && !defined( PLATFORM_OSX ) // OSX GCC (4.2.1) ignores the alignment parameter inside a pack(1) block. + VectorAligned reflectivity; +#else + // must manually align in order to maintain pack(1) expected layout with existing binaries + char pad1[4]; + Vector reflectivity; + char pad2[4]; +#endif + float bumpScale; + ImageFormat imageFormat; + uint8 numMipLevels; + ImageFormat lowResImageFormat; + uint8 lowResImageWidth; + uint8 lowResImageHeight; +}; + +struct VTFFileHeaderV7_2_t : public VTFFileHeaderV7_1_t +{ + DECLARE_BYTESWAP_DATADESC(); + + uint16 depth; +}; + +#define BYTE_POS( byteVal, shft ) uint32( uint32(uint8(byteVal)) << uint8(shft * 8) ) +#if !defined( _X360 ) +#define MK_VTF_RSRC_ID(a, b, c) uint32( BYTE_POS(a, 0) | BYTE_POS(b, 1) | BYTE_POS(c, 2) ) +#define MK_VTF_RSRCF(d) BYTE_POS(d, 3) +#else +#define MK_VTF_RSRC_ID(a, b, c) uint32( BYTE_POS(a, 3) | BYTE_POS(b, 2) | BYTE_POS(c, 1) ) +#define MK_VTF_RSRCF(d) BYTE_POS(d, 0) +#endif + +// Special section for stock resources types +enum ResourceEntryType +{ + // Legacy stock resources, readin/writing are handled differently (i.e. they do not have the length tag word!) + VTF_LEGACY_RSRC_LOW_RES_IMAGE = MK_VTF_RSRC_ID( 0x01, 0, 0 ), // Low-res image data + VTF_LEGACY_RSRC_IMAGE = MK_VTF_RSRC_ID( 0x30, 0, 0 ), // Image data + + // New extended resource + VTF_RSRC_SHEET = MK_VTF_RSRC_ID( 0x10, 0, 0 ), // Sheet data +}; + +// Bytes with special meaning when set in a resource type +enum ResourceEntryTypeFlag +{ + RSRCF_HAS_NO_DATA_CHUNK = MK_VTF_RSRCF( 0x02 ), // Resource doesn't have a corresponding data chunk + RSRCF_MASK = MK_VTF_RSRCF( 0xFF ) // Mask for all the flags +}; + +// Header details constants +enum HeaderDetails +{ + MAX_RSRC_DICTIONARY_ENTRIES = 32, // Max number of resources in dictionary + MAX_X360_RSRC_DICTIONARY_ENTRIES = 4, // 360 needs this to be slim, otherwise preload size suffers +}; + +struct ResourceEntryInfo +{ + union + { + uint32 eType; // Use MK_VTF_??? macros to be endian compliant with the type + uint8 chTypeBytes[4]; + }; + uint32 resData; // Resource data or offset from the beginning of the file +}; + +struct VTFFileHeaderV7_3_t : public VTFFileHeaderV7_2_t +{ + DECLARE_BYTESWAP_DATADESC(); + + char pad4[3]; + uint32 numResources; + +#if defined( _X360 ) || defined( PLATFORM_OSX ) // OSX GCC (4.2.1) ignores the alignment parameter inside a pack(1) block. + // must manually align in order to maintain pack(1) expected layout with existing binaries + char pad5[8]; +#endif + + // AFTER THE IMPLICIT PADDING CAUSED BY THE COMPILER.... + // *** followed by *** ResourceEntryInfo resources[0]; + // Array of resource entry infos sorted ascending by type +}; + +struct VTFFileHeader_t : public VTFFileHeaderV7_3_t +{ + DECLARE_BYTESWAP_DATADESC(); +}; + +#define VTF_X360_MAJOR_VERSION 0x0360 +#define VTF_X360_MINOR_VERSION 8 +struct VTFFileHeaderX360_t : public VTFFileBaseHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + uint32 flags; + uint16 width; // actual width of data in file + uint16 height; // actual height of data in file + uint16 depth; // actual depth of data in file + uint16 numFrames; + uint16 preloadDataSize; // exact size of preload data (may extend into image!) + uint8 mipSkipCount; // used to resconstruct mapping dimensions + uint8 numResources; + Vector reflectivity; // Resides on 16 byte boundary! + float bumpScale; + ImageFormat imageFormat; + uint8 lowResImageSample[4]; + uint32 compressedSize; + + // *** followed by *** ResourceEntryInfo resources[0]; +}; + +/////////////////////////// +// Resource Extensions // +/////////////////////////// + +// extended texture lod control: +#define VTF_RSRC_TEXTURE_LOD_SETTINGS ( MK_VTF_RSRC_ID( 'L','O','D' ) ) +struct TextureLODControlSettings_t +{ + // What to clamp the dimenstions to, mip-map wise, when at picmip 0. keeps texture from + // exceeding (1< +#pragma warning(pop) +#endif +#undef PostMessage + +#endif // WIN32 +#endif // WINLITE_H diff --git a/public/worldsize.h b/public/worldsize.h new file mode 100644 index 0000000..df5f07c --- /dev/null +++ b/public/worldsize.h @@ -0,0 +1,43 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// worldsize.h -- extent of world and resolution/size of coordinate messages used in engine + +#ifndef WORLDSIZE_H +#define WORLDSIZE_H +#pragma once + + +// These definitions must match the coordinate message sizes in coordsize.h + +// Following values should be +16384, -16384, +15/16, -15/16 +// NOTE THAT IF THIS GOES ANY BIGGER THEN DISK NODES/LEAVES CANNOT USE SHORTS TO STORE THE BOUNDS +#define MAX_COORD_INTEGER (16384) +#define MIN_COORD_INTEGER (-MAX_COORD_INTEGER) +#define MAX_COORD_FRACTION (1.0-(1.0/16.0)) +#define MIN_COORD_FRACTION (-1.0+(1.0/16.0)) + +#define MAX_COORD_FLOAT (16384.0f) +#define MIN_COORD_FLOAT (-MAX_COORD_FLOAT) + +// Width of the coord system, which is TOO BIG to send as a client/server coordinate value +#define COORD_EXTENT (2*MAX_COORD_INTEGER) + +// Maximum traceable distance ( assumes cubic world and trace from one corner to opposite ) +// COORD_EXTENT * sqrt(3) +#define MAX_TRACE_LENGTH ( 1.732050807569 * COORD_EXTENT ) + +// This value is the LONGEST possible range (limited by max valid coordinate number, not 2x) +#define MAX_COORD_RANGE (MAX_COORD_INTEGER) + +#define TEST_COORD( v ) (((v).x>=MIN_COORD_INTEGER*2) && ((v).x<=MAX_COORD_INTEGER*2) && \ + ((v).y>=MIN_COORD_INTEGER*2) && ((v).y<=MAX_COORD_INTEGER*2) && \ + ((v).z>=MIN_COORD_INTEGER*2) && ((v).z<=MAX_COORD_INTEGER*2)) + +#define ASSERT_COORD( v ) Assert( TEST_COORD( v ) ); + +#endif // WORLDSIZE_H diff --git a/public/xwvfile.h b/public/xwvfile.h new file mode 100644 index 0000000..f5d139e --- /dev/null +++ b/public/xwvfile.h @@ -0,0 +1,85 @@ +//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= +// +// Purpose: +// +//============================================================================= + +#ifndef XWVFILE_H +#define XWVFILE_H +#ifdef _WIN32 +#pragma once +#endif + +#pragma pack(1) + +#define XWV_ID (('X'<<24)|('W'<<16)|('V'<<8)|(' '<<0)) +#define XWV_VERSION 4 + +enum xwvSampleRate_t +{ + XWV_RATE_11025 = 0, + XWV_RATE_22050 = 1, + XWV_RATE_44100 = 2, +}; + +enum xwvFormat_t +{ + XWV_FORMAT_PCM = 0, + XWV_FORMAT_XMA = 1, + XWV_FORMAT_ADPCM = 2, +}; + +// generated in big-endian +struct xwvHeader_t +{ + unsigned int id; + unsigned int version; + unsigned int headerSize; // header only + unsigned int staticDataSize; // follows header + unsigned int dataOffset; // start of samples, possibly sector aligned + unsigned int dataSize; // length of samples in bytes + unsigned int numDecodedSamples; // for duration calcs + int loopStart; // -1 = no loop, offset of loop in samples + unsigned short loopBlock; // the xma block where the loop starts + unsigned short numLeadingSamples; // number of leading samples in the loop block to discard + unsigned short numTrailingSamples; // number of trailing samples at the final block to discard + unsigned short vdatSize; // follows seek table + byte format; + byte bitsPerSample; + byte sampleRate; + byte channels; + byte quality; + byte bHasSeekTable; // indicates presence, follows header + byte padding[2]; // created as 0 + + inline unsigned int GetPreloadSize() { return headerSize + staticDataSize; } + + inline int GetBitsPerSample() const { return bitsPerSample; } + + int GetSampleRate() const + { + int rates[] = {11025, 22050, 44100}; + int rate = sampleRate; + return rates[rate]; + } + + inline int GetChannels() const { return channels; } + + void SetSampleRate( int sampleRateIn ) + { + byte rate = ( sampleRateIn == 11025 ) ? XWV_RATE_11025 : ( sampleRateIn==22050 )? XWV_RATE_22050 : XWV_RATE_44100; + sampleRate = rate; + } + + inline void SetChannels( int channelsIn ) { channels = channelsIn; } + + inline int GetSeekTableSize() + { + // seek table is indexed by packets + return bHasSeekTable ? ( dataSize / 2048 ) * sizeof( int ) : 0; + } +}; + +#pragma pack() + +#endif // XWVFILE_H diff --git a/public/zip/XUnzip.h b/public/zip/XUnzip.h new file mode 100644 index 0000000..1e66db2 --- /dev/null +++ b/public/zip/XUnzip.h @@ -0,0 +1,424 @@ +// XUnzip.h Version 1.1 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use 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. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XUNZIP_H +#define XUNZIP_H + +#if !defined( DWORD ) +#ifdef _WIN32 +typedef unsigned long DWORD; +#else +typedef unsigned int DWORD; +#endif +#endif + +#if !defined( TCHAR ) +typedef char TCHAR; +#endif + +#if defined(POSIX) && !defined(MAX_PATH) +#include +#define MAX_PATH PATH_MAX +typedef bool BOOL; +#endif + +#ifndef XZIP_H +#if !defined(DECLARE_HANDLE) +#if !defined(HANDLE) +typedef void *HANDLE; +#endif +#define DECLARE_HANDLE(name) typedef struct name##__ { int unused; } *name +#endif +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that is being created +#endif + +#if defined(_WIN32) && !defined(_WINBASE_) && !defined(_FILETIME_) +#define _FILETIME_ +typedef struct _FILETIME +{ + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME, * LPFILETIME, *PFILETIME; +#endif + +#if defined(POSIX) +typedef time_t FILETIME; +#endif + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenZip() +// +// Purpose: Open an existing zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0 +// +HZIP OpenZip(void *z, unsigned int len, DWORD flags); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE); +// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE); +// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME); +// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened i n any other way, then full random access is possible. +// Note: pipe input is not yet implemented. + + +/////////////////////////////////////////////////////////////////////////////// +// +// GetZipItem() +// +// Purpose: Get information about an item in an open zip archive +// +// Parameters: hz - handle of open zip archive +// index - index number (0 based) of item in zip +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define GetZipItem GetZipItemW +#else +#define GetZipItem GetZipItemA +#endif + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + + +/////////////////////////////////////////////////////////////////////////////// +// +// FindZipItem() +// +// Purpose: Find item by name and return information about it +// +// Parameters: hz - handle of open zip archive +// name - name of file to look for inside zip archive +// ic - TRUE = case insensitive +// index - pointer to index number returned, or -1 +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define FindZipItem FindZipItemW +#else +#define FindZipItem FindZipItemA +#endif + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + + +/////////////////////////////////////////////////////////////////////////////// +// +// UnzipItem() +// +// Purpose: Find item by index and unzip it +// +// Parameters: hz - handle of open zip archive +// index - index number of file to unzip +// dst - target file name of unzipped file +// len - for memory (ZIP_MEMORY. length of buffer; +// otherwise 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE); +// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE); +// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i +#ifdef IS_WINDOWS_PC +#include +#else +#define INVALID_HANDLE_VALUE (void *)0 +#define FILE_BEGIN SEEK_SET +#define FILE_END SEEK_END +#endif +#include "utlbuffer.h" +#include "utllinkedlist.h" +#include "zip_utils.h" +#include "zip_uncompressed.h" +#include "checksum_crc.h" +#include "byteswap.h" +#include "utlstring.h" + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +// Data descriptions for byte swapping - only needed +// for structures that are written to file for use by the game. +BEGIN_BYTESWAP_DATADESC( ZIP_EndOfCentralDirRecord ) + DEFINE_FIELD( signature, FIELD_INTEGER ), + DEFINE_FIELD( numberOfThisDisk, FIELD_SHORT ), + DEFINE_FIELD( numberOfTheDiskWithStartOfCentralDirectory, FIELD_SHORT ), + DEFINE_FIELD( nCentralDirectoryEntries_ThisDisk, FIELD_SHORT ), + DEFINE_FIELD( nCentralDirectoryEntries_Total, FIELD_SHORT ), + DEFINE_FIELD( centralDirectorySize, FIELD_INTEGER ), + DEFINE_FIELD( startOfCentralDirOffset, FIELD_INTEGER ), + DEFINE_FIELD( commentLength, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ZIP_FileHeader ) + DEFINE_FIELD( signature, FIELD_INTEGER ), + DEFINE_FIELD( versionMadeBy, FIELD_SHORT ), + DEFINE_FIELD( versionNeededToExtract, FIELD_SHORT ), + DEFINE_FIELD( flags, FIELD_SHORT ), + DEFINE_FIELD( compressionMethod, FIELD_SHORT ), + DEFINE_FIELD( lastModifiedTime, FIELD_SHORT ), + DEFINE_FIELD( lastModifiedDate, FIELD_SHORT ), + DEFINE_FIELD( crc32, FIELD_INTEGER ), + DEFINE_FIELD( compressedSize, FIELD_INTEGER ), + DEFINE_FIELD( uncompressedSize, FIELD_INTEGER ), + DEFINE_FIELD( fileNameLength, FIELD_SHORT ), + DEFINE_FIELD( extraFieldLength, FIELD_SHORT ), + DEFINE_FIELD( fileCommentLength, FIELD_SHORT ), + DEFINE_FIELD( diskNumberStart, FIELD_SHORT ), + DEFINE_FIELD( internalFileAttribs, FIELD_SHORT ), + DEFINE_FIELD( externalFileAttribs, FIELD_INTEGER ), + DEFINE_FIELD( relativeOffsetOfLocalHeader, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ZIP_LocalFileHeader ) + DEFINE_FIELD( signature, FIELD_INTEGER ), + DEFINE_FIELD( versionNeededToExtract, FIELD_SHORT ), + DEFINE_FIELD( flags, FIELD_SHORT ), + DEFINE_FIELD( compressionMethod, FIELD_SHORT ), + DEFINE_FIELD( lastModifiedTime, FIELD_SHORT ), + DEFINE_FIELD( lastModifiedDate, FIELD_SHORT ), + DEFINE_FIELD( crc32, FIELD_INTEGER ), + DEFINE_FIELD( compressedSize, FIELD_INTEGER ), + DEFINE_FIELD( uncompressedSize, FIELD_INTEGER ), + DEFINE_FIELD( fileNameLength, FIELD_SHORT ), + DEFINE_FIELD( extraFieldLength, FIELD_SHORT ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ZIP_PreloadHeader ) + DEFINE_FIELD( Version, FIELD_INTEGER ), + DEFINE_FIELD( DirectoryEntries, FIELD_INTEGER ), + DEFINE_FIELD( PreloadDirectoryEntries, FIELD_INTEGER ), + DEFINE_FIELD( Alignment, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( ZIP_PreloadDirectoryEntry ) + DEFINE_FIELD( Length, FIELD_INTEGER ), + DEFINE_FIELD( DataOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +#ifdef WIN32 +//----------------------------------------------------------------------------- +// For >2 GB File Support +//----------------------------------------------------------------------------- +class CWin32File +{ +public: + static HANDLE CreateTempFile( CUtlString &WritePath, CUtlString &FileName ) + { + char tempFileName[MAX_PATH]; + if ( WritePath.IsEmpty() ) + { + // use a safe name in the cwd + char *pBuffer = tmpnam( NULL ); + if ( !pBuffer ) + { + return INVALID_HANDLE_VALUE; + } + if ( pBuffer[0] == '\\' ) + { + pBuffer++; + } + if ( pBuffer[strlen( pBuffer )-1] == '.' ) + { + pBuffer[strlen( pBuffer )-1] = '\0'; + } + V_snprintf( tempFileName, sizeof( tempFileName ), "_%s.tmp", pBuffer ); + } + else + { + // generate safe name at the desired prefix + char uniqueFilename[MAX_PATH]; + SYSTEMTIME sysTime; \ + GetLocalTime( &sysTime ); + sprintf( uniqueFilename, "%d_%d_%d_%d_%d.tmp", sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond, sysTime.wMilliseconds ); \ + V_ComposeFileName( WritePath.String(), uniqueFilename, tempFileName, sizeof( tempFileName ) ); + } + + FileName = tempFileName; + HANDLE hFile = CreateFile( tempFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + + return hFile; + } + + static unsigned int FileSeek( HANDLE hFile, unsigned int distance, DWORD MoveMethod ) + { + LARGE_INTEGER li; + + li.QuadPart = distance; + li.LowPart = SetFilePointer( hFile, li.LowPart, &li.HighPart, MoveMethod); + if ( li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR ) + { + li.QuadPart = -1; + } + + return ( unsigned int )li.QuadPart; + } + + static unsigned int FileTell( HANDLE hFile ) + { + return FileSeek( hFile, 0, FILE_CURRENT ); + } + + static bool FileRead( HANDLE hFile, void *pBuffer, unsigned int size ) + { + DWORD numBytesRead; + BOOL bSuccess = ::ReadFile( hFile, pBuffer, size, &numBytesRead, NULL ); + return bSuccess && ( numBytesRead == size ); + } + + static bool FileWrite( HANDLE hFile, void *pBuffer, unsigned int size ) + { + DWORD numBytesWritten; + BOOL bSuccess = WriteFile( hFile, pBuffer, size, &numBytesWritten, NULL ); + return bSuccess && ( numBytesWritten == size ); + } +}; +#else +class CWin32File +{ +public: + static HANDLE CreateTempFile( CUtlString &WritePath, CUtlString &FileName ) + { + char tempFileName[MAX_PATH]; + if ( WritePath.IsEmpty() ) + { + // use a safe name in the cwd + char *pBuffer = tmpnam( NULL ); + if ( !pBuffer ) + { + return INVALID_HANDLE_VALUE; + } + if ( pBuffer[0] == '\\' ) + { + pBuffer++; + } + if ( pBuffer[strlen( pBuffer )-1] == '.' ) + { + pBuffer[strlen( pBuffer )-1] = '\0'; + } + V_snprintf( tempFileName, sizeof( tempFileName ), "_%s.tmp", pBuffer ); + } + else + { + char uniqueFilename[MAX_PATH]; + static int counter = 0; + time_t now = time( NULL ); + struct tm *tm = localtime( &now ); + sprintf( uniqueFilename, "%d_%d_%d_%d_%d.tmp", tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec, ++counter ); \ + V_ComposeFileName( WritePath.String(), uniqueFilename, tempFileName, sizeof( tempFileName ) ); + } + + FileName = tempFileName; + FILE *hFile = fopen( tempFileName, "rw+" ); + + return (HANDLE)hFile; + } + + static unsigned int FileSeek( HANDLE hFile, unsigned int distance, DWORD MoveMethod ) + { + return fseeko( (FILE *)hFile, distance, MoveMethod ); + } + + static unsigned int FileTell( HANDLE hFile ) + { + return ftello( (FILE *)hFile ); + } + + static bool FileRead( HANDLE hFile, void *pBuffer, unsigned int size ) + { + size_t bytesRead = fread( pBuffer, 1, size, (FILE *)hFile ); + return bytesRead == size; + } + + static bool FileWrite( HANDLE hFile, void *pBuffer, unsigned int size ) + { + size_t bytesWrtitten = fwrite( pBuffer, 1, size, (FILE *)hFile ); + return bytesWrtitten == size; + } +}; +#endif + +//----------------------------------------------------------------------------- +// Purpose: Interface to allow abstraction of zip file output methods, and +// avoid duplication of code. Files may be written to a CUtlBuffer or a filestream +//----------------------------------------------------------------------------- +abstract_class IWriteStream +{ +public: + virtual void Put( const void* pMem, int size ) = 0; + virtual unsigned int Tell( void ) = 0; +}; + +//----------------------------------------------------------------------------- +// Purpose: Wrapper for CUtlBuffer methods +//----------------------------------------------------------------------------- +class CBufferStream : public IWriteStream +{ +public: + CBufferStream( CUtlBuffer& buff ) : IWriteStream(), m_buff( &buff ) {} + + // Implementing IWriteStream method + virtual void Put( const void* pMem, int size ) { m_buff->Put( pMem, size ); } + + // Implementing IWriteStream method + virtual unsigned int Tell( void ) { return m_buff->TellPut(); } + +private: + CUtlBuffer *m_buff; +}; + +//----------------------------------------------------------------------------- +// Purpose: Wrapper for file I/O methods +//----------------------------------------------------------------------------- +class CFileStream : public IWriteStream +{ +public: + CFileStream( FILE *fout ) : IWriteStream(), m_file( fout ), m_hFile( INVALID_HANDLE_VALUE ) {} + CFileStream( HANDLE hOutFile ) : IWriteStream(), m_file( NULL ), m_hFile( hOutFile ) {} + + // Implementing IWriteStream method + virtual void Put( const void* pMem, int size ) + { + if ( m_file ) + { + fwrite( pMem, size, 1, m_file ); + } +#ifdef WIN32 + else + { + DWORD numBytesWritten; + WriteFile( m_hFile, pMem, size, &numBytesWritten, NULL ); + } +#endif + } + + // Implementing IWriteStream method + virtual unsigned int Tell( void ) + { + if ( m_file ) + { + return ftell( m_file ); + } +#ifdef WIN32 + else + { + return CWin32File::FileTell( m_hFile ); + } +#endif + } + +private: + FILE *m_file; + HANDLE m_hFile; +}; + +//----------------------------------------------------------------------------- +// Purpose: Container for modifiable pak file which is embedded inside the .bsp file +// itself. It's used to allow one-off files to be stored local to the map and it is +// hooked into the file system as an override for searching for named files. +//----------------------------------------------------------------------------- +class CZipFile +{ +public: + // Construction + CZipFile( const char *pDiskCacheWritePath, bool bSortByName ); + ~CZipFile( void ); + + // Public API + // Clear all existing data + void Reset( void ); + + // Add file to zip under relative name + void AddFileToZip( const char *relativename, const char *fullpath ); + + // Delete file from zip + void RemoveFileFromZip( const char *relativename ); + + // Add buffer to zip as a file with given name + void AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode ); + + // Check if a file already exists in the zip. + bool FileExistsInZip( const char *relativename ); + + // Reads a file from a zip file + bool ReadFileFromZip( const char *relativename, bool bTextMode, CUtlBuffer &buf ); + bool ReadFileFromZip( HANDLE hZipFile, const char *relativename, bool bTextMode, CUtlBuffer &buf ); + + // Initialize the zip file from a buffer + void ParseFromBuffer( void *buffer, int bufferlength ); + HANDLE ParseFromDisk( const char *pFilename ); + + // Estimate the size of the zip file (including header, padding, etc.) + unsigned int EstimateSize(); + + // Print out a directory of files in the zip. + void PrintDirectory( void ); + + // Use to iterate directory, pass 0 for first element + // returns nonzero element id with filled buffer, or -1 at list conclusion + int GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize ); + + // Write the zip to a buffer + void SaveToBuffer( CUtlBuffer& buffer ); + // Write the zip to a filestream + void SaveToDisk( FILE *fout ); + void SaveToDisk( HANDLE hOutFile ); + + unsigned int CalculateSize( void ); + + void ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize ); + + unsigned int GetAlignment(); + + void SetBigEndian( bool bigEndian ); + void ActivateByteSwapping( bool bActivate ); + +private: + enum + { + MAX_FILES_IN_ZIP = 32768, + }; + + typedef struct + { + CUtlSymbol m_Name; + unsigned int filepos; + int filelen; + } TmpFileInfo_t; + + CByteswap m_Swap; + unsigned int m_AlignmentSize; + bool m_bForceAlignment; + bool m_bCompatibleFormat; + + unsigned short CalculatePadding( unsigned int filenameLen, unsigned int pos ); + void SaveDirectory( IWriteStream& stream ); + int MakeXZipCommentString( char *pComment ); + void ParseXZipCommentString( const char *pComment ); + + // Internal entry for faster searching, etc. + class CZipEntry + { + public: + CZipEntry( void ); + ~CZipEntry( void ); + + CZipEntry( const CZipEntry& src ); + + // RB tree compare function + static bool ZipFileLessFunc( CZipEntry const& src1, CZipEntry const& src2 ); + static bool ZipFileLessFunc_CaselessSort( CZipEntry const& src1, CZipEntry const& src2 ); + + // Name of entry + CUtlSymbol m_Name; + + // Lenth of data element + int m_Length; + // Raw data, could be null and data may be in disk write cache + void *m_pData; + + // Offset in Zip ( set and valid during final write ) + unsigned int m_ZipOffset; + // CRC of blob ( set and valid during final write ) + CRC32_t m_ZipCRC; + + // Location of data in disk cache + unsigned int m_DiskCacheOffset; + unsigned int m_SourceDiskOffset; + }; + + // For fast name lookup and sorting + CUtlRBTree< CZipEntry, int > m_Files; + + // Used to buffer zip data, instead of ram + bool m_bUseDiskCacheForWrites; + HANDLE m_hDiskCacheWriteFile; + CUtlString m_DiskCacheName; + CUtlString m_DiskCacheWritePath; + + bool m_bIsUpdateFormat; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CZipFile::CZipEntry::CZipEntry( void ) +{ + m_Name = ""; + m_Length = 0; + m_pData = NULL; + m_ZipOffset = 0; + m_ZipCRC = 0; + m_DiskCacheOffset = 0; + m_SourceDiskOffset = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : src - +//----------------------------------------------------------------------------- +CZipFile::CZipEntry::CZipEntry( const CZipFile::CZipEntry& src ) +{ + m_Name = src.m_Name; + m_Length = src.m_Length; + + if ( src.m_Length > 0 && src.m_pData ) + { + m_pData = malloc( src.m_Length ); + memcpy( m_pData, src.m_pData, src.m_Length ); + } + else + { + m_pData = NULL; + } + + m_ZipOffset = src.m_ZipOffset; + m_ZipCRC = src.m_ZipCRC; + m_DiskCacheOffset = src.m_DiskCacheOffset; + m_SourceDiskOffset = src.m_SourceDiskOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: Clear any leftover data +//----------------------------------------------------------------------------- +CZipFile::CZipEntry::~CZipEntry( void ) +{ + if ( m_pData ) + { + free( m_pData ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Construction +//----------------------------------------------------------------------------- +CZipFile::CZipFile( const char *pDiskCacheWritePath, bool bSortByName ) +: m_Files( 0, 32 ) +{ + m_AlignmentSize = 0; + m_bForceAlignment = false; + m_bCompatibleFormat = true; + m_bIsUpdateFormat = false; + + m_bUseDiskCacheForWrites = ( pDiskCacheWritePath != NULL ); + m_DiskCacheWritePath = pDiskCacheWritePath; + m_hDiskCacheWriteFile = INVALID_HANDLE_VALUE; + + if ( bSortByName ) + { + m_Files.SetLessFunc( CZipEntry::ZipFileLessFunc_CaselessSort ); + } + else + { + m_Files.SetLessFunc( CZipEntry::ZipFileLessFunc ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy zip data +//----------------------------------------------------------------------------- +CZipFile::~CZipFile( void ) +{ + m_bUseDiskCacheForWrites = false; + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: Delete all current data +//----------------------------------------------------------------------------- +void CZipFile::Reset( void ) +{ + m_Files.RemoveAll(); + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { +#ifdef WIN32 + CloseHandle( m_hDiskCacheWriteFile ); + DeleteFile( m_DiskCacheName.String() ); +#else + fclose( (FILE *)m_hDiskCacheWriteFile ); + unlink( m_DiskCacheName.String() ); +#endif + m_hDiskCacheWriteFile = INVALID_HANDLE_VALUE; + } + + if ( m_bUseDiskCacheForWrites ) + { + m_hDiskCacheWriteFile = CWin32File::CreateTempFile( m_DiskCacheWritePath, m_DiskCacheName ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Comparison for sorting entries +// Input : src1 - +// src2 - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CZipFile::CZipEntry::ZipFileLessFunc( CZipEntry const& src1, CZipEntry const& src2 ) +{ + return ( src1.m_Name < src2.m_Name ); +} + +bool CZipFile::CZipEntry::ZipFileLessFunc_CaselessSort( CZipEntry const& src1, CZipEntry const& src2 ) +{ + return ( V_stricmp( src1.m_Name.String(), src2.m_Name.String() ) < 0 ); +} + +void CZipFile::ForceAlignment( bool bAligned, bool bCompatibleFormat, unsigned int alignment ) +{ + // special update format, force the args as desired + m_bIsUpdateFormat = false; + if ( alignment == 0xFFFFFFFF ) + { + bAligned = false; + alignment = 0; + m_bIsUpdateFormat = true; + } + + m_bForceAlignment = bAligned; + m_AlignmentSize = alignment; + m_bCompatibleFormat = bCompatibleFormat; + + if ( !bAligned ) + { + m_AlignmentSize = 0; + } + else if ( !IsPowerOfTwo( m_AlignmentSize ) ) + { + m_AlignmentSize = 0; + } +} + +unsigned int CZipFile::GetAlignment() +{ + if ( !m_bForceAlignment || !m_AlignmentSize ) + { + return 0; + } + + return m_AlignmentSize; +} + +void CZipFile::SetBigEndian( bool bigEndian ) +{ + m_Swap.SetTargetBigEndian( bigEndian ); +} + +void CZipFile::ActivateByteSwapping( bool bActivate ) +{ + m_Swap.ActivateByteSwapping( bActivate ); +} + +//----------------------------------------------------------------------------- +// Purpose: Load pak file from raw buffer +// Input : *buffer - +// bufferlength - +//----------------------------------------------------------------------------- +void CZipFile::ParseFromBuffer( void *buffer, int bufferlength ) +{ + // Throw away old data + Reset(); + + // Initialize a buffer + CUtlBuffer buf( 0, bufferlength +1 ); // +1 for null termination + + // need to swap bytes, so set the buffer opposite the machine's endian + buf.ActivateByteSwapping( m_Swap.IsSwappingBytes() ); + + buf.Put( buffer, bufferlength ); + + buf.SeekGet( CUtlBuffer::SEEK_TAIL, 0 ); + unsigned int fileLen = buf.TellGet(); + + // Start from beginning + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + unsigned int offset; + ZIP_EndOfCentralDirRecord rec = { 0 }; + + bool bFoundEndOfCentralDirRecord = false; + for ( offset = fileLen - sizeof( ZIP_EndOfCentralDirRecord ); offset >= 0; offset-- ) + { + buf.SeekGet( CUtlBuffer::SEEK_HEAD, offset ); + buf.GetObjects( &rec ); + if ( rec.signature == PKID( 5, 6 ) ) + { + bFoundEndOfCentralDirRecord = true; + + // Set any xzip configuration + if ( rec.commentLength ) + { + char commentString[128]; + int commentLength = min( rec.commentLength, sizeof( commentString ) ); + buf.Get( commentString, commentLength ); + commentString[commentLength] = '\0'; + ParseXZipCommentString( commentString ); + } + break; + } + else + { + // wrong record + rec.nCentralDirectoryEntries_Total = 0; + } + } + Assert( bFoundEndOfCentralDirRecord ); + + // Make sure there are some files to parse + int numzipfiles = rec.nCentralDirectoryEntries_Total; + if ( numzipfiles <= 0 ) + { + // No files + return; + } + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, rec.startOfCentralDirOffset ); + + // Allocate space for directory + TmpFileInfo_t *newfiles = new TmpFileInfo_t[numzipfiles]; + Assert( newfiles ); + + // build directory + int i; + for ( i = 0; i < rec.nCentralDirectoryEntries_Total; i++ ) + { + ZIP_FileHeader zipFileHeader; + buf.GetObjects( &zipFileHeader ); + Assert( zipFileHeader.signature == PKID( 1, 2 ) ); + Assert( zipFileHeader.compressionMethod == 0 ); + + char tmpString[1024]; + buf.Get( tmpString, zipFileHeader.fileNameLength ); + tmpString[zipFileHeader.fileNameLength] = '\0'; + Q_strlower( tmpString ); + + // can determine actual filepos, assuming a well formed zip + newfiles[i].m_Name = tmpString; + newfiles[i].filelen = zipFileHeader.compressedSize; + newfiles[i].filepos = zipFileHeader.relativeOffsetOfLocalHeader + + sizeof( ZIP_LocalFileHeader ) + + zipFileHeader.fileNameLength + + zipFileHeader.extraFieldLength; + + int nextOffset; + if ( m_bCompatibleFormat ) + { + nextOffset = zipFileHeader.extraFieldLength + zipFileHeader.fileCommentLength; + } + else + { + nextOffset = 0; + } + buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nextOffset ); + } + + // Insert current data into rb tree + for ( i=0; i 0 ) + { + e.m_pData = malloc( e.m_Length ); + + // Copy in data + buf.SeekGet( CUtlBuffer::SEEK_HEAD, newfiles[i].filepos ); + buf.Get( e.m_pData, e.m_Length ); + } + else + { + e.m_pData = NULL; + } + + // Add to tree + m_Files.Insert( e ); + } + + // Through away directory + delete[] newfiles; +} + +//----------------------------------------------------------------------------- +// Purpose: Mount pak file from disk +//----------------------------------------------------------------------------- +HANDLE CZipFile::ParseFromDisk( const char *pFilename ) +{ +#ifdef WIN32 + HANDLE hFile = CreateFile( pFilename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); +#else + HANDLE hFile = fopen( pFilename, "rw+" ); +#endif + if ( !hFile ) + { + // not found + return NULL; + } + + unsigned int fileLen = CWin32File::FileSeek( hFile, 0, FILE_END ); + CWin32File::FileSeek( hFile, 0, FILE_BEGIN ); + if ( fileLen < sizeof( ZIP_EndOfCentralDirRecord ) ) + { + // bad format +#ifdef WIN32 + CloseHandle( hFile ); +#else + fclose( (FILE *)hFile ); +#endif + return NULL; + } + + // need to get the central dir + unsigned int offset; + ZIP_EndOfCentralDirRecord rec = { 0 }; + for ( offset = fileLen - sizeof( ZIP_EndOfCentralDirRecord ); offset >= 0; offset-- ) + { + CWin32File::FileSeek( hFile, offset, FILE_BEGIN ); + + CWin32File::FileRead( hFile, &rec, sizeof( rec ) ); + m_Swap.SwapFieldsToTargetEndian( &rec ); + + if ( rec.signature == PKID( 5, 6 ) ) + { + // Set any xzip configuration + if ( rec.commentLength ) + { + char commentString[128]; + int commentLength = min( rec.commentLength, sizeof( commentString ) ); + CWin32File::FileRead( hFile, commentString, commentLength ); + commentString[commentLength] = '\0'; + ParseXZipCommentString( commentString ); + } + break; + } + else + { + // wrong record + rec.nCentralDirectoryEntries_Total = 0; + } + } + + // Make sure there are some files to parse + int numZipFiles = rec.nCentralDirectoryEntries_Total; + if ( numZipFiles <= 0 ) + { + // No files +#ifdef WIN32 + CloseHandle( hFile ); +#else + fclose( (FILE *)hFile ); +#endif + return NULL; + } + + CWin32File::FileSeek( hFile, rec.startOfCentralDirOffset, FILE_BEGIN ); + + // read entire central dir into memory + CUtlBuffer zipDirBuff( 0, rec.centralDirectorySize, 0 ); + zipDirBuff.ActivateByteSwapping( m_Swap.IsSwappingBytes() ); + CWin32File::FileRead( hFile, zipDirBuff.Base(), rec.centralDirectorySize ); + zipDirBuff.SeekPut( CUtlBuffer::SEEK_HEAD, rec.centralDirectorySize ); + + // build directory + for ( int i = 0; i < numZipFiles; i++ ) + { + ZIP_FileHeader zipFileHeader; + zipDirBuff.GetObjects( &zipFileHeader ); + + if ( zipFileHeader.signature != PKID( 1, 2 ) || zipFileHeader.compressionMethod != 0 ) + { + // bad contents +#ifdef WIN32 + CloseHandle( hFile ); +#else + fclose( (FILE *)hFile ); +#endif + return NULL; + } + + char fileName[1024]; + zipDirBuff.Get( fileName, zipFileHeader.fileNameLength ); + fileName[zipFileHeader.fileNameLength] = '\0'; + Q_strlower( fileName ); + + // can determine actual filepos, assuming a well formed zip + CZipEntry e; + e.m_Name = fileName; + e.m_Length = zipFileHeader.compressedSize; + e.m_SourceDiskOffset = zipFileHeader.relativeOffsetOfLocalHeader + + sizeof( ZIP_LocalFileHeader ) + + zipFileHeader.fileNameLength + + zipFileHeader.extraFieldLength; + // Add to tree + m_Files.Insert( e ); + + int nextOffset; + if ( m_bCompatibleFormat ) + { + nextOffset = zipFileHeader.extraFieldLength + zipFileHeader.fileCommentLength; + } + else + { + nextOffset = 0; + } + + zipDirBuff.SeekGet( CUtlBuffer::SEEK_CURRENT, nextOffset ); + } + + return hFile; +} + +static int GetLengthOfBinStringAsText( const char *pSrc, int srcSize ) +{ + const char *pSrcScan = pSrc; + const char *pSrcEnd = pSrc + srcSize; + int numChars = 0; + for( ; pSrcScan < pSrcEnd; pSrcScan++ ) + { + if( *pSrcScan == '\n' ) + { + numChars += 2; + } + else + { + numChars++; + } + } + return numChars; +} + + +//----------------------------------------------------------------------------- +// Copies text data from a form appropriate for disk to a normal string +//----------------------------------------------------------------------------- +static void ReadTextData( const char *pSrc, int nSrcSize, CUtlBuffer &buf ) +{ + buf.EnsureCapacity( nSrcSize + 1 ); + const char *pSrcEnd = pSrc + nSrcSize; + for ( const char *pSrcScan = pSrc; pSrcScan < pSrcEnd; ++pSrcScan ) + { + if ( *pSrcScan == '\r' ) + { + if ( pSrcScan[1] == '\n' ) + { + buf.PutChar( '\n' ); + ++pSrcScan; + continue; + } + } + + buf.PutChar( *pSrcScan ); + } + + // Null terminate + buf.PutChar( '\0' ); +} + + +//----------------------------------------------------------------------------- +// Copies text data into a form appropriate for disk +//----------------------------------------------------------------------------- +static void CopyTextData( char *pDst, const char *pSrc, int dstSize, int srcSize ) +{ + const char *pSrcScan = pSrc; + const char *pSrcEnd = pSrc + srcSize; + char *pDstScan = pDst; + +#ifdef DBGFLAG_ASSERT + char *pDstEnd = pDst + dstSize; +#endif + + for ( ; pSrcScan < pSrcEnd; pSrcScan++ ) + { + if ( *pSrcScan == '\n' ) + { + *pDstScan = '\r'; + pDstScan++; + *pDstScan = '\n'; + pDstScan++; + } + else + { + *pDstScan = *pSrcScan; + pDstScan++; + } + } + Assert( pSrcScan == pSrcEnd ); + Assert( pDstScan == pDstEnd ); +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a new lump, or overwrites existing one +// Input : *relativename - +// *data - +// length - +//----------------------------------------------------------------------------- +void CZipFile::AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode ) +{ + // Lower case only + char name[512]; + Q_strcpy( name, relativename ); + Q_strlower( name ); + + int dstLength = length; + if ( bTextMode ) + { + dstLength = GetLengthOfBinStringAsText( ( const char * )data, length ); + } + + // See if entry is in list already + CZipEntry e; + e.m_Name = name; + int index = m_Files.Find( e ); + + // If already existing, throw away old data and update data and length + if ( index != m_Files.InvalidIndex() ) + { + CZipEntry *update = &m_Files[ index ]; + if ( update->m_pData ) + { + free( update->m_pData ); + } + + if ( bTextMode ) + { + update->m_pData = malloc( dstLength ); + CopyTextData( ( char * )update->m_pData, ( char * )data, dstLength, length ); + update->m_Length = dstLength; + } + else + { + update->m_pData = malloc( length ); + memcpy( update->m_pData, data, length ); + update->m_Length = length; + } + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { + update->m_DiskCacheOffset = CWin32File::FileTell( m_hDiskCacheWriteFile ); + CWin32File::FileWrite( m_hDiskCacheWriteFile, update->m_pData, update->m_Length ); + free( update->m_pData ); + update->m_pData = NULL; + } + } + else + { + // Create a new entry + e.m_Length = dstLength; + if ( dstLength > 0 ) + { + if ( bTextMode ) + { + e.m_pData = malloc( dstLength ); + CopyTextData( (char *)e.m_pData, ( char * )data, dstLength, length ); + } + else + { + e.m_pData = malloc( length ); + memcpy( e.m_pData, data, length ); + } + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { + e.m_DiskCacheOffset = CWin32File::FileTell( m_hDiskCacheWriteFile ); + CWin32File::FileWrite( m_hDiskCacheWriteFile, e.m_pData, e.m_Length ); + free( e.m_pData ); + e.m_pData = NULL; + } + } + else + { + e.m_pData = NULL; + } + + m_Files.Insert( e ); + } +} + + +//----------------------------------------------------------------------------- +// Reads a file from the zip +//----------------------------------------------------------------------------- +bool CZipFile::ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) +{ + // Lower case only + char pName[512]; + Q_strncpy( pName, pRelativeName, 512 ); + Q_strlower( pName ); + + // See if entry is in list already + CZipEntry e; + e.m_Name = pName; + int nIndex = m_Files.Find( e ); + if ( nIndex == m_Files.InvalidIndex() ) + { + // not found + return false; + } + + CZipEntry *pEntry = &m_Files[ nIndex ]; + if ( bTextMode ) + { + buf.SetBufferType( true, false ); + ReadTextData( (char*)pEntry->m_pData, pEntry->m_Length, buf ); + } + else + { + buf.SetBufferType( false, false ); + buf.Put( pEntry->m_pData, pEntry->m_Length ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Reads a file from the zip +//----------------------------------------------------------------------------- +bool CZipFile::ReadFileFromZip( HANDLE hZipFile, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) +{ + // Lower case only + char pName[512]; + Q_strncpy( pName, pRelativeName, 512 ); + Q_strlower( pName ); + + // See if entry is in list already + CZipEntry e; + e.m_Name = pName; + int nIndex = m_Files.Find( e ); + if ( nIndex == m_Files.InvalidIndex() ) + { + // not found + return false; + } + + CZipEntry *pEntry = &m_Files[nIndex]; + + void *pData = malloc( pEntry->m_Length ); + CWin32File::FileSeek( hZipFile, pEntry->m_SourceDiskOffset, FILE_BEGIN ); + if ( !CWin32File::FileRead( hZipFile, pData, pEntry->m_Length ) ) + { + free( pData ); + return false; + } + + if ( bTextMode ) + { + buf.SetBufferType( true, false ); + ReadTextData( (const char *)pData, pEntry->m_Length, buf ); + } + else + { + buf.SetBufferType( false, false ); + buf.Put( pData, pEntry->m_Length ); + } + + free( pData ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Check if a file already exists in the zip. +// Input : *relativename - +//----------------------------------------------------------------------------- +bool CZipFile::FileExistsInZip( const char *pRelativeName ) +{ + // Lower case only + char pName[512]; + Q_strncpy( pName, pRelativeName, 512 ); + Q_strlower( pName ); + + // See if entry is in list already + CZipEntry e; + e.m_Name = pName; + int nIndex = m_Files.Find( e ); + + // If it is, then it exists in the pack! + return nIndex != m_Files.InvalidIndex(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Adds a new file to the zip. +//----------------------------------------------------------------------------- +void CZipFile::AddFileToZip( const char *relativename, const char *fullpath ) +{ + FILE *temp = fopen( fullpath, "rb" ); + if ( !temp ) + return; + + // Determine length + fseek( temp, 0, SEEK_END ); + int size = ftell( temp ); + fseek( temp, 0, SEEK_SET ); + byte *buf = (byte *)malloc( size + 1 ); + + // Read data + fread( buf, size, 1, temp ); + fclose( temp ); + + // Now add as a buffer + AddBufferToZip( relativename, buf, size, false ); + + free( buf ); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes a file from the zip. +//----------------------------------------------------------------------------- +void CZipFile::RemoveFileFromZip( const char *relativename ) +{ + CZipEntry e; + e.m_Name = relativename; + int index = m_Files.Find( e ); + + if ( index != m_Files.InvalidIndex() ) + { + CZipEntry update = m_Files[index]; + m_Files.Remove( update ); + } +} + +//--------------------------------------------------------------- +// Purpose: Calculates how many bytes should be added to the extra field +// to push the start of the file data to the next aligned boundary +// Output: Required padding size +//--------------------------------------------------------------- +unsigned short CZipFile::CalculatePadding( unsigned int filenameLen, unsigned int pos ) +{ + if ( m_AlignmentSize == 0 ) + { + return 0; + } + + unsigned int headerSize = sizeof( ZIP_LocalFileHeader ) + filenameLen; + return (unsigned short)( m_AlignmentSize - ( ( pos + headerSize ) % m_AlignmentSize ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Create the XZIP identifying comment string +// Output : Length +//----------------------------------------------------------------------------- +int CZipFile::MakeXZipCommentString( char *pCommentString ) +{ + char tempString[XZIP_COMMENT_LENGTH]; + + memset( tempString, 0, sizeof( tempString ) ); + + char cFormat = m_bCompatibleFormat ? '1' : '2'; + if ( m_bCompatibleFormat ) + { + cFormat = '1'; + } + else if ( !m_bIsUpdateFormat ) + { + cFormat = '2'; + } + else + { + // update format + cFormat = '3'; + } + + V_snprintf( tempString, sizeof( tempString ), "XZP%c %d", cFormat, m_AlignmentSize ); + if ( pCommentString ) + { + memcpy( pCommentString, tempString, sizeof( tempString ) ); + } + + // expected fixed length + return XZIP_COMMENT_LENGTH; +} + +//----------------------------------------------------------------------------- +// Purpose: An XZIP has its configuration in the ascii comment +//----------------------------------------------------------------------------- +void CZipFile::ParseXZipCommentString( const char *pCommentString ) +{ + if ( !V_strnicmp( pCommentString, "XZP", 3 ) ) + { + m_bCompatibleFormat = true; + if ( pCommentString[3] == '2' ) + { + m_bCompatibleFormat = false; + } + else if ( pCommentString[3] == '3' ) + { + m_bCompatibleFormat = false; + m_bIsUpdateFormat = true; + } + + // parse out the alignement configuration + if ( !m_bForceAlignment ) + { + m_AlignmentSize = 0; + sscanf( pCommentString + 4, "%d", &m_AlignmentSize ); + if ( !IsPowerOfTwo( m_AlignmentSize ) ) + { + m_AlignmentSize = 0; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate the exact size of zip file, with headers and padding +// Output : int +//----------------------------------------------------------------------------- +unsigned int CZipFile::CalculateSize( void ) +{ + unsigned int size = 0; + unsigned int dirHeaders = 0; + for ( int i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) ) + { + CZipEntry *e = &m_Files[ i ]; + + if ( e->m_Length == 0 ) + continue; + + // local file header + size += sizeof( ZIP_LocalFileHeader ); + size += strlen( e->m_Name.String() ); + + // every file has a directory header that duplicates the filename + dirHeaders += sizeof( ZIP_FileHeader ) + strlen( e->m_Name.String() ); + + // calculate padding + if ( m_AlignmentSize != 0 ) + { + // round up to next boundary + unsigned int nextBoundary = ( size + m_AlignmentSize ) & ~( m_AlignmentSize - 1 ); + + // the directory header also duplicates the padding + dirHeaders += nextBoundary - size; + + size = nextBoundary; + } + + // data size + size += e->m_Length; + } + + size += dirHeaders; + + // All processed zip files will have a comment string + size += sizeof( ZIP_EndOfCentralDirRecord ) + MakeXZipCommentString( NULL ); + + return size; +} + +//----------------------------------------------------------------------------- +// Purpose: Print a directory of files in the zip +//----------------------------------------------------------------------------- +void CZipFile::PrintDirectory( void ) +{ + for ( int i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) ) + { + CZipEntry *e = &m_Files[ i ]; + + Msg( "%s\n", e->m_Name.String() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Iterate through directory +//----------------------------------------------------------------------------- +int CZipFile::GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize ) +{ + if ( id == -1 ) + { + id = m_Files.FirstInorder(); + } + else + { + id = m_Files.NextInorder( id ); + } + if ( id == m_Files.InvalidIndex() ) + { + // list is empty + return -1; + } + + CZipEntry *e = &m_Files[id]; + + Q_strncpy( pBuffer, e->m_Name.String(), bufferSize ); + fileSize = e->m_Length; + + return id; +} + +//----------------------------------------------------------------------------- +// Purpose: Store data out to disk +//----------------------------------------------------------------------------- +void CZipFile::SaveToDisk( FILE *fout ) +{ + CFileStream stream( fout ); + SaveDirectory( stream ); +} + +void CZipFile::SaveToDisk( HANDLE hOutFile ) +{ + CFileStream stream( hOutFile ); + SaveDirectory( stream ); +} + +//----------------------------------------------------------------------------- +// Purpose: Store data out to a CUtlBuffer +//----------------------------------------------------------------------------- +void CZipFile::SaveToBuffer( CUtlBuffer& buf ) +{ + CBufferStream stream( buf ); + SaveDirectory( stream ); +} + +//----------------------------------------------------------------------------- +// Purpose: Store data back out to a stream (could be CUtlBuffer or filestream) +//----------------------------------------------------------------------------- +void CZipFile::SaveDirectory( IWriteStream& stream ) +{ + void *pPaddingBuffer = NULL; + if ( m_AlignmentSize ) + { + // get a temp buffer for all padding work + pPaddingBuffer = malloc( m_AlignmentSize ); + memset( pPaddingBuffer, 0x00, m_AlignmentSize ); + } + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { +#ifdef WIN32 + FlushFileBuffers( m_hDiskCacheWriteFile ); +#else + fflush( (FILE *)m_hDiskCacheWriteFile ); +#endif + } + + bool bDataWritten = false; + int i; + for ( i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) ) + { + CZipEntry *e = &m_Files[i]; + Assert( e ); + + // Fix up the offset + e->m_ZipOffset = stream.Tell(); + + if ( e->m_Length > 0 && ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) ) + { + // get the data back from the write cache + e->m_pData = malloc( e->m_Length ); + if ( e->m_pData ) + { + CWin32File::FileSeek( m_hDiskCacheWriteFile, e->m_DiskCacheOffset, FILE_BEGIN ); + CWin32File::FileRead( m_hDiskCacheWriteFile, e->m_pData, e->m_Length ); + } + } + + if ( e->m_Length > 0 && e->m_pData != NULL ) + { + ZIP_LocalFileHeader hdr = { 0 }; + hdr.signature = PKID( 3, 4 ); + hdr.versionNeededToExtract = 10; // This is the version that the winzip that I have writes. + hdr.flags = 0; + hdr.compressionMethod = 0; // NO COMPRESSION! + hdr.lastModifiedTime = 0; + hdr.lastModifiedDate = 0; + + CRC32_Init( &e->m_ZipCRC ); + CRC32_ProcessBuffer( &e->m_ZipCRC, e->m_pData, e->m_Length ); + CRC32_Final( &e->m_ZipCRC ); + hdr.crc32 = e->m_ZipCRC; + + const char *pFilename = e->m_Name.String(); + hdr.compressedSize = e->m_Length; + hdr.uncompressedSize = e->m_Length; + hdr.fileNameLength = strlen( pFilename ); + hdr.extraFieldLength = CalculatePadding( hdr.fileNameLength, e->m_ZipOffset ); + int extraFieldLength = hdr.extraFieldLength; + + // Swap header in place + m_Swap.SwapFieldsToTargetEndian( &hdr ); + stream.Put( &hdr, sizeof( hdr ) ); + stream.Put( pFilename, strlen( pFilename ) ); + stream.Put( pPaddingBuffer, extraFieldLength ); + + // An update format specifically does not place any files + // except the first file which should be the preload section. + // All files in an update zip, exist compressed in the preload section. + if ( m_bCompatibleFormat || !m_bIsUpdateFormat || !bDataWritten ) + { + // write the data + stream.Put( e->m_pData, e->m_Length ); + bDataWritten = true; + } + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { + free( e->m_pData ); + + // temp hackery for the logic below to succeed + e->m_pData = (void*)0xFFFFFFFF; + } + } + } + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { + CWin32File::FileSeek( m_hDiskCacheWriteFile, 0, FILE_END ); + } + + unsigned int centralDirStart = stream.Tell(); + if ( m_AlignmentSize ) + { + // align the central directory starting position + unsigned int newDirStart = AlignValue( centralDirStart, m_AlignmentSize ); + int padLength = newDirStart - centralDirStart; + if ( padLength ) + { + stream.Put( pPaddingBuffer, padLength ); + centralDirStart = newDirStart; + } + } + + int realNumFiles = 0; + for ( i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) ) + { + CZipEntry *e = &m_Files[i]; + Assert( e ); + + if ( e->m_Length > 0 && e->m_pData != NULL ) + { + ZIP_FileHeader hdr = { 0 }; + hdr.signature = PKID( 1, 2 ); + hdr.versionMadeBy = 20; // This is the version that the winzip that I have writes. + hdr.versionNeededToExtract = 10; // This is the version that the winzip that I have writes. + hdr.flags = 0; + hdr.compressionMethod = 0; + hdr.lastModifiedTime = 0; + hdr.lastModifiedDate = 0; + hdr.crc32 = e->m_ZipCRC; + + hdr.compressedSize = e->m_Length; + hdr.uncompressedSize = e->m_Length; + hdr.fileNameLength = strlen( e->m_Name.String() ); + hdr.extraFieldLength = CalculatePadding( hdr.fileNameLength, e->m_ZipOffset ); + hdr.fileCommentLength = 0; + hdr.diskNumberStart = 0; + hdr.internalFileAttribs = 0; + hdr.externalFileAttribs = 0; // This is usually something, but zero is OK as if the input came from stdin + hdr.relativeOffsetOfLocalHeader = e->m_ZipOffset; + int extraFieldLength = hdr.extraFieldLength; + + // Swap the header in place + m_Swap.SwapFieldsToTargetEndian( &hdr ); + stream.Put( &hdr, sizeof( hdr ) ); + stream.Put( e->m_Name.String(), strlen( e->m_Name.String() ) ); + if ( m_bCompatibleFormat ) + { + stream.Put( pPaddingBuffer, extraFieldLength ); + } + + realNumFiles++; + + if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) + { + // clear out temp hackery + e->m_pData = NULL; + } + } + } + + unsigned int centralDirEnd = stream.Tell(); + if ( m_AlignmentSize ) + { + // align the central directory starting position + unsigned int newDirEnd = AlignValue( centralDirEnd, m_AlignmentSize ); + int padLength = newDirEnd - centralDirEnd; + if ( padLength ) + { + stream.Put( pPaddingBuffer, padLength ); + centralDirEnd = newDirEnd; + } + } + + ZIP_EndOfCentralDirRecord rec = { 0 }; + rec.signature = PKID( 5, 6 ); + rec.numberOfThisDisk = 0; + rec.numberOfTheDiskWithStartOfCentralDirectory = 0; + rec.nCentralDirectoryEntries_ThisDisk = realNumFiles; + rec.nCentralDirectoryEntries_Total = realNumFiles; + rec.centralDirectorySize = centralDirEnd - centralDirStart; + rec.startOfCentralDirOffset = centralDirStart; + + char commentString[128]; + int commentLength = MakeXZipCommentString( commentString ); + rec.commentLength = commentLength; + + // Swap the header in place + m_Swap.SwapFieldsToTargetEndian( &rec ); + stream.Put( &rec, sizeof( rec ) ); + stream.Put( commentString, commentLength ); + + if ( pPaddingBuffer ) + { + free( pPaddingBuffer ); + } +} + +class CZip : public IZip +{ +public: + CZip( const char *pDiskCacheWritePath, bool bSortByName ); + virtual ~CZip(); + + virtual void Reset(); + + // Add a single file to a zip - maintains the zip's previous alignment state + virtual void AddFileToZip( const char *relativename, const char *fullpath ); + + // Whether a file is contained in a zip - maintains alignment + virtual bool FileExistsInZip( const char *pRelativeName ); + + // Reads a file from the zip - maintains alignement + virtual bool ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ); + virtual bool ReadFileFromZip( HANDLE hZipFile, const char *relativename, bool bTextMode, CUtlBuffer &buf ); + + // Removes a single file from the zip - maintains alignment + virtual void RemoveFileFromZip( const char *relativename ); + + // Gets next filename in zip, for walking the directory - maintains alignment + virtual int GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize ); + + // Prints the zip's contents - maintains alignment + virtual void PrintDirectory( void ); + + // Estimate the size of the Zip (including header, padding, etc.) + virtual unsigned int EstimateSize( void ); + + // Add buffer to zip as a file with given name - uses current alignment size, default 0 (no alignment) + virtual void AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode ); + + // Writes out zip file to a buffer - uses current alignment size + // (set by file's previous alignment, or a call to ForceAlignment) + virtual void SaveToBuffer( CUtlBuffer& outbuf ); + + // Writes out zip file to a filestream - uses current alignment size + // (set by file's previous alignment, or a call to ForceAlignment) + virtual void SaveToDisk( FILE *fout ); + virtual void SaveToDisk( HANDLE hOutFile ); + + // Reads a zip file from a buffer into memory - sets current alignment size to + // the file's alignment size, unless overridden by a ForceAlignment call) + virtual void ParseFromBuffer( void *buffer, int bufferlength ); + virtual HANDLE ParseFromDisk( const char *pFilename ); + + // Forces a specific alignment size for all subsequent file operations, overriding files' previous alignment size. + // Return to using files' individual alignment sizes by passing FALSE. + virtual void ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize ); + + // Sets the endianess of the zip + virtual void SetBigEndian( bool bigEndian ); + virtual void ActivateByteSwapping( bool bActivate ); + + virtual unsigned int GetAlignment(); + +private: + CZipFile m_ZipFile; +}; + +static CUtlLinkedList< CZip* > g_ZipUtils; + +IZip *IZip::CreateZip( const char *pDiskCacheWritePath, bool bSortByName ) +{ + CZip *pZip = new CZip( pDiskCacheWritePath, bSortByName ); + g_ZipUtils.AddToTail( pZip ); + + return pZip; +} + +void IZip::ReleaseZip( IZip *pZip ) +{ + g_ZipUtils.FindAndRemove( (CZip *)pZip ); + + delete ((CZip *)pZip); +} + +CZip::CZip( const char *pDiskCacheWritePath, bool bSortByName ) : m_ZipFile( pDiskCacheWritePath, bSortByName ) +{ + m_ZipFile.Reset(); +} + +CZip::~CZip() +{ +} + +void CZip::SetBigEndian( bool bigEndian ) +{ + m_ZipFile.SetBigEndian( bigEndian ); +} + +void CZip::ActivateByteSwapping( bool bActivate ) +{ + m_ZipFile.ActivateByteSwapping( bActivate ); +} + +void CZip::AddFileToZip( const char *relativename, const char *fullpath ) +{ + m_ZipFile.AddFileToZip( relativename, fullpath ); +} + +bool CZip::FileExistsInZip( const char *pRelativeName ) +{ + return m_ZipFile.FileExistsInZip( pRelativeName ); +} + +bool CZip::ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) +{ + return m_ZipFile.ReadFileFromZip( pRelativeName, bTextMode, buf ); +} + +bool CZip::ReadFileFromZip( HANDLE hZipFile, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) +{ + return m_ZipFile.ReadFileFromZip( hZipFile, pRelativeName, bTextMode, buf ); +} + +void CZip::RemoveFileFromZip( const char *relativename ) +{ + m_ZipFile.RemoveFileFromZip( relativename ); +} + +int CZip::GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize ) +{ + return m_ZipFile.GetNextFilename( id, pBuffer, bufferSize, fileSize ); +} + +void CZip::PrintDirectory( void ) +{ + m_ZipFile.PrintDirectory(); +} + +void CZip::Reset() +{ + m_ZipFile.Reset(); +} + +unsigned int CZip::EstimateSize( void ) +{ + return m_ZipFile.CalculateSize(); +} + +// Add buffer to zip as a file with given name +void CZip::AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode ) +{ + m_ZipFile.AddBufferToZip( relativename, data, length, bTextMode ); +} + +void CZip::SaveToBuffer( CUtlBuffer& outbuf ) +{ + m_ZipFile.SaveToBuffer( outbuf ); +} + +void CZip::SaveToDisk( FILE *fout ) +{ + m_ZipFile.SaveToDisk( fout ); +} + +void CZip::SaveToDisk( HANDLE hOutFile ) +{ + m_ZipFile.SaveToDisk( hOutFile ); +} + +void CZip::ParseFromBuffer( void *buffer, int bufferlength ) +{ + m_ZipFile.Reset(); + m_ZipFile.ParseFromBuffer( buffer, bufferlength ); +} + +HANDLE CZip::ParseFromDisk( const char *pFilename ) +{ + m_ZipFile.Reset(); + return m_ZipFile.ParseFromDisk( pFilename ); +} + +void CZip::ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize ) +{ + m_ZipFile.ForceAlignment( aligned, bCompatibleFormat, alignmentSize ); +} + +unsigned int CZip::GetAlignment() +{ + return m_ZipFile.GetAlignment(); +} + diff --git a/public/zip_utils.h b/public/zip_utils.h new file mode 100644 index 0000000..6f9ec0f --- /dev/null +++ b/public/zip_utils.h @@ -0,0 +1,87 @@ +//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef ZIP_UTILS_H +#define ZIP_UTILS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlsymbol.h" + +class CUtlBuffer; +#include "tier0/dbg.h" + +abstract_class IZip +{ +public: + virtual void Reset() = 0; + + // Add a single file to a zip - maintains the zip's previous alignment state + virtual void AddFileToZip ( const char *relativename, const char *fullpath ) = 0; + + // Whether a file is contained in a zip - maintains alignment + virtual bool FileExistsInZip ( const char *pRelativeName ) = 0; + + // Reads a file from the zip - maintains alignement + virtual bool ReadFileFromZip ( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) = 0; +#ifdef _WIN32 + virtual bool ReadFileFromZip ( HANDLE hFile, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) = 0; +#endif + + // Removes a single file from the zip - maintains alignment + virtual void RemoveFileFromZip ( const char *relativename ) = 0; + + // Gets next filename in zip, for walking the directory - maintains alignment + virtual int GetNextFilename ( int id, char *pBuffer, int bufferSize, int &fileSize ) = 0; + + // Prints the zip's contents - maintains alignment + virtual void PrintDirectory ( void ) = 0; + + // Estimate the size of the Zip (including header, padding, etc.) + virtual unsigned int EstimateSize ( void ) = 0; + + // Add buffer to zip as a file with given name - uses current alignment size, default 0 (no alignment) + virtual void AddBufferToZip ( const char *relativename, void *data, int length, bool bTextMode ) = 0; + + // Writes out zip file to a buffer - uses current alignment size + // (set by file's previous alignment, or a call to ForceAlignment) + virtual void SaveToBuffer ( CUtlBuffer& outbuf ) = 0; + + // Writes out zip file to a filestream - uses current alignment size + // (set by file's previous alignment, or a call to ForceAlignment) + virtual void SaveToDisk ( FILE *fout ) = 0; +#ifdef _WIN32 + virtual void SaveToDisk ( HANDLE hFileOut ) = 0; +#endif + + // Reads a zip file from a buffer into memory - sets current alignment size to + // the file's alignment size, unless overridden by a ForceAlignment call) + virtual void ParseFromBuffer ( void *buffer, int bufferlength ) = 0; + + // Mounts a zip file from the disk + // Only ReadFileFromZip() is supported because the zip file could be >2GB +#ifdef _WIN32 + virtual HANDLE ParseFromDisk ( const char *pFilename ) = 0; +#endif + + // Forces a specific alignment size for all subsequent file operations, overriding files' previous alignment size. + // Return to using files' individual alignment sizes by passing FALSE. + virtual void ForceAlignment ( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize=0 ) = 0; + + virtual unsigned int GetAlignment() = 0; + + // Sets the endianess of the zip + virtual void SetBigEndian( bool bigEndian ) = 0; + virtual void ActivateByteSwapping( bool bActivate ) = 0; + + // Create/Release additional instances + // Disk Caching is necessary for large zips + static IZip *CreateZip( const char *pDiskCacheWritePath = NULL, bool bSortByName = false ); + static void ReleaseZip( IZip *zip ); +}; + +#endif // ZIP_UTILS_H diff --git a/scanner.cpp b/scanner.cpp new file mode 100644 index 0000000..c095362 --- /dev/null +++ b/scanner.cpp @@ -0,0 +1,431 @@ +//===========================================================================// +// +// Author: NULLderef +// Purpose: Portal 2: Multiplayer Mod server plugin memory scanner +// +//===========================================================================// + +#include "scanner.hpp" + +#ifdef _WIN32 +#include +#include +#else +#endif + +#include +#include +#include +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern void Log(int level, bool dev, const char* pMsgFormat, ...); + +namespace Memory { +#ifndef _WIN32 + inline void __cpuidex(int cpuid[4], int function, int subleaf) { + asm volatile("cpuid" + : "=a" (cpuid[0]), + "=b" (cpuid[1]), + "=c" (cpuid[2]), + "=d" (cpuid[3]) + : "0" (function), "2" (subleaf) + ); + } +#endif // _WIN32 + + enum MaskState : uint8_t { + MASK_EMPTY = 0x00, + MASK_FULL = 0xFF, + }; + + class ScanData { + public: + ScanData(std::string patternString) : locatorFirst(0), locatorLast(0) { + std::istringstream patternStream(patternString); + std::string patternByte; + + while (patternStream >> patternByte) { + if (patternByte == "?" || patternByte == "??") { + this->pattern.push_back(0x00); + this->mask.push_back(MASK_EMPTY); + } + else { + this->pattern.push_back(static_cast(std::stoul(patternByte, nullptr, 16))); + this->mask.push_back(MASK_FULL); + } + } + + for (size_t first = 0; first < this->mask.size() - 1; first++) { + if (this->mask[first] == MASK_FULL) { + this->locatorFirst = first; + break; + } + } + + for (size_t last = this->mask.size() - 1; last > 0; last--) { + if (this->mask[last] == MASK_FULL) { + this->locatorLast = last; + break; + } + } + + if (this->mask[this->locatorFirst] == MASK_EMPTY || this->mask[this->locatorLast] == MASK_EMPTY) { + throw std::runtime_error("Unable to find locating bytes (mask may be too loose)"); + } + } + + std::vector pattern; + std::vector mask; + size_t locatorFirst; + size_t locatorLast; + }; + + // implementation: AVX + + class AVXScanner : public ScannerImplementation { + public: + uintptr_t Scan(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + const __m256i locatorFirstMask = _mm256_set1_epi8(static_cast(scanData.pattern[scanData.locatorFirst])); + const __m256i locatorLastMask = _mm256_set1_epi8(static_cast(scanData.pattern[scanData.locatorLast])); + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size() - sizeof(__m256i); blockOffset += sizeof(__m256i)) { + const __m256i scanFirstBlock = _mm256_loadu_si256(reinterpret_cast<__m256i*>(®ion[blockOffset + scanData.locatorFirst])); + const __m256i scanLastBlock = _mm256_loadu_si256(reinterpret_cast<__m256i*>(®ion[blockOffset + scanData.locatorLast])); + + const uint32_t comparedMask = _mm256_movemask_epi8(_mm256_and_si256( + _mm256_cmpeq_epi8(scanFirstBlock, locatorFirstMask), + _mm256_cmpeq_epi8(scanLastBlock, locatorLastMask) + )); + + if (comparedMask != 0) { + // WANING: this for loop needs the count of *bits* and not *bytes* + for (size_t bitPosition = 0; bitPosition < sizeof(comparedMask) * 8; bitPosition++) { + if ((comparedMask & (1 << bitPosition)) != 0) { + if (this->InnerCompare(std::span(®ion[blockOffset + bitPosition], scanData.pattern.size()), scanData)) { + return reinterpret_cast(®ion[blockOffset + bitPosition + offset]); + } + } + } + } + + } + + throw std::runtime_error("Unable to find signature"); + } + + std::vector ScanMultiple(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + std::vector matches; + + const __m256i locatorFirstMask = _mm256_set1_epi8(static_cast(scanData.pattern[scanData.locatorFirst])); + const __m256i locatorLastMask = _mm256_set1_epi8(static_cast(scanData.pattern[scanData.locatorLast])); + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size() - sizeof(__m256i); blockOffset += sizeof(__m256i)) { + const __m256i scanFirstBlock = _mm256_loadu_si256(reinterpret_cast<__m256i*>(®ion[blockOffset + scanData.locatorFirst])); + const __m256i scanLastBlock = _mm256_loadu_si256(reinterpret_cast<__m256i*>(®ion[blockOffset + scanData.locatorLast])); + + const uint32_t comparedMask = _mm256_movemask_epi8(_mm256_and_si256( + _mm256_cmpeq_epi8(scanFirstBlock, locatorFirstMask), + _mm256_cmpeq_epi8(scanLastBlock, locatorLastMask) + )); + + if (comparedMask != 0) { + // WANING: this for loop needs the count of *bits* and not *bytes* + for (size_t bitPosition = 0; bitPosition < sizeof(comparedMask) * 8; bitPosition++) { + if ((comparedMask & (1 << bitPosition)) != 0) { + if (this->InnerCompare(std::span(®ion[blockOffset + bitPosition], scanData.pattern.size()), scanData)) { + matches.push_back(reinterpret_cast(®ion[blockOffset + bitPosition + offset])); + } + } + } + } + } + + return matches; + } + + private: + inline bool InnerCompare(std::span region, ScanData& scanData) { + size_t lastAligned = (region.size() & ~(sizeof(__m256i) - 1)); + for (size_t offset = 0; offset < lastAligned; offset += sizeof(__m256i)) { + if ( + ~_mm256_movemask_epi8( + _mm256_cmpeq_epi8( + _mm256_loadu_si256(reinterpret_cast<__m256i*>(®ion[offset])), + _mm256_loadu_si256(reinterpret_cast<__m256i*>(&scanData.pattern[offset])) + ) + ) + & + _mm256_movemask_epi8( + _mm256_loadu_si256(reinterpret_cast<__m256i*>(&scanData.mask[offset])) + ) + ) { + return false; + } + } + + for (size_t offset = lastAligned; offset < region.size(); offset++) { + if (region[offset] != scanData.pattern[offset] && scanData.mask[offset] == MASK_FULL) { + return false; + } + } + + return true; + } + }; + + // implementation: SSE + + class SSEScanner : public ScannerImplementation { + public: + uintptr_t Scan(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + const __m128i locatorFirstMask = _mm_set1_epi8(static_cast(scanData.pattern[scanData.locatorFirst])); + const __m128i locatorLastMask = _mm_set1_epi8(static_cast(scanData.pattern[scanData.locatorLast])); + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size() - sizeof(__m128i); blockOffset += sizeof(__m128i)) { + const __m128i scanFirstBlock = _mm_loadu_si128(reinterpret_cast<__m128i*>(®ion[blockOffset + scanData.locatorFirst])); + const __m128i scanLastBlock = _mm_loadu_si128(reinterpret_cast<__m128i*>(®ion[blockOffset + scanData.locatorLast])); + + const uint16_t comparedMask = _mm_movemask_epi8(_mm_and_si128( + _mm_cmpeq_epi8(scanFirstBlock, locatorFirstMask), + _mm_cmpeq_epi8(scanLastBlock, locatorLastMask) + )); + + if (comparedMask != 0) { + // WANING: this for loop needs the count of *bits* and not *bytes* + for (size_t bitPosition = 0; bitPosition < sizeof(comparedMask) * 8; bitPosition++) { + if ((comparedMask & (1 << bitPosition)) != 0) { + if (this->InnerCompare(std::span(®ion[blockOffset + bitPosition], scanData.pattern.size()), scanData)) { + return reinterpret_cast(®ion[blockOffset + bitPosition + offset]); + } + } + } + } + } + + throw std::runtime_error("Unable to find signature"); + } + + std::vector ScanMultiple(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + std::vector matches; + + const __m128i locatorFirstMask = _mm_set1_epi8(static_cast(scanData.pattern[scanData.locatorFirst])); + const __m128i locatorLastMask = _mm_set1_epi8(static_cast(scanData.pattern[scanData.locatorLast])); + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size() - sizeof(__m128i); blockOffset += sizeof(__m128i)) { + const __m128i scanFirstBlock = _mm_loadu_si128(reinterpret_cast<__m128i*>(®ion[blockOffset + scanData.locatorFirst])); + const __m128i scanLastBlock = _mm_loadu_si128(reinterpret_cast<__m128i*>(®ion[blockOffset + scanData.locatorLast])); + + const uint16_t comparedMask = _mm_movemask_epi8(_mm_and_si128( + _mm_cmpeq_epi8(scanFirstBlock, locatorFirstMask), + _mm_cmpeq_epi8(scanLastBlock, locatorLastMask) + )); + + if (comparedMask != 0) { + // WANING: this for loop needs the count of *bits* and not *bytes* + for (size_t bitPosition = 0; bitPosition < sizeof(comparedMask) * 8; bitPosition++) { + if ((comparedMask & (1 << bitPosition)) != 0) { + if (this->InnerCompare(std::span(®ion[blockOffset + bitPosition], scanData.pattern.size()), scanData)) { + matches.push_back(reinterpret_cast(®ion[blockOffset + bitPosition + offset])); + } + } + } + } + } + + return matches; + } + + private: + inline bool InnerCompare(std::span region, ScanData& scanData) { + size_t lastAligned = (region.size() & ~(sizeof(__m128i) - 1)); + for (size_t offset = 0; offset < lastAligned; offset += sizeof(__m128i)) { + if ( + ~_mm_movemask_epi8( + _mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast<__m128i*>(®ion[offset])), + _mm_loadu_si128(reinterpret_cast<__m128i*>(&scanData.pattern[offset])) + ) + ) + & + _mm_movemask_epi8( + _mm_loadu_si128(reinterpret_cast<__m128i*>(&scanData.mask[offset])) + ) + ) { + return false; + } + } + + for (size_t offset = lastAligned; offset < region.size(); offset++) { + if (region[offset] != scanData.pattern[offset] && scanData.mask[offset] == MASK_FULL) { + return false; + } + } + + return true; + } + }; + + // implementation: generic + + class GenericScanner : public ScannerImplementation { + public: + uintptr_t Scan(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size(); blockOffset++) { + if ( + region[blockOffset + scanData.locatorFirst] == scanData.pattern[scanData.locatorFirst] && + region[blockOffset + scanData.locatorLast] == scanData.pattern[scanData.locatorLast] + ) { + if (this->InnerCompare(std::span(®ion[blockOffset], scanData.pattern.size()), scanData)) { + return reinterpret_cast(®ion[blockOffset + offset]); + } + } + } + + throw std::runtime_error("Unable to find signature"); + } + + std::vector ScanMultiple(std::span region, std::string patternString, int offset) { + ScanData scanData(patternString); + + std::vector matches; + + for (size_t blockOffset = 0; blockOffset < region.size() - scanData.pattern.size(); blockOffset++) { + if ( + region[blockOffset + scanData.locatorFirst] == scanData.pattern[scanData.locatorFirst] && + region[blockOffset + scanData.locatorLast] == scanData.pattern[scanData.locatorLast] + ) { + if (this->InnerCompare(std::span(®ion[blockOffset], scanData.pattern.size()), scanData)) { + matches.push_back(reinterpret_cast(®ion[blockOffset + offset])); + } + } + } + + throw std::runtime_error("Unable to find signature"); + } + + private: + inline bool InnerCompare(std::span region, ScanData& scanData) { + for (size_t offset = 0; offset < region.size(); offset++) { + if (region[offset] != scanData.pattern[offset] && scanData.mask[offset] == MASK_FULL) { + return false; + } + } + + return true; + } + }; + + // common + + std::unique_ptr& Scanner::Implementation() { + static std::unique_ptr implementation; + + if (implementation == nullptr) { + int cpuid[4]; + + __cpuidex(cpuid, 0, 0); + + int ncpuids = cpuid[0]; + if(ncpuids >= 7) { + __cpuidex(cpuid, 7, 0); + if (cpuid[1] & (1 << 5)) { + implementation = std::make_unique(); + } else { + implementation = std::make_unique(); + } + } else { + __cpuidex(cpuid, 1, 0); + if(cpuid[3] & (1 << 26)) { + implementation = std::make_unique(); + } else { + implementation = std::make_unique(); + } + } + } + + return implementation; + } + + std::unordered_map> Modules::loadedModules; + + void Modules::PopulateModules() { +#ifdef _WIN32 + HMODULE modules[1024]; + auto processHandle = GetCurrentProcess(); + DWORD modulesNeeded; + if (EnumProcessModules(processHandle, modules, sizeof(modules), &modulesNeeded)) { + for (DWORD i = 0; i < (modulesNeeded / sizeof(HMODULE)); i++) { + char pathBuffer[MAX_PATH]; + if (!GetModuleFileNameA(modules[i], pathBuffer, sizeof(pathBuffer))) continue; + MODULEINFO moduleInfo = {}; + if (!GetModuleInformation(processHandle, modules[i], &moduleInfo, sizeof(MODULEINFO))) continue; + std::filesystem::path modulePath(pathBuffer); + loadedModules.insert( + std::make_pair( + modulePath.stem().string(), + std::span( + reinterpret_cast(moduleInfo.lpBaseOfDll), + static_cast(moduleInfo.SizeOfImage) + ) + ) + ); + } + } +#else +#endif + } + + std::span Modules::Get(std::string name) { + if (loadedModules.empty()) { + PopulateModules(); + } + + if (loadedModules.contains(name)) { + return loadedModules[name]; + } + else { + throw std::runtime_error("Failed to Get a required module"); + } + } + + void ReplacePattern(std::string target_module, std::string patternBytes, std::string replace_with) + { + void* addr = Memory::Scanner::Scan(Memory::Modules::Get(target_module), patternBytes); + if (!addr) + { + Log(1, false, "Failed to replace pattern! Turn on wss9000_developer for more info..."); + Log(1, true, "Target Module: %s", target_module.c_str()); + Log(1, true, "Pattern Bytes To Find: %s", patternBytes.c_str()); + Log(1, true, "Bytes To Replace Pattern Bytes With: %s", replace_with.c_str()); + return; + } + + std::vector replace; + + std::istringstream patternStream(replace_with); + std::string patternByte; + while (patternStream >> patternByte) + { + replace.push_back((uint8_t)std::stoul(patternByte, nullptr, 16)); + } + + DWORD oldprotect = 0; + DWORD newprotect = PAGE_EXECUTE_READWRITE; + VirtualProtect(addr, replace.size(), newprotect, &oldprotect); + memcpy_s(addr, replace.size(), replace.data(), replace.size()); + VirtualProtect(addr, replace.size(), oldprotect, &newprotect); + } +}; diff --git a/scanner.hpp b/scanner.hpp new file mode 100644 index 0000000..c88884d --- /dev/null +++ b/scanner.hpp @@ -0,0 +1,60 @@ +//===========================================================================// +// +// Author: NULLderef +// Purpose: Portal 2: Multiplayer Mod server plugin memory scanner +// +//===========================================================================// + +#ifndef SCANNER_HPP +#define SCANNER_HPP + +#define SERVERDLL Memory::Modules::Get("server") +#define ENGINEDLL Memory::Modules::Get("engine") +#define CLIENTDLL Memory::Modules::Get("client") + +#include +#include +#include +#include +#include +#include + +namespace Memory { + class ScannerImplementation { + public: + virtual uintptr_t Scan(std::span region, std::string pattern, int offset) = 0; + virtual std::vector ScanMultiple(std::span region, std::string pattern, int offset) = 0; + }; + + class Scanner { + public: + template static T Scan(std::span region, std::string pattern, int offset = 0) { + return reinterpret_cast(Scanner::Implementation().get()->Scan(region, pattern, offset)); + } + + static std::vector ScanMultiple(std::span region, std::string pattern, int offset = 0) { + return Scanner::Implementation().get()->ScanMultiple(region, pattern, offset); + } + + private: + static std::unique_ptr& Implementation(); + }; + class Modules { + public: + static std::span Get(std::string name); + + private: + static void PopulateModules(); + static std::unordered_map> loadedModules; + }; + + void ReplacePattern(std::string target_module, std::string patternBytes, std::string replace_with); + + + template T Rel32(void* relPtr) { + auto rel = reinterpret_cast(relPtr); + return reinterpret_cast(rel + *reinterpret_cast(rel) + sizeof(int32_t)); + } +}; + +#endif // SCANNER_HPP diff --git a/workshopstopper9000.sln b/workshopstopper9000.sln new file mode 100644 index 0000000..6193c3f --- /dev/null +++ b/workshopstopper9000.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "workshopstopper9000", "workshopstopper9000.vcxproj", "{531796FA-476D-47F1-84FA-CD3613D68536}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {531796FA-476D-47F1-84FA-CD3613D68536}.Release|Win32.ActiveCfg = Release|Win32 + {531796FA-476D-47F1-84FA-CD3613D68536}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1419AF7F-D3CD-4504-8192-DB23461E2072} + EndGlobalSection +EndGlobal diff --git a/workshopstopper9000.vcxproj b/workshopstopper9000.vcxproj new file mode 100644 index 0000000..e0fc296 --- /dev/null +++ b/workshopstopper9000.vcxproj @@ -0,0 +1,96 @@ + + + + + Release + Win32 + + + + {531796FA-476D-47F1-84FA-CD3613D68536} + workshopstopper9000 + 10.0 + WorkshopStopper9000 + + + + DynamicLibrary + true + MultiByte + v143 + + + + + + + + + + .\Release\.\ + + + .\Release\.\ + + + workshopstopper9000 + + + .dll + + + *.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.metagen;*.bi + + + + Level4 + MinSpace + ;public;public\tier0;public\tier1 + true + VPC;RAD_TELEMETRY_DISABLED;_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_ALLOW_RUNTIME_LIBRARY_MISMATCH;_ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH;_ALLOW_MSC_VER_MISMATCH;%(PreprocessorDefinitions);COMPILER_MSVC32;COMPILER_MSVC;_DLL_EXT=.dll;DLLNAME=workshopstopper9000;BINK_VIDEO;AVI_VIDEO;WMV_VIDEO;DEV_BUILD;FRAME_POINTER_OMISSION_DISABLED;_MBCS;_EXTERNAL_DLL_EXT=.dll;VPCGAMECAPS=HL2MP;SOURCE1=1;VPCGAME=hl2mp + false + false + Default + MultiThreadedDLL + true + NotSet + Fast + true + + + $(IntDir)/ + $(IntDir)/ + $(IntDir)/ + CompileAsCpp + ;4316 + true + Prompt + false + ProgramDatabase + false + stdcpplatest + + + DebugFastLink + lib\public;minhook\lib;%(AdditionalLibraryDirectories) + legacy_stdio_definitions.lib;vstdlib.lib;tier0.lib;tier1.lib;tier2.lib;mathlib.lib;interfaces.lib;libMinHook-x86-v141-mtd.lib + libc;libcd;libcmt;libcpmt;libcpmt1 + + + IF EXIST ./copy.bat copy.bat + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workshopstopper9000.vcxproj.user b/workshopstopper9000.vcxproj.user new file mode 100644 index 0000000..d12a36c --- /dev/null +++ b/workshopstopper9000.vcxproj.user @@ -0,0 +1,9 @@ + + + + + copy $(TargetPath) "$(ProjectDir)..\Portal-2-Multiplayer-Mod\src\ModFiles\Portal 2\install_dlc\addons" + + + + \ No newline at end of file diff --git a/workshopstopper9000.vdf b/workshopstopper9000.vdf new file mode 100644 index 0000000..3d5fbdc --- /dev/null +++ b/workshopstopper9000.vdf @@ -0,0 +1,4 @@ +"plugin" +{ + "file" "addons/workshopstopper9000" +} \ No newline at end of file